WPF MVVM导航视图

54

我有一个包含多个视图的WPF应用程序。我想要从视图1切换到视图2,然后可以在那里切换到多个视图。因此,我希望在视图1上有一个按钮,可以在同一窗口中加载视图2。

我尝试了这些方法,但无法使其工作。

从第一个链接中,问题在于我不理解ViewModelLocator代码。他们调用CreateMain();函数,但是它在哪里定义,以及如何从视图内部切换到另一个视图。


1
@AndrasSebö,在这个问题上,我不同意你的看法。虽然我承认这不是一个很好的问题,但我见过更糟糕的问题,我相信用户想要什么是非常清楚的。 - Sheridan
3
如何在一个视图内部切换视图? - user2499088
1
你找到了解决这个问题的好方法吗? - User1551892
1
我最终使用了Magellan框架。对于具有大量导航的应用程序来说,这是一个很棒的框架。 - user2499088
1
@user2499088,请将其放入答案中,可能需要添加几行,然后接受它作为正确答案,以便将此问题标记为已回答。 - Sheridan
3
@user2499088,请添加一个答案并接受它。如果这个问题没有被接受的答案,那么重复的问题将不能被关闭。请遵循“当有人回答我的问题时我该怎么做?”和“什么是接受的答案?”页面上的指南。 - Sheridan
4个回答

129
首先,您不需要使用任何工具包/框架来实现MVVM。它可以像这样简单...假设我们有一个名为MainViewModel的视图模型,以及一个名为PersonViewModel和一个名为CompanyViewModel的视图模型,每个视图模型都与其相关视图相关联,并扩展了BaseViewModel抽象基类。
BaseViewModel中,我们可以添加公共属性和/或ICommand实例,并实现INotifyPropertyChanged接口。由于它们都扩展了BaseViewModel类,因此我们可以在MainViewModel类中拥有此属性,该属性可以设置为我们的任何视图模型之一:
public BaseViewModel ViewModel { get; set; }
当然,您会正确地在您的属性上实现INotifyPropertyChanged接口,不像这个快速示例。现在,在App.xaml中,我们声明一些简单的DataTemplate来连接视图和视图模型:
<DataTemplate DataType="{x:Type ViewModels:MainViewModel}">
    <Views:MainView />
</DataTemplate>
<DataTemplate DataType="{x:Type ViewModels:PersonViewModel}">
    <Views:PersonView />
</DataTemplate>
<DataTemplate DataType="{x:Type ViewModels:CompanyViewModel}">
    <Views:CompanyView />
</DataTemplate>

现在,无论我们在应用程序中使用哪个BaseViewModel实例,这些DataTemplate都会告诉框架显示相关的视图。我们可以像这样显示它们:

现在,在我们的应用程序中使用任何一个BaseViewModel实例时,这些DataTemplate将指示框架显示相关视图。我们可以像下面这样显示它们:

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

现在,要切换到新视图,我们只需要从MainViewModel类中设置ViewModel属性:

ViewModel = new PersonViewModel();

最后,我们如何从其他视图更改视图?好吧,有几种可能的方法可以做到这一点,但最简单的方法是直接从子视图添加一个 BindingMainViewModel 中的 ICommand。我使用了 RelayComand 的自定义版本,但你可以使用任何你喜欢的类型,并且我猜你会明白:

public ICommand DisplayPersonView
{
    get { return new ActionCommand(action => ViewModel = new PersonViewModel(), 
        canExecute => !IsViewModelOfType<Person>()); }
}

在子视图的XAML中:

<Button Command="{Binding DataContext.DisplayPersonView, RelativeSource=
    {RelativeSource AncestorType={x:Type MainView}}, Mode=OneWay}" />

就是这样了!享受吧。


6
如果您正在使用MVVM Light,那么您应该坚持使用他们用来完成这些任务的工具... 我的观点是,您不需要使用框架来实现此功能。 - Sheridan
4
不确定我真正理解您的问题@ETG87。视图模型类只是扩展“BaseViewModel”类的类,而视图只是“UserControl”。 - Sheridan
3
伙计,你需要阅读有关WPF XAML的XAML命名空间和命名空间映射的内容……我的视图在AppName.Views命名空间中声明,它具有映射的XAML命名空间前缀Views。视图模型在AppName.ViewModels命名空间中声明,因此映射到XAML命名空间前缀ViewModels。不应该有BaseViewModel命名空间,因为那是一个类,而不是命名空间。 - Sheridan
2
@Sheridan,我在我的应用程序中这样做,但即使我在代码中创建了一个新的ViewModel实例,XAML代码仍会创建另一个实例。有没有办法解决这个问题?我正在尝试切换视图并立即绑定到事件,但由于XAML创建了一个新实例,我无法实现它。 - Cody
2
@AUSTX_RJL,如果您还没有,请就此提出新问题。 - Sheridan
显示剩余14条评论

8
当我第一次开始使用MVVM时,我也为不同的MVVM框架和特别是导航部分而苦恼。因此,我使用了这个小教程,这是Rachel Lim创建的。它非常好,并且解释得很清楚。请点击以下链接查看: 希望它对你有所帮助 :)

3
谢谢,但这不是我的意思。我之前用这个例子来做另外一个应用程序,但对于这个应用程序我没有侧边栏菜单。所以我在视图1上有一个按钮,当我点击它时,它需要切换到视图2。 - user2499088

1
<ContentControl x:Name="K.I.S.S" Content="{Binding ViewModel, Converter={StaticResource ViewLocator}}"/>

1
也许this链接能为你提供帮助。只需将NavigateTo属性设置为你需要在窗口中显示的视图即可。
例如,你可以像这样操作:
<Window x:Class="MainWindowView" 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:meffed="http:\\www.codeplex.com\MEFedMVVM"
                                 meffed:ViewModelLocator.NonSharedViewModel="YourViewModel"
                                 WindowStartupLocation="CenterScreen">

    <Button meffed:NavigationExtensions.NavigateTo="firstview"
                    meffed:NavigationExtensions.NavigationHost="{Binding ElementName=_viewContainer}"
                    meffed:NavigationExtensions.NavigateOnceLoaded="False"
                    Visibility="Visible" />

    <ContentControl x:Name="_viewContainer" Margin="0,0,0,10" />
<Window>

然后,类文件将是:

public partial class MainWindowView : Window
{
    public MainWindowView()
    {           
              InitializeComponent();
    }

        public ContentControl ViewContainer { get { return _viewContainer; } }

    }

然后,您可以将每个视图定义为 UserControl,然后使用我上面提供的链接绑定按钮的 meffed:NavigationExtensions.NavigateTo="secondView"。要针对 WindowContentControl 进行定位,只需使用 RelativeSource 绑定。例如:meffed:NavigationExtensions.NavigationHost="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Window}},Path=ViewContainer}"
在每个视图中,只需查看您是否使用 [NavigationView("firstview")] 注释了代码后端类定义,以此类推。
这对于第一次来说可能比较复杂,但是一旦理解了思路,就会变得非常容易。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接