介面INotifyPropertyChanged

23f9f63a7d27bdbd1cb08f49b5249101.jpg

微軟的WPF框架,要讓表格(DataGrid)能呈現資料表,能即時呈現資料異動,資料結構必須先繼承和實作INotifyPropertyChanged,再儲存到ObservableCollection<T>內,ObservableCollection<T>再繫節(Binding)到DataGrid.ItemsSource

為了讓資料結構能序列化(Serializable),能利用ORMap工具(譬如Dapper)直接對應到DB的DataTable,能直接序列化儲存到檔案或是透過網路傳輸,實作INotifyPropertyChanged時有些眉角要注意([field: NonSerialized()]),否則序列化時會跳錯(SerializationException)

參考資料
https://stackoverflow.com/questions/8879426/serializationexception-when-serializing-instance-of-a-class-which-implements-ino
https://docs.microsoft.com/en-us/dotnet/api/system.nonserializedattribute?redirectedfrom=MSDN&view=net-6.0
https://stackoverflow.com/questions/1315621/implementing-inotifypropertychanged-does-a-better-way-exist

這裡先記錄資料結構的基底類別NotifyPropertyChanged

[Serializable]
public class NotifyPropertyChanged : INotifyPropertyChanged
{
    /// <summary>
    /// 事件通知指定欄位有資料變更
    /// </summary>
    [field: NonSerialized()]
    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void OnPropertyChanged(PropertyChangedEventArgs e)
    {
        PropertyChanged?.Invoke(this, e);
    }

    /// <summary>
    /// 
    /// </summary>
    /// <param name="propertyName">欄位名稱</param>
    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = "")
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }

    /// <summary>
    /// 有一個欄位可能有資料異動
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="field">舊資料</param>
    /// <param name="value">新資料</param>
    /// <param name="propertyName">欄位名稱</param>
    /// <returns>是否有異動</returns>
    protected virtual bool OnPropertyChanged<T>(ref T field, T value, [CallerMemberName] string propertyName = "")
    {
        if (EqualityComparer<T>.Default.Equals(field, value))
        {
            return false;
        }

        field = value;
        OnPropertyChanged(propertyName);

        return true;
    }

    /// <summary>
    /// 有多個欄位可能有資料異動
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="field">舊資料</param>
    /// <param name="value">新資料</param>
    /// <param name="propertyName">第一個欄位名稱</param>
    /// <param name="otherNames">其它欄位名稱</param>
    /// <returns>是否有異動</returns>
    protected virtual bool OnPropertiesChanged<T>(ref T field, T value, string propertyName, params string[] otherNames)
    {
        if (EqualityComparer<T>.Default.Equals(field, value))
        {
            return false;
        }

        field = value;
        OnPropertyChanged(propertyName);

        foreach (string name in otherNames)
        {
            OnPropertyChanged(name);
        }

        return true;
    }
}

寫C/C++的人看到new PropertyChangedEventArgs(propertyName)大概會覺得渾身不舒服,每次變更資料都要new一個PropertyChangedEventArgs物件,記憶體使用上的規劃和效能的損耗會覺得很恐怖

實際測試是看不出CPU或記憶體飆高的情況,耗時也測不出來(< 1 ms),應該是.NET有很好的記憶體管理機制(garbage collection)

DataGrid和ObservableCollection<T>相關程式碼之後再補充

arrow
arrow
    文章標籤
    程式設計 C# WPF
    全站熱搜
    創作者介紹
    創作者 Yang 的頭像
    Yang

    GNAySolution

    Yang 發表在 痞客邦 留言(0) 人氣()