C# 为不同的ViewModel但相同的属性动态显示不同的视图

C# 为不同的ViewModel但相同的属性动态显示不同的视图,c#,wpf,mvvm,C#,Wpf,Mvvm,好的,我正在努力掌握MVVM。我有一个应用程序,它有多个图像捕获选项。根据模式,图像可以从现有文件加载,也可以从相机捕获 我正在使用MVVM模式编写一个页面,该模式表示图像捕获设备的配置 该模型由两个类组成,它们为符合IImageSource公共接口的每种模式公开特定(和非公共)值 两个模型类中的每一个都有一个上下文定义的viewmodel: CameraSourceViewModel FileSourceViewModel 以及两种相应的观点 CameraSourceView FileS

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

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

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

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

  • CameraSourceViewModel
  • FileSourceViewModel
以及两种相应的观点

  • CameraSourceView
  • FileSourceView
该模型有一个返回IImageSource的属性

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


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

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

  • 视图模型
  • 看法
  • 在ViewModel级别上,只需使用ViewModel工厂,即可通过EventType/ValueType实例化适当的ViewModel:

    private IImageSourceViewModel ProcessEvent(IEEvent someEvent)
    {
    返回viewModelFactory.Create(someEvent.Type)
    }
    
    然后在视图级别上,只需使用DataTemplateSelector,它通过绑定已解析的ViewModel实例接受,然后决定使用哪个视图:

    MainViewXAML:

    
    
    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()]();
        }
    }
    
    私有密封类ImageSourceViewDataTemplateSelector:DataTemplateSelector
    {
    公共ImageSourceViewDataTemplateSelector(…依赖项,如果有…)
    {             
    }
    公共覆盖数据模板SelectTemplate(对象项,DependencyObject容器)
    {
    DataTemplate DataTemplate=null;
    IImageSourceViewModel实例=项目作为IImageSourceViewModel;
    //移入构造函数
    var dataTemplateFactory=新字典
    {
    {typeof(ICameraSourceViewModel),(x)=>this.Resources[“CameraSourceDataTemplate”]as DataTemplate},
    {typeof(IFileSourceViewModel),(x)=>this.Resources[“FileSourceViewModel”]作为数据模板}
    };
    //TODO:自己处理不支持的类型大小写
    返回dataTemplateFactory[instance.GetType()]();
    }
    }
    
    以下是一些您可以做的事情:

    拥有一个
    ImageSourceViewModel
    ,即
    ImageSourceView
    视图的视图模型。此viewModel的角色是从模型中获取“您的值”,并将其作为类型为
    IImageSource
    的公共属性公开

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


    请参见

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

    • 有一个主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>
    
    
    
    我已经尽了最大的努力来破解你想在这里告诉我的东西,但我有点失败了。我对字典里的Func有点小麻烦;IFilter的作用是什么?为什么不在最后一行中传递它的值?很抱歉,花了这么长时间才标记出您的答案,在项目的另一部分被跟踪,只需要回到这里。
    <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>