介面INotifyPropertyChanged
微軟的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>相關程式碼之後再補充