C# 如何使用DataTemplate+触发器在视图之间切换

C# 如何使用DataTemplate+触发器在视图之间切换,c#,wpf,silverlight,mvvm,datatemplate,C#,Wpf,Silverlight,Mvvm,Datatemplate,我有一个要求,用户可以切换到以树或datagrid中的文本或流程图的形式查看分层数据 用户可以通过点击切换按钮来实现这一点,切换按钮上写着:切换模式。我想这样做,它可以在视图中处理,因为在所有三种情况下,ViewModel都是相同的 如何基于触发器将视图应用于ViewModel。如果要显示的视图的状态保存在某个枚举属性中,则可以使用,例如: <ContentControl> <ContentControl.Style> <Style Targ

我有一个要求,用户可以切换到以树或datagrid中的文本或流程图的形式查看分层数据

用户可以通过点击切换按钮来实现这一点,切换按钮上写着:切换模式。我想这样做,它可以在视图中处理,因为在所有三种情况下,ViewModel都是相同的


如何基于触发器将视图应用于ViewModel。

如果要显示的视图的状态保存在某个枚举属性中,则可以使用,例如:

<ContentControl>
    <ContentControl.Style>
        <Style TargetType="{x:Type ContentControl}">
            <Style.Triggers>
                <DataTrigger Binding="{Binding ViewMode}" Value="TreeMode">
                    <Setter Property="Content">
                        <Setter.Value>
                            <uc:TreeModeView />
                        </Setter.Value>
                    </Setter>
                </DataTrigger>
                <DataTrigger Binding="{Binding ViewMode}" Value="GridMode">
                    <Setter Property="Content">
                        <Setter.Value>
                            <uc:GridModeView />
                        </Setter.Value>
                    </Setter>
                </DataTrigger>
            </Style.Triggers>
        </Style>
    </ContentControl.Style>
</ContentControl>
由于该样式仅在一个位置使用,请直接将其设置为ContentControl.style。这将起作用,如果要在多个位置使用它,则应改为设置ContentTemplate,因为否则,只有一个视图实例由所有控件共享,其样式是WPF不允许的,当然,需要将内容设置为要应用的模板


当然,您也可以使用直接绑定到IsChecked的ToggleButton。然后,相关值将为True、False和{x:Null}。

如果要显示的视图的状态保存在某个枚举属性中,则可以使用,例如:

<ContentControl>
    <ContentControl.Style>
        <Style TargetType="{x:Type ContentControl}">
            <Style.Triggers>
                <DataTrigger Binding="{Binding ViewMode}" Value="TreeMode">
                    <Setter Property="Content">
                        <Setter.Value>
                            <uc:TreeModeView />
                        </Setter.Value>
                    </Setter>
                </DataTrigger>
                <DataTrigger Binding="{Binding ViewMode}" Value="GridMode">
                    <Setter Property="Content">
                        <Setter.Value>
                            <uc:GridModeView />
                        </Setter.Value>
                    </Setter>
                </DataTrigger>
            </Style.Triggers>
        </Style>
    </ContentControl.Style>
</ContentControl>
由于该样式仅在一个位置使用,请直接将其设置为ContentControl.style。这将起作用,如果要在多个位置使用它,则应改为设置ContentTemplate,因为否则,只有一个视图实例由所有控件共享,其样式是WPF不允许的,当然,需要将内容设置为要应用的模板


当然,您也可以使用直接绑定到IsChecked的ToggleButton。然后,相关的值将是True、False和{x:Null}。

H.B.的答案是好的,但在某些情况下,它不是很好

如果构建视图及其基础视图模型的成本很高,那么在用户每次更改视图时切换Content属性将支付此费用

在某些情况下,在同一容器(例如网格)中创建两个视图并切换其可见性是有意义的。如果在视图模型中使用惰性评估,则在视图可见之前不会执行代价高昂的操作,更重要的是,只有在视图第一次可见时才会执行这些操作。显示两个视图后,用户可以在视图之间来回切换,而无需重建基础视图模型

编辑:

我被纠正了,有点:H.B.的答案没有看上去那么好

不能使用样式将ContentControl的Content属性设置为UIElement。请参阅以获取完整的详细信息,但其长短不一之处在于,如果使用H.B.的方法,将出现运行时错误

您可以改为设置ContentTemplate属性,例如:

