为不同的ViewModel动态显示不同的View但属性相同

时间:2013-07-03 12:03:14

标签: c# wpf mvvm

好的,我正试图搞定MVVM。我有一个应用程序,有多个图像捕获选项。根据模式,图像可以从现有文件加载,也可以从相机捕获。

我正在使用代表图像捕获设备配置的MVVM模式编写页面。

该模型由两个类组成,这两个类公开了符合IImageSource的公共接口的每个模式的特定(和非公共)值。

两个模型类中的每一个都有一个上下文定义的视图模型:

  • CameraSourceViewModel
  • FileSourceViewModel

和两个相应的观点。

  • CameraSourceView
  • FileSourceView

模型有一个返回IImageSource的属性。

我目前正在使用第三个视图ImageSourceView作为页面。我正在处理从模型中获取值的加载事件,然后,根据类型将实例化正确的视图模型和正确的视图,然后添加它作为内容。然而,这似乎违背了MVVM的精神,因为我现在已经在代码中编写了一些决策代码

是否有更优雅/更好的方法来确定应该实例化和使用哪个viewmodel / view?

3 个答案:

答案 0 :(得分:3)

实际上,您不需要TemplateSelector,因为两个ViewModel将具有不同的类型。您可以将XAML中的DataTemplates声明为模型类型为key的资源,以便WPF自动选择正确的DataTemplate:

  • 有一个主ViewModel,它公开了一个ImageSourceViewModel属性。该属性将根据需要返回CameraSourceViewModel或FileSourceViewModel。
  • 在你的页面中,DataContext将是主要的ViewModel,你可以像这样使用XAML:

代码示例:

<Page x:Class="Page1"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
  xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
  xmlns:my="clr-namespace:WpfApplication1"
  mc:Ignorable="d" 
  d:DesignHeight="300" d:DesignWidth="300"
  Title="Page1">
<Page.Resources>
    <DataTemplate DataType="{x:Type my:CameraSourceViewModel}">
        <my:CameraSourceView/>
    </DataTemplate>
    <DataTemplate DataType="{x:Type my:FileSourceViewModel}">
        <my:FileSourceView/>
    </DataTemplate>
</Page.Resources>
<Grid>
    <ContentControl Content="{Binding ImageSourceViewModel}"/>
</Grid>
</Page>

答案 1 :(得分:1)

您有两个需要决定在运行时使用哪种类型的地方:

  1. 视图模型
  2. 查看
  3. 在ViewModel级别上只使用ViewModel工厂,因此只需通过EventType / ValueType实例化相应的ViewModel:

    private IImageSourceViewModel ProcessEvent(IEvent someEvent)
    {
        return viewModelFactory.Create(someEvent.Type)
    }
    

    然后在View级别上使用DataTemplateSelector接受已绑定的ViewModel实例,然后决定使用哪个View:

    MainView XAML:

    <ContentControl 
       Content="{Binding ImageSourceViewModel}"    
       ContentTemplateSelector = 
          "{StaticResource ImageSourceViewDataTemplateSelector}">
    </ContentControl>
    

    ImageSourceViewDataTemplateSelector:

    private sealed class ImageSourceViewDataTemplateSelector: DataTemplateSelector
    {
        public ImageSourceViewDataTemplateSelector(... dependencies if any...)
        {             
        }
        public override DataTemplate SelectTemplate(object item, DependencyObject container)
        {
            DataTemplate dataTemplate = null;
            IImageSourceViewModel instance = item as IImageSourceViewModel;
    
            // move out into the constructor
            var dataTemplateFactory = new Dictionary<Type, Func<DataTemplate>>
                {
                        { typeof(ICameraSourceViewModel), (x) => this.Resources["CameraSourceDataTemplate"] as DataTemplate }, 
                        { typeof(IFileSourceViewModel), (x) => this.Resources["FileSourceViewModel"] as DataTemplate }
                };
    
            // TODO: handle not supported type case yourself
    
            return dataTemplateFactory[instance.GetType()]();
        }
    }
    

答案 2 :(得分:1)

以下是关于你能做什么的一些想法:

拥有ImageSourceViewModel,即ImageSourceView视图的ViewModel。这个viewModel的作用是从模型中获取“你的价值”,并将其作为IImageSource类型的公共属性公开。

然后,在ImageSourceView视图中,您可以使用模板选择器来更改视图的内容,具体取决于公开的IImageSource属性的具体类型。

请参阅http://www.codeproject.com/Articles/418250/WPF-Based-Dynamic-DataTemplateSelector