2017/6/13

Xamarin.Forms 教學系列文(十八.壹 - 1) MVVM - INPC 縮寫



接續上一小節,單純想獨立出來以方便資料查找與引用~

前一小節的兩支範例可以發現兩個問題:
  • OnPropertyChanged 使用 弱型別 當作參數 (程式常常死在這種地方,打錯一個字,幹)
  • set 有太多重複的程式碼,每次都要寫判斷值是否相同這件事


解決弱型別

一般來說寫 OnPropertyChanged 時會像前一小節這樣的寫法
public double Number
{
    set
    {
        if (number != value)
        {
            number = value;
            OnPropertyChanged("Number");
        }
    }
    get
    {
        return number;
    }
}

這種寫法有個很大的問題,就是當代入的參數打錯字時 (弱型別),程式不會產生例外錯誤,但就是無法正常運作。

這個問題可以藉由 C# 5.0 一個特殊的方法來解決,CallerMemberNameAttribute,可以讓你在參數取得 "呼叫此方法的屬性名稱"

改寫一下:
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
    PropertyChangedEventHandler handler = PropertyChanged;
    if (handler != null)
    {
        PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }
}

用法如下,就不用自行帶入弱型別的字串了:
public double Number
{
    set
    {
        if (number != value)
        {
            number = value;
            OnPropertyChanged();

            // Do something with the new value. 
        }
    }
    get
    {
        return number;
    }
}


接著為了讓我們的程式更精簡,

由於每個 set 都有重複的商業邏輯,不如直接寫一支泛型方法來使用,

這裡將 判斷新舊值是否更改 這件事改成泛型方法,並結合上方寫好的新 OnPropertyChanged:
bool SetProperty(ref T storage, T value, [CallerMemberName] string propertyName = null)
{
    if (Object.Equals(storage, value))
        return false;

    storage = value;
    OnPropertyChanged(propertyName);

    return true;
}

protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
    PropertyChangedEventHandler handler = PropertyChanged;

    if (handler != null)
    {
        PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }
}

用法如下:
public double Number
{
    set
    {
        if (SetProperty(ref number, value))
        {
            // Do something with the new value. }
        }
    }
    get
    {
        return number;
    }
}

如果沒有其他邏輯可以更縮減:
public double Number
{
    set { SetProperty(ref number, value); }
    get { return number; }
}


最後,乾脆將這兩個方法寫在 ViewModelBase 類別內,以便 ViewModel 繼承使用:
namespace Xamarin.FormsBook.Toolkit
{
    public class ViewModelBase : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        protected bool SetProperty(ref T storage, T value, [CallerMemberName] string propertyName = null)
        {
            if (Object.Equals(storage, value))
                return false;
            storage = value;

            OnPropertyChanged(propertyName);

            return true;
        }

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




沒有留言:

張貼留言