📅  最后修改于: 2020-11-19 05:24:52             🧑  作者: Mango
构建MVVM应用程序时,通常将复杂的信息屏幕分解为一组父视图和子视图,其中子视图包含在面板或容器控件的父视图内,并形成使用层次。
分解复杂的视图后,这并不意味着您分离为自己的XAML文件的每个子内容都必须是MVVM视图。
内容块仅提供将某些内容呈现到屏幕的结构,不支持用户对该内容的任何输入或操作。
它可能不需要单独的ViewModel,但可能只是基于父级ViewModel公开的属性呈现的大块XAML。
最后,如果您具有Views和ViewModels的层次结构,则父ViewModel可以成为通信的中心,以便每个子ViewModel都可以与其他子ViewModel以及与其父级尽可能分离。
让我们看一个示例,其中我们将在不同视图之间定义一个简单的层次结构。创建一个新的WPF应用程序项目MVVMHierarchiesDemo
步骤1-将三个文件夹(Model,ViewModel和Views)添加到您的项目中。
步骤2-在Model文件夹中添加Customer和Order类,在Views文件夹中添加CustomerListView和OrderView,在ViewModel文件夹中添加CustomerListViewModel和OrderViewModel,如下图所示。
步骤3-在CustomerListView和OrderView中添加文本块。这是CustomerListView.xaml文件。
以下是OrderView.xaml文件。
现在我们需要一些东西来承载这些视图,并在MainWindow中为其提供一个合适的位置,因为它是一个简单的应用程序。我们需要一个容器控件,以便我们可以放置视图并以导航方式对其进行切换。为此,我们需要在MainWindow.xaml文件中添加ContentControl,我们将使用其content属性并将其绑定到ViewModel引用。
现在,在资源字典中为每个视图定义数据模板。以下是MainWindow.xaml文件。请注意,每个数据模板如何将数据类型(ViewModel类型)映射到对应的View。
每当当前视图模型设置为CustomerListViewModel的实例时,它将渲染出与ViewModel连接的CustomerListView。它是一个订单ViewModel,它将呈现出OrderView等。
现在,我们需要一个具有CurrentViewModel属性的ViewModel,并具有一些逻辑和命令,以便能够在该属性内切换ViewModel的当前引用。
让我们为此MainWindow创建一个名为MainWindowViewModel的ViewModel。我们可以仅通过XAML创建ViewModel的实例,然后使用该实例来设置窗口的DataContext属性。为此,我们需要创建一个基类来封装ViewModel的INotifyPropertyChanged的实现。
该类的主要思想是封装INotifyPropertyChanged实现,并为派生类提供帮助方法,以便它们可以轻松触发适当的通知。以下是BindableBase类的实现。
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
namespace MVVMHierarchiesDemo {
class BindableBase : INotifyPropertyChanged {
protected virtual void SetProperty(ref T member, T val,
[CallerMemberName] string propertyName = null) {
if (object.Equals(member, val)) return;
member = val;
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
protected virtual void OnPropertyChanged(string propertyName) {
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
public event PropertyChangedEventHandler PropertyChanged = delegate { };
}
}
现在是时候开始使用CurrentViewModel属性开始一些视图切换了。我们只需要一些方法来驱动此属性的设置。而且我们要做到这一点,以便最终用户可以命令转到客户列表或订单视图。首先在您的项目中添加一个新类,该类将实现ICommand接口。以下是ICommand接口的实现。
using System;
using System.Windows.Input;
namespace MVVMHierarchiesDemo {
public class MyICommand : ICommand {
Action _TargetExecuteMethod;
Func _TargetCanExecuteMethod;
public MyICommand(Action executeMethod) {
_TargetExecuteMethod = executeMethod;
}
public MyICommand(Action executeMethod, Func canExecuteMethod) {
_TargetExecuteMethod = executeMethod;
_TargetCanExecuteMethod = canExecuteMethod;
}
public void RaiseCanExecuteChanged() {
CanExecuteChanged(this, EventArgs.Empty);
}
#region ICommand Members
bool ICommand.CanExecute(object parameter) {
if (_TargetCanExecuteMethod != null) {
T tparm = (T)parameter;
return _TargetCanExecuteMethod(tparm);
}
if (_TargetExecuteMethod != null) {
return true;
}
return false;
}
// Beware - should use weak references if command instance lifetime is
longer than lifetime of UI objects that get hooked up to command
// Prism commands solve this in their implementation
public event EventHandler CanExecuteChanged = delegate { };
void ICommand.Execute(object parameter) {
if (_TargetExecuteMethod != null) {
_TargetExecuteMethod((T)parameter);
}
}
#endregion
}
}
现在,我们需要为这些视图设置一些顶级导航到ViewModel,并且该切换的逻辑应属于MainWindowViewModel。为此,我们将使用在Navigation上调用的方法,该方法采用字符串目标并返回CurrentViewModel属性。
private void OnNav(string destination) {
switch (destination) {
case "orders":
CurrentViewModel = orderViewModelModel;
break;
case "customers":
default:
CurrentViewModel = custListViewModel;
break;
}
}
为了浏览这些不同的视图,我们需要在MainWindow.xaml文件中添加两个按钮。以下是完整的XAML文件实现。
以下是完整的MainWindowViewModel实现。
using MVVMHierarchiesDemo.ViewModel;
using MVVMHierarchiesDemo.Views;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MVVMHierarchiesDemo {
class MainWindowViewModel : BindableBase {
public MainWindowViewModel() {
NavCommand = new MyICommand(OnNav);
}
private CustomerListViewModel custListViewModel = new CustomerListViewModel();
private OrderViewModel orderViewModelModel = new OrderViewModel();
private BindableBase _CurrentViewModel;
public BindableBase CurrentViewModel {
get {return _CurrentViewModel;}
set {SetProperty(ref _CurrentViewModel, value);}
}
public MyICommand NavCommand { get; private set; }
private void OnNav(string destination) {
switch (destination) {
case "orders":
CurrentViewModel = orderViewModelModel;
break;
case "customers":
default:
CurrentViewModel = custListViewModel;
break;
}
}
}
}
从BindableBase类派生所有ViewModel。编译并执行上述代码后,您将看到以下输出。
如您所见,我们在MainWindow上仅添加了两个按钮和一个CurrentViewModel。如果单击任何按钮,它将导航到该特定视图。让我们单击“客户”按钮,您将看到显示了CustomerListView。
我们建议您逐步执行上述示例,以更好地理解。