[WPF] MVVM 軟體架構模式 - 從基礎元件 ViewModelBase 到完成一個基本的範例

從基礎元件 ViewModelBase 到完成一個基本的範例

實施 MVVM(Model-View-VeiwModel) 設計模式一般第一步就在程式解決方案中新增一個類別 ViewModelBase

聽名字就知道之後的 XXXXViewModel 都會是繼承這個類別的!

ViewModelBase 的重點是實作 INotifyPropertyChanged 介面,作為響應 Property 有變化時的對應處理

基本就是通知 UI(View) 上跟這個 Property 綁定的元件跟著變化,或是通知數據處理模組(Model)跟著做對應的處理

以下是我會用的 ViewModelBase 例子,MVVM_Sample 要改成自己的命名空間啊!(要沿用這個命名空間也行啦 XD)

//using System;
//using System.Diagnostics;
using System.ComponentModel;
using System.Runtime.CompilerServices;

namespace MVVM_Sample.ViewModel
{
    public abstract class ViewModelBase : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        public void SetValue<T>(ref T property, T value, [CallerMemberName] string propertyName = null)
        {
            if (property != null)
            {
                if (property.Equals(value))
                {
                    return;
                }
            }

            property = value;
            OnPropertyChanged(propertyName);
        }

        protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        #region -- Debugging Aides --
        //protected virtual bool ThrowOnInvalidPropertyName { get; private set; }

        //[Conditional("DEBUG")]
        //[DebuggerStepThrough]
        //public void VerifyPropertyName(string propertyName)
        //{
        //    // Verify that the property name matches a real, public, instance property on this object. 
        //    if (TypeDescriptor.GetProperties(this)[propertyName] == null)
        //    {
        //        string msg = "Invalid property name: " + propertyName;
        //        if (this.ThrowOnInvalidPropertyName)
        //            throw new Exception(msg);
        //        else
        //            Debug.Fail(msg);
        //    }
        //}
        #endregion -- Debugging Aides --
    }
}

哈~或許有人會問那接註記掉的部分是啥吧?那是要 Debug 時確認響應的 Property 是正確的 Property 用的

不過現在寫 ViewModelBase 會用 [CallerMemberName] 這個 Attribute 就不會錯了,為表懷念還是留著 XD (使用的話就要用到 System 跟 System.Diagnostics 這兩個命名空間)

接著上個使用這基礎類別的範例~

XXModel 部分

namespace MVVM_Sample.Model
{
    class XXModel
    {
        public string Path { get; set; }
    }
}

XXViewModel 部分 (別忘了加入 Model 的命名空間)

using MVVM_Sample.Model;

namespace MVVM_Sample.ViewModel
{
    public class XXViewModel : ViewModelBase
    {
        private static XXModel file { get; set; }
        public string FilePath
        {
            get 
            {
                return file.Path; 
            }
            set
            {
                if (file.Path != value)
                {
                    file.Path = value;
                    OnPropertyChanged();
                }
            }
        }
	}
}

這例子多一個判斷式來決定是否要改變 file.Path 這 Property

XXView 部分 (把後面 Mode=TwoWay 刪除也行,但我習慣能確定的屬性都寫清楚的 XD)

<TextBox Name="txtPath" Text="{Binding FilePath, Mode=TwoWay}"/>

也別忘了要把 ViewModel 對應到 View 的 DataContext,方法有兩種:

(方法一)在 View 的 xaml 下設定

<Page.DataContext>
        <vm:XXViewModel/>
</Page.DataContext>

P.S. 我這個 View 是 Page

別忘了要加上 ViewModel 的命名空間

<Page x:Class="MVVM.View.XXView"
      ......
      xmlns:vm="clr-namespace:MVVM.ViewModel" <-- 加上這行才能設定 <vm:XXViewModel/>
      ......>

(方法二)在 View 的 xaml.cs 下設定

public XXView()
{
	InitializeComponent();
	this.DataContext = XXViewModel;
}

到此基本大功告成~

只要任何地方改動到 FilePath 這 Property,就可以看到 UI 上也會更著改變,或是改變 TextBox 上的字串,XXModel 的 Path 也會跟著改變~完結灑花~