📜  MVVM –层次结构和导航

📅  最后修改于: 2020-11-19 05:24:52             🧑  作者: Mango


构建MVVM应用程序时,通常将复杂的信息屏幕分解为一组父视图和子视图,其中子视图包含在面板或容器控件的父视图内,并形成使用层次。

  • 分解复杂的视图后,这并不意味着您分离为自己的XAML文件的每个子内容都必须是MVVM视图。

  • 内容块仅提供将某些内容呈现到屏幕的结构,不支持用户对该内容的任何输入或操作。

  • 它可能不需要单独的ViewModel,但可能只是基于父级ViewModel公开的属性呈现的大块XAML。

  • 最后,如果您具有Views和ViewModels的层次结构,则父ViewModel可以成为通信的中心,以便每个子ViewModel都可以与其他子ViewModel以及与其父级尽可能分离。

MVVM层次结构和导航

让我们看一个示例,其中我们将在不同视图之间定义一个简单的层次结构。创建一个新的WPF应用程序项目MVVMHierarchiesDemo

WPF应用

步骤1-将三个文件夹(Model,ViewModel和Views)添加到您的项目中。

WPF申请步骤1

步骤2-在Model文件夹中添加Customer和Order类,在Views文件夹中添加CustomerListView和OrderView,在ViewModel文件夹中添加CustomerListViewModel和OrderViewModel,如下图所示。

WPF申请步骤2

步骤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。编译并执行上述代码后,您将看到以下输出。

层次结构和导航MainWindow1

如您所见,我们在MainWindow上仅添加了两个按钮和一个CurrentViewModel。如果单击任何按钮,它将导航到该特定视图。让我们单击“客户”按钮,您将看到显示了CustomerListView。

层次结构和导航MainWindow2

我们建议您逐步执行上述示例,以更好地理解。