WPF使用MVVM模式浏览视图

我正在使用MVVM模式构build我的第一个WPF。 在这个社区的帮助下,我设法创build了我的模型,我的第一个ViewModel和视图。 现在我想为应用程序devise基本的应用程序布局界面增加一些复杂性。 我的想法是至less有两个子视图和一个主视图,并将它们分开放在几个XAML上:

  • Main.XAML
  • Products.XAML
  • Clients.XAML

Main将有一个菜单和一个空间来加载子视图(产品和客户端)。 现在,遵循MVVM模式,视图之间的所有导航逻辑应该被写在ViewModel上。 所以mi想法是有4个ViewModels:

  • MainViewModel
  • ProductsViewModel
  • ClientsViewModel
  • NavigationViewModel

那么NavigationViewModel应该包含一系列的子视图模型? 一个活跃的视图模型是可行的?

所以我的问题是:

1)如何使用MVVM模式在主视图中加载不同的视图(产品,客户端)?

2)我如何实现导航视图模型

3)如何控制打开或活动视图的最大数量?

4)如何在打开的视图之间切换?

我一直在做大量的search和阅读,找不到任何简单的MVVM导航WPF导航,在主视图中加载多个视图的例子。 其中许多是:

1)使用外部工具包,我现在不想使用。

2)将创build所有视图的所有代码放在一个单独的XAML文件中,这似乎不是一个好主意,因为我需要实现近80个视图!

我在这里的道路上? 任何帮助,特别是一些代码将被折衷。 谢谢!

UPDATE

所以,我build立了一个@LordTakkera的build议,但卡住了testing项目。 这是我的解决scheme的样子: 解

我创造:

  • 两种模式(客户和产品)

  • 一个MainWindow和两个wpf用户控件(客户和产品)XAML。

  • 三个ViewModel(客户,产品和主ViewModel)

然后我把每个视图上的dataContext设置为相应的viewModel。 之后,我使用ContentPresenter创buildMainWindow,并将其绑定到viewmodel的属性。

MainWindow.XAML

<Window x:Class="PruevaMVVMNavNew.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="MainWindow" Height="519" Width="890"> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition Width="150"/> <ColumnDefinition Width="*"/> </Grid.ColumnDefinitions> <Grid.RowDefinitions> <RowDefinition Height="80"/> <RowDefinition Height="*"/> <RowDefinition Height="20"/> </Grid.RowDefinitions> <Border Grid.Column="0" Grid.ColumnSpan="2" Background="AntiqueWhite" ></Border> <Border Grid.Row="1" Grid.RowSpan="2" Background="AliceBlue"></Border> <Border Grid.Row="1" Grid.Column="1" Background="CadetBlue"></Border> <ContentPresenter Grid.Row="1" Grid.Column="1" x:Name="ContentArea" Content="{Binding CurrentView}"/> <StackPanel Margin="5" Grid.Column="0" Grid.Row="1"> <Button>Clients</Button> <Button>Products</Button> </StackPanel> </Grid> 

而这也是来自MainWindow的视图模型:

 class Main_ViewModel : BaseViewModel { public Main_ViewModel() { CurrentView = new Clients(); } private UserControl _currentView; public UserControl CurrentView { get { return _currentView; } set { if (value != _currentView) { _currentView = value; OnPropertyChanged("CurrentView"); } } } } 

所以这个默认的客户端加载视图,看起来像这样(这是正确的!):

当前状态

所以我想我需要一种方法来将左边的button与某个viemodel相关联,然后将它们与Main viewModel的CurrentView属性绑定。 我怎样才能做到这一点? 谢谢!

UPDATE2

