MVVM開發模式簡單實例MVVM Demo

本文主要是翻譯Rachel Lim的一篇有關MVVM模式介紹的博文 A Simple MVVM Example設計模式

並具體給出了一個簡單的Demo(原文是以WPF開發的,對於我本身添加或修改的一部分會用紅色標註)  mvvm

如今開始:wordpress

在我看來,若是你使用的是WPF或Sliverlight來開發程序就應該使用MVVM設計模式。它是你的代碼清晰明瞭並易於維護。函數

可問題是網上有不少有關MVVM模式的資源都有本身強大的實現方式。這裏我將介紹最基礎的MVVM設計模式的實現方法。動畫

MVVM  (是Model-View-ViewModel的縮寫)this

Model: 保存數據的簡單類對象,它只能包含屬性和屬性驗證(應該就是驗證屬性值是否正確),而不負責存儲數據、事件點擊、複雜運算、業務規則和其餘操做。spa

View: 呈現給用戶的數據界面,不少狀況下,他是以數據模板(DataTemplates)的方式告訴應用如何呈現類中內容的。翻譯

      若是代碼內容只跟View有關(好比社交焦點和執行動畫),能夠將代碼寫在View的後臺。設計

ViewModel:用來處理邏輯。你的後臺代碼(數據訪問、點擊事件、複雜運算、業務規則驗證等)都寫在這裏。這裏面的代碼View的反應。code

好比,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獲取保存數據到存儲空間

相關文章
相關標籤/搜索