【问题标题】:How to switch between views using DataTemplate + Triggers如何使用 DataTemplate + Triggers 在视图之间切换
【发布时间】:2011-09-26 15:33:51
【问题描述】:

我有一个要求,用户可以在其中切换以将分层数据作为树或作为数据网格中的文本或作为流程图来查看。

用户可以通过单击一个切换按钮来执行此操作,该按钮显示:切换模式。我想以这样一种方式来完成这一切,它只能在 View 中处理,因为 ViewModel 在所有三种情况下都是相同的。

如何基于 Trigger 将 View 应用到我的 ViewModel。

【问题讨论】:

    标签: c# wpf silverlight mvvm datatemplate


    【解决方案1】:

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

    <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 不允许的(当然Content 需要设置为 something 才能应用模板)

    当然,您也可以使用ElementName 直接绑定到ToggleButtonIsChecked。相关值将是 TrueFalse{x:Null}

    【讨论】:

    • 我喜欢这个答案,但它确实显示了 WPF 有时会变得多么冗长。
    【解决方案2】:

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

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

    在某些情况下,在同一个容器中创建两个视图(例如Grid)并切换它们的Visibility 是有意义的。如果您在视图模型中使用惰性求值,则在视图变得可见之前不会执行昂贵的操作,而且 - 重要的是 - 它们只会在视图变得可见时执行第一次。显示两个视图后,用户可以在视图之间来回切换,而无需重建底层视图模型。

    编辑:

    我的立场是正确的,有点:H.B. 的答案并不像看起来那么好。

    您不能使用样式将ContentControlContent 属性设置为UIElement。详情请参阅this blog post,但总而言之,如果您使用 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>
    

    【讨论】:

    • 实际上并非如此,与数据模板不同,Setters 只创建这些对象的一个​​实例,您可以通过仅使用 TextBox 作为内容并更改其文本来测试这一点,然后循环浏览视图,它将仍然在那里。如果不是这样就不会有this之类的问题。
    • 这很有趣。当我试图构建一个演示以确定在这种情况下是否属实时,我发现您所描述的结果是您的答案中的方法实际上根本不起作用。查看我的编辑。
    • 你可能误解了这里的问题,关键句是:"这会抛出异常,因为Setter.Value的值是共享的,继承自UIElement的对象不能有多个父级." 因此,如果这种样式从不用于多个 ContentControl(可以通过直接设置而不将其作为资源提供来确保),这完全没有问题。 (我之前在其中一个应用程序中也使用过这种方法,效果很好)。
    • 其实你甚至可以使这个工作为多个样式目标工作,你只需要将样式定义为x:Shared设置为false的资源,试试看。 (比如,这有多酷?)
    • 好吧,我测试了你的代码 - 我将它复制到一个项目中并创建了 TreeViewModeGridModeView 控件,正如我链接到的博客文章所示,它崩溃了。我是不是做错了什么?
    猜你喜欢
    • 2018-07-19
    • 2014-01-31
    • 2020-04-01
    • 2015-09-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-06-05
    • 1970-01-01
    相关资源
    最近更新 更多