内容控制中的MVVM轻切换视图(用户控件)

时间:2018-07-24 16:23:13

标签: c# wpf mvvm

我知道这个问题经常出现,但是我想了解这一点,而不仅仅是复制代码。我有两个UserControls和一个MainWindow。我要实现的是简单的导航功能:按下按钮1->在内容控件中打开UserControl1。与UserControl2的按钮2相同。

这就是我得到的(虽然不多,但全部由我自己编码,如果出现问题,请原谅):

MainWindow.xaml:

<Window x:Class="gf_mvvmlight.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
    xmlns:command="http://www.galasoft.ch/mvvmlight"

    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:local="clr-namespace:gf_mvvmlight"
    mc:Ignorable="d"
    Title="MainWindow" Height="450" Width="800"         
    DataContext="{Binding Main, Source={StaticResource Locator}}" >

<i:Interaction.Triggers>
    <i:EventTrigger EventName="Loaded">
        <command:EventToCommand Command="{Binding LoadedCommand, Mode=OneWay}" />
    </i:EventTrigger>
</i:Interaction.Triggers>
<Grid>
    <Button>switch to Page 2</Button>
    <ContentControl Content="{Binding Page1View, Source={StaticResource Locator}}" />
</Grid>

Page1.xaml(Page2相同):

<UserControl x:Class="gf_mvvmlight.View.Page1View"
         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:local="clr-namespace:gf_mvvmlight.View"
         mc:Ignorable="d" 
         d:DesignHeight="450" d:DesignWidth="800"
                      DataContext="{Binding Path=Page1ViewModel, Source={StaticResource Locator}}">

<Grid>
    <Label>orkpwefkwe99</Label>
</Grid>

ViewModelLocater.cs:

public ViewModelLocator()
    {
        ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);
        SimpleIoc.Default.Register<MainViewModel>();
        SimpleIoc.Default.Register<Page1ViewModel>();
        SimpleIoc.Default.Register<Page2ViewModel>();
    }
    public MainViewModel Main
    {
        get
        {
            return ServiceLocator.Current.GetInstance<MainViewModel>();
        }
    }
    public Page1ViewModel Page1View
    {
        get
        {
            return ServiceLocator.Current.GetInstance<Page1ViewModel>();
        }
    }
    public Page2ViewModel Page2View
    {
        get
        {
            return ServiceLocator.Current.GetInstance<Page2ViewModel>();
        }
    }

所以我的问题是:按下按钮时,如何使ContentControl动态并打开Page2View?我没有任何代码,只是一些技巧:)

并且该程序应该能够切换每个类的视图!!!

预先感谢:)

1 个答案:

答案 0 :(得分:1)

我个人从未见过使用视图模型定位器的任何令人信服的理由。我通常将其删除,在App.xaml中声明MainViewModel的实例,并在DataContext="{StaticResource MainViewModel}"的MainWindow中引用它。

无论是否使用ViewModelLocator,都可以使用DataTemplates填充视图。因此,在MainViewModel中,我通常会执行以下操作:

private BasePage _CurrentPage;
public BasePage CurrentPage
{
    get { return this._CurrentPage; }
    set
    {
        if (this._CurrentPage != value)
        {
            this._CurrentPage = value;
            RaisePropertyChanged(() => this.CurrentPage);
        }
    }
}

然后我将BasePage子类化为不同的页面类型,即PageViewModel1,PageViewModel2等。然后在xaml中,我的MainWindow如下所示:

<Window.Resources>

    <DataTemplate DataType="{x:Type vm:PageViewModel1}">
        <views:PageView1 />
    </DataTemplate>

    <DataTemplate DataType="{x:Type vm:PageViewModel2}">
        <views:PageView2 />
    </DataTemplate>

</Window.Resources>

<ContentControl Content="{Binding CurrentPage}" />

...其中PageView1和PageView2是您的用户控件。因此,现在每当将CurrentPage设置为一个页面时,ContentControl就会使用适当的DataTemplate填充它。

请注意,仅仅因为我不使用ViewModelLocator并不意味着我不相信使用依赖注入,实际上恰恰相反。我只是认为这里不需要它,而且有比ViewModelLocator(即Ninject)更好的实现。

更新:根据下面的问题进一步阐明这一点...上面的示例暗示了3种视图模型:MainViewModel,PageViewModel1和PageViewModel2。我与DataTemplates一起发布的XAML应该在您的MainWindow上,它的DataContext设置为MainViewModel的实例。假设MainViewModel构造函数执行以下操作:

public MainViewModel()
{    
    this.CurrentPage = new PageViewModel1();
}

该行代码设置CurrentPage,如果您像我一样声明了它(即使用属性更改通知),则MainView中的ContentControl将自动填充PageView1控件的实例。之所以这样做,是因为1)ContentControl绑定到CurrentPage属性,并且2)我设置了一个DataTemplate,该模板有效地说:“只要某内容的内容为PageViewModel1类型,我希望您使用PageView1用户控件。此外,这也很重要……将PageView1控件的DataContext设置为CurrentPage是什么(即PageViewModel1的实例),以便可以绑定到那里的属性。我们在屏幕上显示一个相应的视图模型,其中包含与该视图相关的所有逻辑。

现在,假设我们重构PageViewModel1和PageViewModel2以接受指向父级MainViewModel的指针,并假设响应按钮按下或执行以下操作:

this.MyParentMainViewModel.CurrentPage = new PageViewModel2()

该属性现在已更改,并且该框架将通过处理PageView1控件并将其替换为PageView2控件进行响应,该控件又将其DataContext设置为CurrentPage。因此,您已经“更改了页面”,但是您完全在视图模型层中完成了此操作,并依靠数据绑定和数据模板将这些更改自动传播到视图层。

这显然是一个非常非常简单的示例,我仅用于说明DataTemplating的工作方式。实际上,子视图模型永远不会直接访问其父级,或者甚至永远不会了解它们,您通常会使用依赖项注入框架将接口传递给子级,然后由父级处理...。但这是另一个主题文章。 :)