2

好的,我正在尝试掌握 MVVM。我有一个具有多种图像捕获选项的应用程序。根据模式,图像要么从现有文件加载,要么从相机捕获。

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

该模型由两个类组成,它们为符合IImageSource.

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

  • CameraSourceViewModel
  • FileSourceViewModel

和两个对应的视图。

  • CameraSourceView
  • FileSourceView

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

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

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

4

3 回答 3

4

实际上,您不需要 TemplateSelector,因为这两个 ViewModel 将具有不同的类型。您可以将 XAML 中的 DataTemplates 声明为以模型类型为键的资源,以便 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>
于 2013-07-03T15:43:59.580 回答
1

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

  1. 视图模型
  2. 看法

在 ViewModel 级别只使用 ViewModel 工厂,因此只需通过 EventType/ValueType 实例化适当的 ViewModel:

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

然后在视图级别使用 DataTemplateSelector ,它通过绑定接受已经解析的 ViewModel 实例,然后决定使用哪个视图:

主视图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()]();
    }
}
于 2013-07-03T12:12:49.777 回答
1

这里有一些关于你可以做什么的想法:

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

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

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

于 2013-07-03T12:14:12.810 回答