WPF MVVM架構

  • 11472
  • 0
  • WPF
  • 2018-09-06

WPF MVVM架構 

談到WPF ,最多人提到就是用MVVM這種架構去寫軟體。

雖然也可以用傳統WindowForm的那種事件方式去做控制項的相關操作

但是MVVM模式更貼近WPF的設計

MVVM是由Model  ViewModel View三個部分組成

Model 通常是放不加處理的類別,像是poco類別。

public class 產品Model
{
    public string 品號{get;set;}
    public string 單位{get;set;}
}

ViewModel則是用來做顯示介面View的Binding

 public class ViewModelBase : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
        protected void OnPropertyChanged([CallerMemberName] string propertyname = null)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyname));
            }
        }
    }
public class  產品ViewModel  : ViewModelBase
    {
       private 產品Model;
       public 產品ViewModel()
       {
          產品Model= new 產品Model();
       }
       public string 品號
       {
         get { return 產品Model.品號;}
         set { 產品Model.品號=value; OnPropertyChanged(); }
       }
       public string 單位
       {
         get { return 產品Model.單位;}
         set { 產品Model.單位=value; OnPropertyChanged(); }
       }


    }

我在寫的時候會用一個母類別ViewModelBase讓所有ViewModel繼承,這樣可以省略上方OnPropertyChanged的程式碼

OnPropertyChanged的功能是,在程式中,可以完全不用使用到前方的控件,只需要專注的將商業邏輯寫在ViewModel中

透過改變ViewModel中的屬性,會呼叫到OnPropertyChanged,讓前方的View知道,後方的ViewModel的值已經改變了

需要更新顯示的資料。

而不僅僅是這樣,像是之後的Command的Binding,若是有更改都需要透過OnPropertyChanged去做通知的動作。

也就是說,當你採用MVVM模式時,若是更改了ViewModel中的值

而View沒有變化,那就是沒有去呼叫OnPropertyChanged

或是Binding失敗。

程式碼中的[CallerMemberName] 忘記是多少以後版本才有

這個特性的用法是,會自動以呼叫這個方法的屬性當參數傳入方法中

WPF中的特色,透過Binding跟Command可以有效的將前端的View跟ViewModel做有效的連接,乾淨的切割。

這樣設計界面的可以透過VisualStudio中的資料來源,用ViewModel 快速產生出前端的View

只要不去調整到Binding的連結,就可以隨意地變更前端控件的樣式。

 

前方的產品View ,XAML大概會長這樣,最重要的是要在程式碼中設定DataContext或是在Xaml中設定

<Page x:Name="產品View">

  <Grid>
            <Grid.RowDefinitions>
                <RowDefinition></RowDefinition>
                <RowDefinition></RowDefinition>
            </Grid.RowDefinitions>
            <Grid.ColumnDefinitions>
                <ColumnDefinition></ColumnDefinition>
                <ColumnDefinition></ColumnDefinition>
            </Grid.ColumnDefinitions>
            <TextBlock Text="品號" Grid.Row="0" Grid.Column="0"></TextBlock>
            <TextBox Text="{Binding 品號}" Grid.Row="0" Grid.Column="1"></TextBox>
            <TextBlock Text="單位" Grid.Row="1" Grid.Column="0"></TextBlock>
            <TextBox Text="{Binding 單位}" Grid.Row="1" Grid.Column="1"></TextBox>
        </Grid>
</Page>
 public partial class 產品表 : Page
    {
        產品ViewModel viewModel;
        public 產品表()
        {
            InitializeComponent();
            viewModel = new 產品ViewModel;
            this.DataContext = viewModel;
        }

     
    }

整個架構就是這樣,在XAML中使用Binding跟ViewModel中的屬性名稱做連結,在程式碼中設定DataContext,就可以達到後方跟前方的分離。

若是要顯示預設值,可以在ViewModel中的建構式直接設定屬性,程式運行時,一開始顯示的就是建構式中設定的值。

以上的介紹是用單一產品。

若是要用DataGrid 或List,做一個主從式的顯示。

可以新增一個View,ViewModel跟View的關係是很緊密的,View要怎麼顯示,那對應的ViewModel就要有對應的屬性。

 public class 產品表主從式ViewModel : ViewModelBase
    {       
        public 產品表主從式ViewModel()
        {
         //設定屬性預設值,或從其他資料來源取的資料
        }
        public 產品主檔ViewModel 產品主檔
        {
            get
            {
                return _產品主檔;
            }
            set
            {
                _產品主檔 = value;
                OnPropertyChanged();
            }
        }
        private 產品主檔ViewModel _產品主檔;
        public ObservableCollection<產品表ViewModel> 明細
        {
            get
            {
                return _明細;
            }
            set
            {
                _明細 = value;
                OnPropertyChanged();
            }
        }
        private ObservableCollection<產品表ViewModel> 明細
}

上方的程式碼,是做一個簡單的主從式ViewModel,包含兩個原先的ViewModel。

這邊要注意的是 ObservableCollection 泛型的類別裡的屬性一定要有呼叫OnPropertyChanged,不然改變值的時候,不會顯示在View上。

有任何改進的意見及問題歡迎傳送到電子郵件

電子郵件:momo16542@gmail.com