【问题标题】:WPF/MVVM: switching between viewsWPF/MVVM:在视图之间切换
【发布时间】:2020-06-11 20:28:04
【问题描述】:

我正在尝试学习 WPF/MVVM,目前正在研究如何在视图之间切换。我首先在互联网上寻找一些例子来学习。我使用的非常简单:两个视图(名为“Home”和“Account”)只显示一个标签,以保持 xaml 和 VM 的简单性,以及一个带有两个按钮的主窗口,用于在视图之间切换。

DataTemplates 在 App.xaml 文件中声明(与命名空间一起),因此它们对整个项目应该是全局的:

<Application.Resources>
    <DataTemplate DataType="{x:Type viewmodels:HomeViewModel}">
        <views:HomeView/>
    </DataTemplate>
    <DataTemplate DataType="{x:Type viewmodels:AccountViewModel}">
        <views:AccountView/>
    </DataTemplate>
</Application.Resources>

按照我的理解,这个技巧是由第三个 VM(称为 MainViewModel.cs)完成的,它实现了一个 SelectedViewModel 属性,该属性跟踪必须显示的 VM,以及绑定到按钮的 ICommand:

        private BaseViewModel _selectedViewModel;
        public BaseViewModel SelectedViewModel
        {
            get { return _selectedViewModel; }
            set
            {
                _selectedViewModel = value;
                OnPropertyChanged(nameof(SelectedViewModel));
            }
        }
        public ICommand UpdateViewCommand { get; set; }

MainWindow.xaml 如下所示:

        <ContentControl Grid.Row="0" Content="{Binding SelectedViewModel}"/>

        <StackPanel Grid.Row="1" Orientation="Horizontal" HorizontalAlignment="Center">
            <Button Margin="10" Width="200" Content="Home" Command="{Binding UpdateViewCommand}" CommandParameter="Home"/>
            <Button Margin="10" Width="200" Content="Account" Command="{Binding UpdateViewCommand}" CommandParameter="Account"/>
        </StackPanel>

在一个单独的类文件 (UpdateViewModel.cs) 中,UpdateViewModel 类实现了 Execute 方法,如下所示:

        public void Execute (object parameter)
        {
            if (parameter.ToString() == "Home")
            {
                viewModel.SelectedViewModel = new HomeViewModel();
            }
            else if (parameter.ToString() == "Account")
            {
                viewModel.SelectedViewModel = new AccountViewModel();
            }
        }

我希望我已经给出了这个想法,而不会让你感到厌烦。这一切都有效,让我了解基础知识。现在我想尝试一种变体,即采用一个视图(“帐户”视图)并实现一个可以直接切换到另一个视图的按钮。我以为我要做的就是将按钮绑定到 UpdateViewModel 类,最初我修改了 Account.xaml 代码如下:

        <Button Content="Button" Command="{Binding Path=UpdateViewCommand}" CommandParameter="Home"/>

程序运行,但是当我单击帐户视图中的按钮时,没有任何反应。所以我把它改成了更复杂的东西:

    <UserControl.DataContext>
        <src:MainViewModel/>
    </UserControl.DataContext>
...
    <Button Content="Button" Command="{Binding Path=UpdateViewCommand}" CommandParameter="Home"/>

但结果是一样的。我怀疑它与绑定有关,但看不到如何更改它。有人可以帮忙吗?

【问题讨论】:

    标签: c# wpf xaml


    【解决方案1】:

    欢迎来到 SO!

    您的按钮绑定是相对于当前 DataContext 完成的,因此绑定到 AccountView 中的 UpdateViewCommand(比如说)将尝试绑定到 AccountViewModel 中的 UpdateViewCommand,而不是 MainViewModel。

    有两种方法可以解决这个问题。第一个是更改按钮绑定以绑定到父视图模型:

    <Button Content="Button" Command="{Binding Path=DataContext.UpdateViewCommand, RelativeSource={RelativeSource AncestorType={x:Type local:MainWindow}}}" CommandParameter="Home"/>
    

    当然,这里的一个主要缺点是您的子视图所在的视觉层次结构可能并不总是像这样可预测。

    第二种(更好的)方法是将 UpdateViewCommand 处理程序添加到您的每个子视图模型,然后让它们将控制权传递给您实际想要处理的任何内容。在实践中,您通常会为所有孩子创建一个基类,以减少代码重复,并且您会创建一个服务(例如 INavigationService)供他们调用。然后,您的 MainViewModel (例如)将实现此接口,并且您将使用依赖注入在创建子视图模型时将该引用注入到子视图模型中(或者,如果您不希望,则将父视图模型直接传递给它们的构造函数使用完整的 DI 框架)。

    【讨论】:

    • 嗨,马克,感谢您的出色建议。你提出的第一种方法实际上帮助我更好地理解了我考虑过但并不完全清楚的那种特定的绑定形式。第二种方法看起来很有趣,但就我目前的知识而言,它可能有点太高级了,尤其是当您提到依赖注入时。你能推荐一些关于这方面的好文章吗?
    • @GiovanniNieddu DI 是您可能已经在一定程度上使用的东西之一,但您并没有意识到这一点。这是一种实现控制反转的方式,但会处理在项目中传递引用的所有混乱细节。它极大地简化了您的架构设计,一旦您开始使用它,您将永远不会回头。 C# 的 DI 框架有很多,但 Ninject 是最好的 IMO,Christiaan Verwijs 写了a really good tutorial 和整个 DI。
    猜你喜欢
    • 1970-01-01
    • 2023-04-01
    • 1970-01-01
    • 1970-01-01
    • 2021-06-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多