<Style TargetType="{x:Type ContentControl}">
    <Style.Triggers>
        <DataTrigger Binding="{Binding ViewMode}"
                        Value="TreeMode">
            <Setter Property="ContentTemplate">
                <Setter.Value>
                    <DataTemplate>
                        <uc:TreeModeView/>
                    </DataTemplate>
                </Setter.Value>
            </Setter>
        </DataTrigger>
        <DataTrigger Binding="{Binding ViewMode}"
                        Value="GridMode">
            <Setter Property="ContentTemplate">
                <Setter.Value>
                    <DataTemplate>
                        <uc:GridModeView/>
                    </DataTemplate>
                </Setter.Value>
            </Setter>
        </DataTrigger>
    </Style.Triggers>
</Style>

H.B.的答案是好的,但也有不太好的情况

如果构建视图及其基础视图模型的成本很高,那么在用户每次更改视图时切换Content属性将支付此费用

在某些情况下,在同一容器(例如网格)中创建两个视图并切换其可见性是有意义的。如果在视图模型中使用惰性评估,则在视图可见之前不会执行代价高昂的操作,更重要的是,只有在视图第一次可见时才会执行这些操作。显示两个视图后,用户可以在视图之间来回切换,而无需重建基础视图模型

编辑:

我被纠正了,有点:H.B.的答案没有看上去那么好

不能使用样式将ContentControl的Content属性设置为UIElement。请参阅以获取完整的详细信息,但其长短不一之处在于,如果使用H.B.的方法,将出现运行时错误

您可以改为设置ContentTemplate属性,例如:

<Style TargetType="{x:Type ContentControl}">
    <Style.Triggers>
        <DataTrigger Binding="{Binding ViewMode}"
                        Value="TreeMode">
            <Setter Property="ContentTemplate">
                <Setter.Value>
                    <DataTemplate>
                        <uc:TreeModeView/>
                    </DataTemplate>
                </Setter.Value>
            </Setter>
        </DataTrigger>
        <DataTrigger Binding="{Binding ViewMode}"
                        Value="GridMode">
            <Setter Property="ContentTemplate">
                <Setter.Value>
                    <DataTemplate>
                        <uc:GridModeView/>
                    </DataTemplate>
                </Setter.Value>
            </Setter>
        </DataTrigger>
    </Style.Triggers>
</Style>

事实并非如此,与datatemplates setter只创建这些对象的一个实例不同,您可以通过使用文本框作为内容并更改其文本来测试这一点,然后在视图中循环,它仍然存在。如果不是这样的话,就不会有这样的问题。这很有趣。当我试图构建一个演示来确定它在本例中是否正确时,我发现您所描述的结果是,您的答案中的方法实际上根本不起作用。请参阅我的编辑。您可能误解了这里的问题,关键语句是:这会引发异常,因为Setter.value的值是共享的,并且从UIElement继承的对象不能有多个父对象。因此,如果此样式从未用于多个ContentControl,则可以通过直接设置它而不将其作为资源提供来确保这一点,这一点都没有问题。我以前也在一个应用程序中使用过这种方法,而且效果很好
您甚至可以对多个样式目标执行此操作,您只需将样式定义为资源,并将其设置为false,然后重试。比如,这有多酷?嗯,我测试了你的代码——我把它复制到一个项目中,创建了TreeViewMode和GridModeView控件,正如我链接到的博客文章所指出的,它崩溃了。我做错了什么吗?事实并非如此,与datatemplates setter只创建这些对象的一个实例不同,您可以通过使用文本框作为内容并更改其文本来测试这一点,然后在视图中循环,它仍然存在。如果不是这样的话,就不会有这样的问题。这很有趣。当我试图构建一个演示来确定它在本例中是否正确时,我发现您所描述的结果是,您的答案中的方法实际上根本不起作用。请参阅我的编辑。您可能误解了这里的问题,关键语句是:这会引发异常,因为Setter.value的值是共享的,并且从UIElement继承的对象不能有多个父对象。因此,如果此样式从未用于多个ContentControl,则可以通过直接设置它而不将其作为资源提供来确保这一点,这一点都没有问题。我以前也在一个应用程序中使用过这种方法,效果很好。实际上,你甚至可以对多个样式目标使用这种方法,你只需要将样式定义为一个资源,并将其设置为false,然后再试一次。比如,这有多酷?嗯,我测试了你的代码——我把它复制到一个项目中,创建了TreeViewMode和GridModeView控件,正如我链接到的博客文章所指出的,它崩溃了。我做错了什么吗?我喜欢这个答案,但它确实显示了WPF有时有多冗长。我喜欢这个答案,但它确实显示了WPF有时有多冗长。