MVVM設計模式教程[一].手動建立MVVM地方法


隨着WPF開發應用的普及.越來越多的人關注MVVM的構建. 這種設計模式能使邏輯與界面很徹底的解耦.為並行開發提供可能,在提高了開發效率的同時,使得程序更容易維護與修改

,而且這種設計模式可以更好的測試更好的分離關注點,模塊與模塊的之間的耦合大幅度降低. 使得測試更加方便.

作為一個WPF 開發人員,很有必要了解MVVM.

MVVM 是由 Model, ViewModel  View 組成. 他之所以能比MVC,MVP耦合度更低,取決於WPF 2個新出的重要概念.命令與數據綁定. 這2個使得完全解耦成為可能.

下面我們將有一個實例 在演示下 用MVVM 創建一個WPF應用實例

    一  首先讓我們打開VisualStudio2010. 新建一個WPF項目.

                                

   實際工作中的MVVM 結構圖.                                       

                             

根據上圖 我們先建立幾個文件夾,為MVVM做准備.

 

二  開始編寫 Model層. 我們在Model層 先添加一個類. Employee 

 代碼如下

View Code
namespace MVVMDemo.Model
{
   public class Employee
    {
        public static Employee CreateEmployee(string name, string age)
        {
            return new Employee() { Name = name, Age = age };
        }
       
        public string Name { get; set; }

        public string Age { get; set; }
    }
}

然后我在 數據層  寫下虛擬數據的加載 創建一個類 . EmployeeRespository 

代碼如下

Respository.cs
using System.Collections.Generic;
using MVVMDemo.Model;

namespace MVVMDemo.DataAccess
{
   public class EmployeeRespository
   {
       readonly List<Employee> _employees;

       public EmployeeRespository()
       {
           if (_employees == null)
           {
               _employees = new List<Employee>();
           }
           _employees.Add(new Employee() { Age = "33", Name = "Tom" });
           _employees.Add(new Employee() { Age = "32", Name = "Gravin" });
           _employees.Add(new Employee() { Age = "28", Name = "Alex" });
       }

       public List<Employee> GetEmployeeRespository()
       {
           return _employees;
       }
   }
}

 三 編寫我們的一個演示界面 View. 我們直接寫在Mainwindow 的xaml里面

MainWindows.xaml
<Window x:Class="MVVMDemo.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:vm="clr-namespace:MVVMDemo.ViewModel"
        Title="MainWindow" Height="Auto" Width="330">
    <Window.DataContext>
        <vm:MainWindowViewModel/>
    </Window.DataContext>
    <StackPanel>
        <ListBox ItemsSource="{Binding AllEmployees}" SelectedIndex="0" SelectedItem="{Binding SelectEmployee}">
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <StackPanel Orientation="Horizontal">
                        <TextBlock Text="{Binding Name}" Margin="5"/>
                        <TextBlock Text="{Binding Age}" Margin="5"/>
                    </StackPanel>
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>
        <StackPanel  Orientation="Horizontal">
            <TextBlock Text="Name" Margin="5,10"/>
            <TextBox Width="100" Margin="5,10" Text="{Binding NewEmployee.Name}"/>
            <TextBlock Text="Age" Margin="5,10"/>
            <TextBox Width="100" Margin="5,10" Text="{Binding NewEmployee.Age}"/>
        </StackPanel>
        <StackPanel  Orientation="Horizontal">
            <Button Content="ADD" Width="100" Margin="10" Command="{Binding AddCommand}"/>
            <Button Content="Remove" Width="100" Margin="10" Command="{Binding RemoveCommand}"/>
        </StackPanel>
      
    </StackPanel>
</Window>

現在界面和數據 Model都好了.. 我們來考慮下這個Demo的邏輯.

首先界面是個ListBox 用於加載 Employee里面的虛擬數據. 然后下面的2個文本框 用於寫入要添加新數據的Name信息與Age信息  最下面的2個按鈕 傳遞操作,添加數據 和 移除數據. 如圖.

 添加數據 要防止數據的重復.  移除數據 是移除選中的一行. 現在我們 用MVVM 來實現這個列子

前面說道 MVVM 之所以能夠 比MVC MVP更好的解耦 ,其原因是WPF 的2大新功能 數據的綁定Data Binding和命令Command  這種機制使得邏輯與界面最大化的解耦

 四 我們開始編寫ViewModel.  實際項目中 通常我們會為ViewModel寫個基類   然后其余的對應View的ViewModel繼承此基類

 ViewModelBase 代碼如下

ViewModelBase.cs
using System.Text;
using System.ComponentModel;

namespace MVVMDemo.ViewModel
{
  public abstract class ViewModelBase:INotifyPropertyChanged,IDisposable
  {
      protected ViewModelBase()
      { 
      
      }

      public event PropertyChangedEventHandler PropertyChanged;

      protected virtual void OnpropertyChanged(string propertyName)
      {
          PropertyChangedEventHandler handler = this.PropertyChanged;
         
          if (handler != null)
          {
              var e = new PropertyChangedEventArgs(propertyName);
              
              handler(this, e);
          }
      }


