一个ViewModel中的两个视图(WPF / MVVM)

时间:2018-03-22 12:25:24

标签: c# wpf mvvm

问题:使用一个具有两个不同视图的viewModel。

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

我需要将两个视图合并到一个ViewModel中,并且因为我将ViewModel分配给之前提到的ContentControlTemplateSelector无法识别哪个是正确的视图,因为两个共享相同的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}" />

TemplateSelector.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;
        }
    }
}

2 个答案:

答案 0 :(得分:2)

当有两种视图类型映射到单个视图模型类型时,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}" />

答案 1 :(得分:2)

可能要求是切换视图并保留一个视图模型。 Datatemplating只是实例化视图的一种方法。 您可以将contentcontrol的datacontext设置为viewmodel的实例,并将视图切换为内容。由于视图是一种视图责任,因此这些任务可以在视图中完全完成而不会打破&#34; 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>

点击事件会切换内容:         private void UC1_Click(object sender,RoutedEventArgs e)         {             parent.Content = new UC1();         }

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

保留oneviewmodel的单个实例,并显示所显示的视图。 hellostring绑定并显示两者都正常。

在你的应用程序中,你需要一种更复杂的方法来设置datacontext,但这个样本纯粹是为了向你展示另一种方法的概念证明。

这是工作样本: https://1drv.ms/u/s!AmPvL3r385QhgpgMZ4KgfMWUnxkRzA