【问题标题】:Two Views in one ViewModel (WPF/MVVM)一个 ViewModel 中的两个视图 (WPF/MVVM)
【发布时间】:2018-03-27 22:07:28
【问题描述】:

问题:使用具有两个不同视图的单个视图模型。

我有一个Window 和一个控件ContentControl,它绑定到DataContext 中的一个属性,称为Object MainContent {get;set;}。基于 navigationType enum 属性,我将其他 ViewModel 分配给它以显示正确的 UserControl

我需要将两个视图合并到一个 ViewModel 中,并且因为我将一个 ViewModel 分配给前面提到的 ContentControl,所以 TemplateSelector 无法识别哪个是正确的视图,因为它们共享相同的 viewModel

如果我将视图而不是 ViewModel 分配给 ContentControl,则会显示正确的视图,但是,所有命令都不起作用。

有什么帮助吗?提前致谢。




解决方案:基于@mm8 答案和https://stackoverflow.com/a/5310213/2315752

ManagePatientViewModel.cs

public class ManagePatientViewModel : ViewModelBase
{
    public ManagePatientViewModel (MainWindowViewModel inMainVM) : base(inMainVM) {}
} 

ViewHelper.cs

public enum ViewState
{
    SEARCH,
    CREATE,
}

MainWindowViewModel.cs

public ViewState State {get;set;}
public ManagePatientViewModel VM {get;set;}

private void ChangeView(ViewState inState)
{
    State = inState;

    // This is need to force the update of Content.
    var copy = VM;
    MainContent = null;
    MainContent = copy;
}

public void NavigateTo (NavigationType inNavigation)
{
    switch (inNavigationType)
    {
        case NavigationType.CREATE_PATIENT:
            ChangeView(ViewState.CREATE);
            break;
        case NavigationType.SEARCH_PATIENT:
            ChangeView(ViewState.SEARCH);
            break;
        default:
            throw new ArgumentOutOfRangeException(nameof(inNavigationType), inNavigationType, null);
    }
}

MainWindow.xaml

<DataTemplate x:Key="CreateTemplate">
        <views:CreateView />
</DataTemplate>

<DataTemplate x:Key="SearchTemplate">
        <views:SearchView/>
</DataTemplate>

<TemplateSelector x:Key="ViewSelector"
    SearchViewTemplate="{StaticResource SearchTemplate}"
    CreateViewTemplate="{StaticResource CreateTemplate}"/>

<ContentControl
        Grid.Row="1"
        Content="{Binding MainContent}"
        ContentTemplateSelector="{StaticResource ViewSelector}" />

模板选择器.cs

public class TemplateSelector : DataTemplateSelector
{
    public DataTemplate SearchViewTemplate {get;set;}
    public DataTemplate CreateViewTemplate {get;set;}
}

public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
    if (!(item is SelectLesionViewModel vm))
    {
        return null;
    }

    switch (vm.ViewType)
    {
        case ViewState.CREATE:
            return CreateViewTemplate;
        case ViewState.SEARCH:
            return SearchViewTemplate;
        default:
            return null;
        }
    }
}

【问题讨论】:

  • 当有两种视图模型类型映射到一个视图类型时,TemplateSelector 应该如何知道使用哪个模板?这根本没有意义。您应该使用两种不同的类型。
  • 感谢您的回答@mm8,这就是我问的原因,我不知道该怎么做,或者我应该改变什么。你能给我指出正确的方向吗?跨度>
  • 我发布了答案。

标签: c# wpf mvvm


【解决方案1】:

当有两种视图类型映射到一个视图模型类型时,TemplateSelector 应该如何知道使用哪个模板?恐怕这毫无意义。

您应该使用两种不同的类型。您可以在公共基类中实现逻辑,然后定义两个标记类型,它们只是从该实现派生并且不添加任何功能:

public class ManagePatientViewModel { */put all your code in this one*/ }

//marker types:

public class SearchPatientViewModel { }

public class CreatePatientViewModel { }

此外,如果您从模板中删除 x:Key 属性,您实际上并不需要模板选择器:

<DataTemplate DataType="{x:Type viewModels:SearchPatientViewModel}">
     <views:SearchPatientView />
</DataTemplate>

<DataTemplate DataType="{x:Type viewModels:CreatePatientViewModel}">
    <views:CreatePatientView />
</DataTemplate>

...
<ContentControl
    Grid.Row="1"
    Content="{Binding MainContent}" />

【讨论】:

  • 我可能解释错了,我需要的是两个不同的视图和一个视图模型,而不是两个视图模型到一个视图中。
  • 我已经测试过了,我有同样的问题,命令不起作用。问题是我已经有两个不同的视图模型,这就是为什么我一开始没有明白你的意思。一是寻找,二是创造。我已将它们合并到ManagePatientViewModel 中,我应该将它用于两个视图。基本上我认为TemplateSelector 可以一次选择正确的视图。
  • 基本上我必须简化我们拥有的所有视图和viewsModel,并且有两个视图(CreateView 和SearchView)和一个viewModel (ManagePatientViewModel) 是有意义的。我想发布每个视图的代码,但是很广泛,Stack 抱怨“代码太多”。我认为拥有两个具有相同 ViewModel 的视图会很容易,我会忘记这一点。
  • 我已经更新了我的问题,我也应用了您的解决方案,但它不起作用,视图更改为正确的视图,但未调用 viewModel 中绑定的命令。对于专家来说可能很容易,但我只是 WPF 的新手。
  • 问题是 TemplateSelector.SelectTemplate.Item 是我在 MainContent 中分配的,所以我不能分配相同的 viewModel,因为那样模板选择器将不知道哪个是正确的。跨度>
【解决方案2】:

也许要求是切换视图并保留一个视图模型。 数据模板只是实例化视图的一种方式。 您可以改为将 contentcontrol 的 datacontext 设置为 viewmodel 的实例,并将视图切换为内容。由于视图是一种视图责任,因此此类任务可以完全在视图中完成,而不会“破坏” mvvm。 这是一个非常快速和肮脏的方法来说明我的意思。 我构建了两个用户控件,UC1 和 UC2。这些对应于您的各种患者观点。 这是其中一个的标记:

<StackPanel>
    <TextBlock Text="User Control ONE"/>
    <TextBlock Text="{Binding HelloString}"/>
</StackPanel>

我创建了一个简单的视图模型。

public class OneViewModel
{
    public string HelloString { get; set; } = "Hello from OneViewModel";
}

我的主窗口标记:

<Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="100"/>
        <ColumnDefinition Width="*"/>
    </Grid.ColumnDefinitions>
    <StackPanel>
        <Button Content="UC1" Click="UC1_Click"/>
        <Button Content="UC2" Click="UC2_Click"/>
    </StackPanel>
    <ContentControl Name="parent"
                    Grid.Column="1"
                    >
        <ContentControl.DataContext>
            <local:OneViewModel/>
        </ContentControl.DataContext>
    </ContentControl>
</Grid>

点击事件切换出内容: 私人无效UC1_Click(对象发送者,RoutedEventArgs e) { parent.Content = new UC1(); }

    private void UC2_Click(object sender, RoutedEventArgs e)
    {
        parent.Content = new UC2();
    }

oneviewmodel 的单个实例被保留并且显示的视图切换出去。 hellostring 绑定并在两者中都显示正常。

在您的应用中,您需要一种更复杂的方法来设置该数据上下文,但此示例纯粹是为了向您展示另一种方法的概念证明。

这是工作示例: https://1drv.ms/u/s!AmPvL3r385QhgpgMZ4KgfMWUnxkRzA

【讨论】:

  • 感谢您的回答,这是我尝试的第一种方法,但我将 ViewModel 分配给 ContentControl.Content 而不是视图。如果我认为非命令或绑定属性的视图有效。
  • 该代码来自一个工作示例,我已将其上传到我的 onedrive,您可以查看一下。绑定有效。
猜你喜欢
  • 2014-10-24
  • 1970-01-01
  • 2010-12-31
  • 1970-01-01
  • 2020-05-19
  • 2019-03-11
  • 2010-12-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多