      public void Dispose()
      {
          this.OnDispose();
      }

      protected virtual void OnDispose()
      {
          throw new NotImplementedException();
      }


  }

}

ViewModel 其中一個非常常用的功能就 對屬性的變化能夠即使反映到UI View.  這需要 就是要繼承 一個接口 INotifyPropertyChanged  

 隨后就是命令, 我也同樣編寫一個命令的基類 

CommandBase
using System;
using System.Windows.Input;

namespace MVVMDemo.Command
{
   public class CommandBase:ICommand
    {

        #region ICommand 成員

        public bool CanExecute(object parameter)
        {
           return _canExecute==null? true:_canExecute(parameter);
        }

        public event EventHandler CanExecuteChanged
        {
            add 
            { 
                CommandManager.RequerySuggested += value; 
            
            }
            remove
            {
                CommandManager.RequerySuggested -= value;
            }
        }

        public void Execute(object parameter)
        {
            _execute(parameter);
        }

        #endregion

        readonly Action<object> _execute;

        readonly Predicate<object> _canExecute; 

        public CommandBase(Action<object> execute):this(execute,null)
        {

        }

        public CommandBase(Action<object> execute, Predicate<object> canExecute)
        {
            if (execute == null)
                throw new ArgumentNullException("execute");

            _execute = execute;
            _canExecute = canExecute;

        }

    }
}

命令有2個很重要的方法 一個執行Execute 要做操作的內容 一個CanExecute在操作前 是否能執行改命令 
這和好的用到了我們這個Demo的 業務邏輯, 比如Add加入數據 我們很容易想到 Execute中寫具體加入數據的操作, 但是要避免錄入的數據與原來數據源的數據重復

所以我們在條件 CanExecute中做出判斷看是否能Add,

MainWindowViewModel代碼如下

MainWindowViewModel.cs
using MVVMDemo.Model;
using System.Collections.ObjectModel;
using MVVMDemo.DataAccess;
using System.Windows.Input;
using MVVMDemo.Command;

namespace MVVMDemo.ViewModel
{
   public class MainWindowViewModel:ViewModelBase
   {
       private ICommand _removeCommand;

       private Employee _newEmployee;
       
       public ObservableCollection<Employee> AllEmployees { get; set; }

       public Employee SelectEmployee{get;set;}

       public Employee NewEmployee { get { return _newEmployee; } }

       public ICommand RemoveCommand
       {
           get { return _removeCommand = new CommandBase(RemoveExecute,RemoveCanExecute);}
       
       }
       public ICommand AddCommand
       {
           get { return _removeCommand = new CommandBase(AddExecute, AddCanExecute); }

       }
    
       public MainWindowViewModel()
       {
           if (AllEmployees == null)
           {
               AllEmployees = new ObservableCollection<Employee>(new EmployeeRespository().GetEmployeeRespository()); 
           }

           _newEmployee = new Employee();
       }

       void RemoveExecute(object param)
       {
           AllEmployees.Remove(SelectEmployee);
       } 

      bool RemoveCanExecute(object param)
      {
          if(SelectEmployee!=null)
          {
             return true;
          }
          
          return false;
      }

      void AddExecute(object param)
      {
          AllEmployees.Add(NewEmployee);
      }

      bool AddCanExecute(object param)
      {
          if (NewEmployee.Name != null && NewEmployee.Age != null)
          {
              foreach (var item in AllEmployees)
              {

                  if (item.Name.Trim() == NewEmployee.Name.Trim())
                  {
                      return false;
                  }
              }
          }
          else
          {
              return false;
          }
          return true;
      }   
   }
}

這樣這個View'所有的邏輯都在這個文件里面了.  我們看看MainWindow中的UI元素. 現在可以很清楚感覺到MVVM的解耦. UI元素可以隨時變換,在本質的業務邏輯不改變的情況下我們的 ViewModel是不需要更改的,這樣設計師和 工程師 不但可以並行開發而且 即使面對UI的變動 邏輯基本不需要修改什么.

我們看看View  只要把ListBox 的itemsource綁定 MainWindowViewModel的 數據源屬性. 其他依次綁定 里面的屬性 指定好數據源

 DataContext.數據就能正確的Load出來. 按鈕綁定ViewModel中的Command屬性  當填寫的新數據信息不復合要求的時候 Button就會處於 Enabel狀態. 非常方便

當我們把View UI(比如ListBox,Button,textbox)的內容隨意刪除  編譯一樣不會報錯 因為邏輯中MainWindowViewModel並沒有任何UI元素. 邏輯與界面 解耦  就是這個意思,而其他的設計模式多少會不可避免的與UI 糾纏在一起,UI一變化化 邏輯勢必變化.這一根本原因是

WPF的MVVM模式是數據驅動UI的理念. 數據變化呈現在UI上, 而MVC MVP 以及以往的各種模式 都是事件驅動UI  事件里必定有UI元素 

 當然 這只是個小列子 僅僅只是演示下MVVM的意思.


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM