zoukankan      html  css  js  c++  java
  • MVVM开发模式简单实例MVVM Demo

    本文主要是翻译Rachel Lim的一篇有关MVVM模式介绍的博文 A Simple MVVM Example

    并具体给出了一个简单的Demo(原文是以WPF开发的,对于我自己添加或修改的一部分会用红色标注)  

    现在开始:

    在我看来,如果你使用的是WPF或Sliverlight来开发程序就应该使用MVVM设计模式。它是你的代码清晰明了并易于维护。

    可问题是网上有很多有关MVVM模式的资源都有自己强大的实现方式。这里我将介绍最基础的MVVM设计模式的实现方法。

    MVVM  (是Model-View-ViewModel的缩写)

    Model: 保存数据的简单类对象,它只能包含属性和属性验证(应该就是验证属性值是否正确),而不负责存储数据、事件点击、复杂运算、业务规则和其他操作。

    View: 呈现给用户的数据界面,很多情况下,他是以数据模板(DataTemplates)的方式告诉应用如何呈现类中内容的。

          如果代码内容只跟View有关(比如社交焦点和执行动画),可以将代码写在View的后台。

    ViewModel:用来处理逻辑。你的后台代码(数据访问、点击事件、复杂运算、业务规则验证等)都写在这里。这里面的代码View的反应。

    比如,View中有一个ListBox对象、选中的对象、保存按钮。ViewModel中就要包含ObservableCollection<Model>集合、

    Mode类型的SelectedObject和命令ICommand SaveCommand.

    下面就通过一个简单的例子看看这三者之间是如何相互联系的。你会发现除了属性和方法名,任意一者是不需要访问另外两者的。

    一旦接口被定义了,每一层可以完全独立于其他运行。

    此例中,我使用的是Product Model,这个类中只含有属性和属性更改通知(INotifyPropertyChanged)

    1.Model

    public class ProductModel : ObservableObject
        {
            //字段
            private int _productId;
            private string _productName;
            private decimal _unitPrice;
    
            //属性
            public int ProductId
            {
                get { return _productId; }
                set
                {
                    SetProperty(ref this._productId, value);
                }
            }
    
            public string ProductName
            {
                get { return _productName; }
                set
                {
                    SetProperty(ref this._productName, value);
                }
            }
    
            public decimal UnitPrice
            {
                get { return _unitPrice; }
                set
                {
                    SetProperty(ref this._unitPrice, value);
                }
            }
    
            public ProductModel()  //这里的构造函数只是为了后面TextBlock能够以此方法绑定显示:Text="{Binding CurrentProduct.ProductId}"
            {
                this.ProductName = "Lumia 930";
                this.ProductId = 123;
                this.UnitPrice = 2799;
            }
        }

    ProductModel继承了ObservableObject类,而ObservableObject实现了INotifyPropertyChanged接口

    ObservableObject和原文不一样,只是简化了一下,具体可以查看 INotifyPropertyChanged接口的实现,介绍的很详细

    public class ObservableObject: INotifyPropertyChanged
        {
            public event PropertyChangedEventHandler PropertyChanged;
    
            public bool SetProperty<T>(ref T storage, T value, [CallerMemberName]string propertyName = null)
            {
                if (object.Equals(storage, value)) return false;
                storage = value;
                this.OnPropertyChanged(propertyName);
                return true;
            }
    
            protected void OnPropertyChanged([CallerMemberName]string propertyName = null)
            {
                var eventHandler = this.PropertyChanged;
                if (null != eventHandler)
                {
                    eventHandler(this, new PropertyChangedEventArgs(propertyName));
                }
            }
        }

    注意属性更改通知(INotifyPropertyChanged):当Model中属性更改时,会通知View实时的更新View页面。

    有人建议将这个放入ViewModel而不是Model中。虽然两种方式都是有效的,但是我发现放入ViewModel更加复杂,

    还需要更多的代码。而放入Model中更简单些。[的确,很多例子都是有ViewModel来继承的...不知道为什么这里要Model继承。。有更加复杂么?]

    2.ViewModel
    由于在创建View之前需要ViewModel,接下来我们就来创建ViewModel。它要包括用户操作所有的交互。

    现在这里包括4个属性:CurrentProduct当前产品, 产品获取命令GetProductCommand,保存命令SaveProductCommand.用来查找某个产品的ProductId

    public class ProductViewModel : ObservableObject
        {
        private int _productId;       private ProductModel _currentProduct;     private ICommand _getProductCommand;     private ICommand _saveProductCommand;
        
        public
    int ProductId     {       get { return _productId; }       set       {         SetProperty(ref this._productId, value);       }     }     public ProductViewModel()
        {
                CurrentProduct = new ProductModel();
        }
    public ProductModel CurrentProduct { get { return _currentProduct; } set { if (null == _currentProduct) _currentProduct = new ProductModel(); _currentProduct = value; } }
    public ICommand SaveProductCommand { get { if (_saveProductCommand == null) { _saveProductCommand = new RelayCommand(SaveProduct);  //实例化构造时和原文参数不一样 } return _saveProductCommand; } } public ICommand GetProductCommand { get { if (_getProductCommand == null) _getProductCommand = new RelayCommand(GetProduct, IsEnable);  //实例化构造时和原文参数不一样 return _getProductCommand; } }
    public bool IsEnable()  //此控件是否可点击 { return true; } public void SaveProduct()  //执行命令 { await new MessageDialog("保存").ShowAsync(); }

         public void GetProduct(object parameter)  
            {
                if (parameter.ToString() == string.Empty)  //多做了一个判断
                    return;
                ProductId = int.Parse(parameter.ToString());
                ProductModel product = new ProductModel();
                product.ProductName = "Test Product";
                product.ProductId = ProductId;
                product.UnitPrice = 10.00M;
                CurrentProduct = product;
            }
    }

    这里出现了一个新类RelayCommand,MVVM的正常使用必不可少。这个命令表示的是由其他类调用委托来实现此类中的代码

    [在建立非空项目的时候Command文件夹会自动生成此类,但是本身只定义了不带参数的方法,需要进行扩展---注释部分]

    public class RelayCommand : ICommand
        {
            private readonly Action _execute;
            private readonly Func<bool> _canExecute;
            private readonly Action<object> _executeParam;  //新增了一个带参数有返回值的方法
    
            public event EventHandler CanExecuteChanged;
    
            public RelayCommand(Action execute)
                : this(execute, null)
            {
            }

    public RelayCommand(Action execute, Func<bool> canExecute) { if (execute == null) throw new ArgumentNullException("execute"); _execute = execute; _canExecute = canExecute; } public RelayCommand(Action<object> executeParam)  //新增重载构造函数 { if(executeParam == null) throw new ArgumentNullException("executeParam"); _executeParam = executeParam; _canExecute = () => true; } public RelayCommand(Action<object> executeParam, Func<bool> canExecute)  //新增重载构造函数 { if (executeParam == null) throw new ArgumentNullException("executeParam"); _executeParam = executeParam; _canExecute = canExecute; } public bool CanExecute(object parameter) { return _canExecute == null ? true : _canExecute(); } public void Execute(object parameter)  //新增判断 { if (parameter == null) _execute(); else _executeParam(parameter); } public void RaiseCanExecuteChanged() { var handler = CanExecuteChanged; if (handler != null) { handler(this, EventArgs.Empty); } } }

    3.View(可以认为是MainPage.xaml    和原文不一样

    <StackPanel>
      <StackPanel x:Name="stackpaenl1">
        <TextBlock Text="{Binding CurrentProduct.ProductId}" Foreground="Yellow"/>
        <TextBlock Text="{Binding CurrentProduct.ProductName}"/>
        <TextBlock Text="{Binding CurrentProduct.UnitPrice}"/>      

        <TextBlock Text="Enter Product Id"/>
        <TextBox x:Name="Input"/>

        <TextBlock Text="{Binding ProductId}" Foreground="Yellow"/>     <Button Content="Get Product" Command="{Binding GetProductCommand}" CommandParameter="{Binding ElementName=Input, Path=Text}"/>     <Button Content="Save Product" Command="{Binding SaveProductCommand}"/>   </StackPanel> </StackPanel>

    最后在View.cs添加代码

      ProductViewModel product = new ProductViewModel();
      product.ProductId = 111;
      this.DataContext = product;  //绑定错了的话   COMMAND是不起作用的

    以上就是简单的MVVM程序。

    PS:下篇文章会接着本文    

    1.将添加Product集合,绑定到列表     

    2.给点击ListBox的添加选项改变时的事件(要附加依赖属性,和Button点击事件不同)

    3.通过自定义类以JSON获取保存数据到存储空间

  • 相关阅读:
    转:Web Service介绍
    CodeIgniter网页缓存
    CodeIgniter管理你的应用程序
    CodeIgniter自动装载资源
    python feedparser 使用
    HTC文件的使用
    动态执行javascript代码
    jQuery获取文本节点之text()/val()/html() 方法区别
    style,runtimeStyle与currentStyle的定义及用法
    File,FileStream,byte[]3者互相转换总结(转)
  • 原文地址:https://www.cnblogs.com/yffswyf/p/4211621.html
Copyright © 2011-2022 走看看