根据@LordTakkera的build议我修改我的主viewModel这种方式:

 class Main_ViewModel : BaseViewModel { public ICommand SwitchViewsCommand { get; private set; } public Main_ViewModel() { //CurrentView = new Clients(); SwitchViewsCommand = new RelayCommand((parameter) => CurrentView = (UserControl)Activator.CreateInstance(parameter as Type)); } private UserControl _currentView; public UserControl CurrentView { get { return _currentView; } set { if (value != _currentView) { _currentView = value; OnPropertyChanged("CurrentView"); } } } } 

我使用RelayCommand而不是DelegateCommand,但我认为它以同样的方式工作。 当我点击button和types参数string它的确定,但我得到这个错误时执行该命令:

错误

翻译: 值不能为空。 参数名称:types。 build议使用New关键字来创build对象实例我不知道把New关键字放在哪里。 我已经尝试CommandParameter,但它不会工作。 任何想法? 谢谢

更新3

当所有的build议和帮助在这里接受和大量的工作,这里是我最后的导航菜单和我的应用程序接口的基础。

捕获1捕获2

感谢所有的帮助。

我不确定你需要一个单独的“导航”视图模型,你可以很容易地把它放在主要。 无论哪种方式:

要分离你的“孩子”的意见,我会在你的“主”视图上使用一个简单的ContentPresenter:

 <ContentPresenter Content="{Binding CurrentView}"/> 

最简单的方法来实现支持属性是使其成为一个UserControl ,但有人会认为这样做违反了MVVM(因为ViewModel现在依赖于“视图”类)。 你可以使它成为一个对象,但你失去了一些types的安全。 在这种情况下,每个视图都是一个UserControl。

要切换它们,你将需要某种select控制。 我之前用单选button做了这个,你把它们绑定成这样:

 <RadioButton Content="View 1" IsChecked="{Binding Path=CurrentView, Converter={StaticResource InstanceEqualsConverter}, ConverterParameter={x:Type views:View1}"/> 

转换器是非常简单的,在“转换”它只是检查当前控件是否是参数types,在“ConvertBack”它返回一个新的参数实例。

 public class InstanceEqualsConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { return (parameter as Type).IsInstanceOfType(value); } public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { return (bool)value ? Activator.CreateInstance(parameter as Type) : Binding.DoNothing; } } 

绑定到combobox或其他select控件将遵循类似的模式。

当然你也可以使用DataTemplates(带有一个select器,不幸的是我之前没做过),并使用合并字典(允许单独的XAML)将它们加载到资源中。 我个人更喜欢用户控制路线,挑哪个最适合你!

这种方法是“一次一个观点”。 转换为多个视图相对容易(您的UserControl成为用户控件的集合,在转换器中使用.Contains等)。

要做到这一点与button,我会使用命令,并利用CommandParameter。

buttonXAML将如下所示:

 <Button ... Command={Binding SwitchViewsCommand} CommandParameter={x:Type local:ClientsView}/> 

然后你有一个委托命令(教程在这里 ),从转换器运行激活码:

 public ICommand SwitchViewsCommand {get; private set;} public MainViewModel() { SwitchViewsCommand = new DelegateCommand((parameter) => CurrentView = Activator.CreateInstance(parameter as Type)); } 

这是我的头顶,但应该是非常接近。 让我知道事情的后续!

让我知道,如果我提供任何进一步的信息!

更新:

回答您的疑虑:

  1. 是的,每次按下button,都会创build一个新的视图实例。 你可以通过持有预先创build了视图和索引的Dictionary<Type, UserControl>来轻松解决这个问题。 对于这个问题,你可以使用Dictonary<String, UserControl>并使用简单的string作为转换参数。 缺点是你的ViewModel变得紧密耦合到它可以呈现的种类(因为它必须填充所述字典)。

  2. 只要没有其他人持有对它的引用(认为它注册的事件处理程序),该类就应该被丢弃。

  3. 正如你所指出的,一次只创build一个视图,所以你不需要担心内存。 当然,你是在调用一个构造函数,但这并不是那么昂贵,特别是在现代计算机上,我们往往有足够的CPU时间来备用。 与往常一样,性能问题的答案是“基准testing”,因为只有您有权访问预期的部署目标和整个源代码,才能看到实际执行的最佳效果。

恕我直言,最好的select是使用MVVM框架(PRISM,MMVM Light,Chinch等),因为导航已经实现。 如果你想创build自己的导航 – 尝试DataTemplate。