• WPF之MVVM模式(3)


    有种想写一个MVVM框架的冲动!!!

    1、Model中的属性应不应该支持OnPropertyChanged事件?


    不应该。应该有ViewModel对该属性进行封装,由ViewModel提供OnPropertyChanged事件。 WPF之MVVM(1)中有实例

     

    2、如何将控件事件转换为命令?


    • 在“扩展”中添加“System.Windows.Interractivity”引用
    • xaml中添加xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity命名空间
    • 使用

      <ListBox Name="LbBox" ItemsSource="{Binding Path=SourceCount}">
          <i:Interaction.Triggers>
              <i:EventTrigger EventName="SelectionChanged">
                  <common:ExInvokeCommandAction Command="{Binding Path=SelectionChangedCmd}" CommandParameter="{Binding ElementName=LbBox, Path=SelectedItem}"/>
              </i:EventTrigger>
          </i:Interaction.Triggers>
      </ListBox>

       

    3、View中如何访问ViewModel


     WPF之MVVM(2)中介绍了

     

    4、ViewModel中如何访问View


     WPF之MVVM(2)中介绍了

     

    5、ViewModel之间通信


    • 聚合关系

      public class VM01
      {
          public string Name{get;set;}
      }
      
      public class VM02
      {
          public list<VM01> Property
          {
              get;
              set;
          }
      }

       

    • 组合关系

      public class VM01
      {
          public string Name{get;set;}
      }
      
      public class VM02
      {
          public string Name{get;set;}
      }
      
      public class VM03
      {
          private VM01 _vm01;
          private VM02 _vm02;
          ...
          public VM03(VM01 vm01, VM02 vm02){}
      }

       

    • 依赖关系

    这里主要介绍下依赖关系的ViewModel如何通信

    通过一个非常简单的程序来演示这种实现:点击左边的数字,右边的数字加1。

    左边为LeftViewModel右边为RightViewModel,两个VM是相互独立的,通过事件进行通信。

    1、定义类型来容纳所有需要发送给事件通知接收者的附件信息

    public class NumberChangedEventArgs : EventArgs
    {
        public int Number { get; set; }
    
        public NumberChangedEventArgs(int num)
        {
            Number = num;
        }
    }

     

    2、在LeftViewModel中定义事件成员

    public event EventHandler<NumberChangedEventArgs> NumberChanged;
    
    protected virtual void OnNumberChanged(NumberChangedEventArgs e)
    {
        var handler = NumberChanged;
        if (handler != null) handler(this, e);
    }

     

    3、定义负责引发事件的方法来通知事件的登记对象

    /// <summary>
    /// 选择命令
    /// </summary>
    private DelegateCommand<ExCommandParameter> _selectionChangedCmd;
    
    public DelegateCommand<ExCommandParameter> SelectionChangedCmd
    {
        get
        {
            if (_selectionChangedCmd == null)
            {
                _selectionChangedCmd = new DelegateCommand<ExCommandParameter>(InvokeMouseDown);
            }
    
            return _selectionChangedCmd;
        }
    }
    
    private void InvokeMouseDown(ExCommandParameter param)
    {
        var number = param.Parameter is int ? (int) param.Parameter : 0;
        //触发事件
        OnNumberChanged(new NumberChangedEventArgs(number));
    }

     

    4、定义方法将输入转化为期望事件

    public class RightViewModel : ViewModelBase
    {
        public RightViewModel()
        {
            //订阅事件
            var vm = ViewModelManager.GetByKey("left") as LeftViewModel;
            if (vm != null) vm.NumberChanged += VmOnNumberChanged;
        }
    
    
        private int _number;
    
        public int Number
        {
            get { return _number; }
            set
            {
                _number = value;
                OnPropertyChanged("Number");
            }
        }
    
        /// <summary>
        /// 事件处理
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void VmOnNumberChanged(object sender, NumberChangedEventArgs e)
        {
            Number = e.Number + 1;
        }
    }

     

    问题RightViewModel中如何获取LeftViewModel呢?

    定义一个容器

    public static class ViewModelManager
    {
        private static readonly Dictionary<string, ViewModelBase> Dic = new Dictionary<string, ViewModelBase>();
    
        public static void Add(string key, ViewModelBase value)
        {
            if (Dic.ContainsKey(key)) return;
    
            Dic.Add(key, value);
        }
    
        public static ViewModelBase GetByKey(string key)
        {
            if (!Dic.ContainsKey(key)) return null;
    
            ViewModelBase value;
            Dic.TryGetValue(key, out value);
    
            return value;
        }
    }

     

    在设置View的DataContext时将ViewModel添加到ViewModelManager

    public LeftView()
    {
        InitializeComponent();
    
        var vm = new LeftViewModel();
        ViewModelManager.Add("left", vm);
        this.DataContext = vm;
    }

     

    总结


     回顾上面3篇博文中解决的问题,我们再来看下MvvmLight ToolKit是如何实现MVVM的,这里引用下园友的总结MvvmLight ToolKit 教程

    我们可以猜测MvvmLight作者使用这些组件是为了解决什么问题?

    • ViewModelBase && ObservableObject(INotifyPropertyChanged接口的实现,解决属性改变通知的问题)
    • ViewModelLocator && SimpleIoc(IOC容器,我们的ViewModelManager高级版)
    • RelayCommand(ICommand接口的实现,解决View和ViewModel通信问题)
    • EventToCommand && IEventArgsConverter(Interaction的封装,解决将事件转换为命令的问题)
    • Messenger(解决View和ViewModel以及ViewModel和ViewModel之间通信的问题)
    • DispatcherHelper(博客中未提到)

    这样分析后,我们就知道MvvmLight是如何产生的,以及它能为我们做什么。

    吹牛吹到现在,我都有种自己想写一个MVVM框架的冲动了。你们有没有?

  • 相关阅读:
    Python性能分析指南
    centos加入启动服务
    北京户口
    北京户口
    【汽车进口税】 关于汽车进口税计算你知道多少-汽车保险资讯-阳光车险官方网站
    在北京公司要多长时间才可以申请摇车牌号
    Kivy: Crossplatform Framework for NUI
    说说设计模式~大话目录(Design Pattern)
    说说设计模式~策略模式(Strategy)
    说说设计模式~装饰器模式(Decorator)
  • 原文地址:https://www.cnblogs.com/igotogo/p/5401112.html
Copyright © 2020-2023  润新知