WPF:根据相应的ViewModel(MVVM)切换UserControls

20

我将尝试通过想象以下示例来简化我正在处理的任务:

假设我们有以下模型类层次结构:

Animal
   Lion
   Snake
   Bird

...相应的ViewModel:

AnimalCollectionViewModel
   AnimalViewModel
      LionViewModel
      SnakeViewModel
      BirdViewModel

...以及相应的视图:

AnimalCollectionView
   LionView
   SnakeView
   BirdView

AnimalCollection应该包含一个填充不同类型动物对象的列表,并在列表下方拥有一个属性网格用于设置所选动物的属性。显然,属性网格将具有不同的属性,并且在所选项的类型更改时应更改。

问题是:如何按照MVVM模式实现在WPF中切换属性网格?使用什么机制?

目前,在基本ViewModel中具有一个抽象枚举属性(AnimalViewModel.PropertyGridType = {Lion, Snake, Bird}),派生类通过返回相应的值来实现它。而AnimalCollectionView根据此属性的值更改属性网格用户控件。像这样:

...

<UserControl.Resources>
    <Style x:Key="PropertyGridStyle" TargetType="ContentControl">
        <Style.Triggers>
            <DataTrigger Binding="{Binding PropertyGridType}" Value="Lion">
                <Setter Property="Content">
                    <Setter.Value>
                        <view:LionPropertyGridView />
                    </Setter.Value>
                </Setter>
            </DataTrigger>
            <DataTrigger Binding="{Binding PropertyGridType}" Value="Snake">
                <Setter Property="Content">
                    <Setter.Value>
                        <view:SnakePropertyGridView />
                    </Setter.Value>
                </Setter>
            </DataTrigger>
        </Style.Triggers>
    </Style>
</UserControl.Resources>

<ContentControl Style="{StaticResource PropertyGridStyle}" />

但我不确定这是否是正确的方法。(至少我不喜欢引入辅助枚举属性。是否可能根据ViewModel类型推断出必要的用户控件?)有人能给出其他选项吗? 提前感谢!


只要您将视图的逻辑与视图分离并使用数据绑定,就可以轻松地进入MVVM领域。这样做的一个主要好处是可测试性,因此请从用例和一些测试开始。然后,您可以根据需要应用不同的OOP和WPF技术。干杯! - Berryl
2个回答

23

能否根据ViewModel类型推断出必要的用户控件?

你的意思是像这样吗?

<Window.Resources>
   <DataTemplate DataType="{x:Type vm:LionViewModel}">
      <v:LionView />
   </DataTemplate>
   <DataTemplate DataType="{x:Type vm:SnakeViewModel}">
      <v:SnakeView />
   </DataTemplate>
   <DataTemplate DataType="{x:Type vm:BirdViewModel}">
      <v:BirdView/>
   </DataTemplate>
</Window.Resources>

请参考Josh Smith关于MVVM的文章中的“将视图应用到视图模型”部分。文章链接:http://msdn.microsoft.com/en-us/magazine/dd419663.aspx#id0090016

编辑:

下面是一个非常简单的基于类型的模板选择示例,您可以将其复制到Kaxaml中来验证它确实有效:

<Page
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  xmlns:sys="clr-namespace:System;assembly=mscorlib">
  <Page.Resources>
    <sys:String x:Key="string">this is a string</sys:String>
    <sys:Int32 x:Key="int32">1234</sys:Int32>
    <DataTemplate DataType="{x:Type sys:String}">
      <TextBlock Foreground="Red" Text="{Binding}"/>
    </DataTemplate>
    <DataTemplate DataType="{x:Type sys:Int32}">
      <TextBlock Foreground="Blue" Text="{Binding}"/>
    </DataTemplate>
  </Page.Resources>
  <StackPanel>  
    <ContentControl Content="{Binding Source={StaticResource string}}"/>
    <ContentControl Content="{Binding Source={StaticResource int32}}"/>
  </StackPanel>
</Page>

2
我几乎确定这正是我想表达的:)) 谢谢,罗伯特 - Niccolo
再次感谢你,Robert。还有一个问题:应用这些数据模板的意思是它们应该用于类似集合控件(如 ListBox)中吗?例如,它们可以应用于用于显示单个数据项的 UserControl 吗? - Niccolo
2
你可以在项控件或内容控件中使用它们。例如,<ContentControl Content="{Binding X}"/> 创建一个包含与 X 类型匹配的任何数据模板的 ContentPresenter,就像 <ItemsControl ItemsSource="{Binding X}"/> 创建一个充满 ItemsPresenters 的面板一样。(通常,任何具有 ItemsSource 属性的都是项控件;任何具有 Content 属性的都是内容控件。) - Robert Rossney
1
嗯,如果没有更多的信息,我无法告诉您做错了什么,但我可以告诉您它应该可以工作-检查我对评论所做的编辑,以获取您可以粘贴到Kaxaml中的快速测试用例。 - Robert Rossney
1
你正在正确的轨道上。在实践中,你经常会在集合视图模型中创建一个 SelectedItem 属性 - 如果,例如,你正在实现命令 - 并将列表框中选定的项和内容控件的内容都绑定到该属性。要直接绑定到控件,请使用 {Binding ElementName=foo, Path=SelectedItem}。如果这不起作用,请在控件中放置一些文字内容并确保它可见 - 当真正的问题是布局问题时,我花了很多时间来排除完全正常的绑定问题。 - Robert Rossney
显示剩余5条评论

2
您可以使用 DataTemplateSelector 来实现。选择正确的模板方法取决于您,您可以使用枚举或测试类类型等方式进行选择。

谢谢,Goran,我会看一下的。 - Niccolo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接