You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

WinUI 3中DataTemplate适配VisualStateGroup的集合绑定报错问题

问题

在WinUI 3(1.1.5)中尝试将VisualStateGroup对象集合传递给ContentControl或ListView等控件时,持续收到“值不在预期范围内”的错误。即使定义了对应DataTemplate也无法解决,但将VisualStateGroup和VisualState的属性复制到自定义密封类Thing1、Thing2后,集合就能正常绑定显示。

错误复现代码

C#代码

public sealed partial class MainPage : Page
{
    public MainViewModel ViewModel { get; }
    public ShellPage ShellPage { get; }

    public MainPage()
    {
        ViewModel = App.GetService<MainViewModel>();
        InitializeComponent();

        ShellPage = App.GetService<ShellPage>();
        var rootGrid = (ShellPage.IsLoaded) ? VisualTreeHelper.GetChild(ShellPage.NavigationViewControl, 0) as Grid : null;
        if (rootGrid != null)
        {
            var listOfVisualStateGroups = VisualStateManager.GetVisualStateGroups(rootGrid);
            if (listOfVisualStateGroups?.Count > 0) cControl.Content = listOfVisualStateGroups[0];
        }
    }
}

XAML代码

<Page
    x:Class="TestCollection.Views.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d">

    <Grid x:Name="ContentArea">
        <ContentControl x:Name="cControl">
            <ContentControl.ContentTemplate>
                <DataTemplate x:DataType="VisualStateGroup">
                    <TextBlock Text="{x:Bind Name}" />
                </DataTemplate>
            </ContentControl.ContentTemplate>
        </ContentControl>
    </Grid>
</Page>

可行的封装方案代码

C#代码

public sealed partial class MainPage : Page
{
    public MainViewModel ViewModel { get; }
    public ShellPage ShellPage { get; }

    public List<Thing1> Things { get; set; } = new();
    public List<Thing2> OtherThings { get; set; } = new();

    public MainPage()
    {
        ViewModel = App.GetService<MainViewModel>();
        InitializeComponent();

        ShellPage = App.GetService<ShellPage>();
        var rootGrid = (ShellPage.IsLoaded) ? VisualTreeHelper.GetChild(ShellPage.NavigationViewControl, 0) as Grid : null;
        if (rootGrid != null)
        {
            var listOfVisualStateGroups = VisualStateManager.GetVisualStateGroups(rootGrid);
            foreach (var group in listOfVisualStateGroups) Things.Add(new(group));
            if (listOfVisualStateGroups?.Count > 0)
                foreach (var state in listOfVisualStateGroups[0].States) OtherThings.Add(new(state));
        }
    }
}

// 复制VisualStateGroup属性的自定义类
public partial class Thing1
{
    public VisualState CurrentState { get; }
    public CoreDispatcher Dispatcher { get; }
    public DispatcherQueue DispatcherQueue { get; }
    public string Name { get; }
    public IList<VisualState> States { get; }
    public IList<VisualTransition> Transitions { get; }

    public Thing1(VisualStateGroup group)
    {
        CurrentState = group.CurrentState;
        Dispatcher = group.Dispatcher;
        DispatcherQueue = group.DispatcherQueue;
        Name = group.Name;
        States = group.States;
        Transitions = group.Transitions;
    }
}

// 复制VisualState属性的自定义类
public partial class Thing2
{
    public CoreDispatcher Dispatcher { get; }
    public DispatcherQueue DispatcherQueue { get; }
    public string Name { get; }
    public SetterBaseCollection Setters { get; }
    public IList<StateTriggerBase> StateTriggers { get; }
    public Storyboard Storyboard { get; set; }

    public Thing2(VisualState state)
    {
        Dispatcher = state.Dispatcher;
        DispatcherQueue = state.DispatcherQueue;
        Name = state.Name;
        Setters = state.Setters;
        StateTriggers = state.StateTriggers;
        Storyboard = state.Storyboard;
    }
}

XAML代码

<Page
    x:Class="TestCollection.Views.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:views="using:TestCollection.Views"
    mc:Ignorable="d">

    <Grid x:Name="ContentArea">
        <StackPanel Orientation="Horizontal">
            <ListView Margin="0,0,20,20" BorderThickness="1" BorderBrush="Black" Header="Things (VisualStateGroups)"
                      ItemsSource="{x:Bind Things}">
                <ListView.ItemTemplate>
                    <DataTemplate x:DataType="views:Thing1">
                        <TextBlock Text="{x:Bind Name}" />
                    </DataTemplate>
                </ListView.ItemTemplate>
            </ListView>
            <ListView Margin="0,0,20,20" BorderThickness="1" BorderBrush="Black" Header="OtherThings (VisualStates)"
                      ItemsSource="{x:Bind OtherThings}">
                <ListView.ItemTemplate>
                    <DataTemplate x:DataType="views:Thing2">
                        <TextBlock Text="{x:Bind Name}" />
                    </DataTemplate>
                </ListView.ItemTemplate>
            </ListView>
        </StackPanel>
    </Grid>
</Page>

原因分析

  • VisualStateGroup的设计定位:它并非为数据绑定场景设计,而是作为Visual State管理的核心对象,内部依赖于与可视化树的强关联,包含大量与控件状态控制相关的内部逻辑和引用,这些内容不适合直接作为绑定源暴露给UI控件。
  • XAML绑定的类型限制:WinUI 3的XAML绑定系统对作为绑定源的类型有隐性要求——需要类型具备符合绑定规则的属性访问权限、无内部状态冲突,且未被标记为仅用于可视化系统内部的类型。VisualStateGroup属于UI框架内部使用的类型,其属性可能包含无法被绑定系统正确处理的内部引用(如Dispatcher、CurrentState等与控件生命周期强绑定的成员),导致绑定过程中出现参数越界错误。
  • 自定义封装类的适配性:Thing1、Thing2仅复制了需要展示的属性,剥离了VisualStateGroup/VisualState与原可视化树的关联,仅保留了纯数据属性(如Name),这些属性完全符合绑定系统的要求,因此可以正常被ListView或ContentControl处理。

内容的提问来源于stack exchange,提问作者aturnbul

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.17 23:45:42