Blazor 綁定屬性變更 Part 3 - 透過 PropertyChanged.Fody 套件,使用 INotifyPropertyChanged 機制,簡化程式碼設計
更多關於 Blazor 教學影片,可以參考 Blazor 教學影片播放清單 或者 Blazor 快速體驗教學影片撥放清單。也歡迎訂閱本 .NET / Blazor / Xamarin.Forms 影片頻道 。
Blazor 綁定屬性變更 Part 1 - 透過 DOM 事件機制,用來偵測有興趣的物件有變化時,需要作出相對應的處理機制
Blazor 綁定屬性變更 Part 3 - 透過 PropertyChanged.Fody 套件,使用 INotifyPropertyChanged 機制,簡化程式碼設計
這是本系列文章的第三個部分,在前一篇文章使用 INotifyPropertyChanged 提供介面,針對要使用的資料模型,都需要進行實作出 INotifyPropertyChanged 介面,可是,這樣會造成該類別內的屬性,都需要使用 含有支援欄位的屬性 的設計方式,而且在設定存取子 Set Accessor 的部分,也需要增加額外的程式碼來進行拋出 PropertyChanged 的事件;對於這樣的設計方式會顯得有些繁雜而且容易出錯。
因此,在這篇文章中,將會使用另一種做法,那就是使用 PropertyChanged.Fody 這個套件,將許多繁雜的程式碼,透過這個套件來交由編譯器來產生出來;另外,該套件也提供了許多額外好用的機制,On_PropertyName_Changed 就是一個,只要依照該套件規範的方式來設計,當指定的屬性值有變更的時候,就會自動觸發與執行這個方法。
這個說明專案的原始碼位於 bzINPCbyFody
建立 Blazor Server-Side 的專案
首先,還是先來建立一個新的專案
- 打開 Visual Studio 2019
- 點選右下方的 [建立新的專案] 按鈕
- [建立新專案] 對話窗將會顯示在螢幕上
- 從[建立新專案] 對話窗的中間區域,找到 [Blazor 應用程式] 這個專案樣板選項,並且選擇這個項目
- 點選右下角的 [下一步] 按鈕
- 現在 [設定新的專案] 對話窗將會出現
-
請在這個對話窗內,輸入適當的 [專案名稱] 、 [位置] 、 [解決方案名稱]
在這裡請輸入 [專案名稱] 為
bzINPCbyFody
-
完成後,請點選 [建立] 按鈕
- 當出現 [建立新的 Blazor 應用程式] 對話窗的時候
- 請選擇最新版本的 .NET Core 與 [Blazor 伺服器應用程式]
- 完成後,請點選 [建立] 按鈕
稍微等會一段時間,Blazor 專案將會建立起來
請在這個對話窗內,輸入適當的 [專案名稱] 、 [位置] 、 [解決方案名稱]
在這裡請輸入 [專案名稱] 為
bzINPCbyFody
完成後,請點選 [建立] 按鈕
安裝 PropertyChanged.Fody NuGet 套件
- 滑鼠右擊 [相依性] 節點
- 選擇 [管理 NuGet 套件]
- 在 [管理 NuGet 套件] 視窗內,點選 [瀏覽] 標籤頁次
- 搜尋
PropertyChanged.Fody
這個套件
- 找到這個套件,點選 [安裝] 按鈕,將這個套件安裝起來
PropertyChanged.Fody
這個套件建立需求網頁 Balzor 元件
- 打開 [Pages] 資料夾內的 [Index.razor] 檔案
- 使用底下 Razor 元件標記與程式碼,替換該檔案內的原有內容
@page "/"
<h1>在 Blazor 透過 PropertyChanged.Fody 套件,使用 INotifyPropertyChanged 事件,設計屬性變更的需求</h1>
<div class="form-group">
<label for="FirstName">名</label>
<input id="FirstName" class="form-control" @bind="MyPerson.FirstName" />
</div>
<div class="form-group">
<label for="LastName">姓</label>
<input id="LastName" class="form-control" @bind="MyPerson.LastName" />
</div>
<div class="form-group">
<label for="Age">年紀</label>
<input type="number" id="Age" class="form-control"
@bind="MyPerson.Age" @bind:event="oninput" />
</div>
<button type="submit" class="btn btn-primary" disabled="@MyPerson.IsDisabled">Submit</button>
<div class="text-danger">
@MyPerson.Age
</div>
<div class="text-danger">
@MyPerson.AgeRange
</div>
@code{
class Person : System.ComponentModel.INotifyPropertyChanged
{
public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
public string FirstName { get; set; }
public string LastName { get; set; }
public int Age { get; set; }
public string AgeRange { get; set; }
public bool IsDisabled { get; set; } = true;
public void OnAgeChanged()
{
if (Age < 13)
{
AgeRange = "你是小朋友";
IsDisabled = true;
}
else if (Age < 20)
{
AgeRange = "你是年青人";
IsDisabled = false;
}
else if (Age < 28)
{
AgeRange = "你是青年人";
IsDisabled = false;
}
else
{
AgeRange = "你是成年人";
IsDisabled = false;
}
}
}
Person MyPerson = new Person();
}
在這個 index.razor 檔案內,對於 HTML 標記的設計上,原則上沒有使用到任何宣告,如同上一篇文章所提到的內容。
不過,在這裡將會針對 Person 類別,同樣也實作了 INotifyPropertyChanged 這個介面,不過,所有的屬性,將會使用 自動實作屬性 來完成,不過,這裡將會產生疑問,那就是當屬性值有異動的時候,要如何觸發 PropertyChanged 這個事件呢?
還記得剛剛之前有安裝了 PropertyChanged.Fody 這個套件,只要建置專案,對於剛剛所需要處理的程式碼,這個套件將會幫忙產生其他會用到的程式碼。
使用這樣的設計方式,有點像是在設計 ViewModel,將會把許多與頁面有關的程式碼,都寫到這個類別內,不過,透過了資料綁定機制,形成鬆散耦合的關係;另外,這裡也會使用 PropertyChanged.Fody 套件提供的功能,可以撰寫一個屬性變更的 callback 函式,也就是說,當屬性值有變動的時候,會自動執行的方法。
想要使用這樣的功能,僅需要使用特定的方法簽章來命名即可, PropertyChanged.Fody 套件會自動完成其他相關程式碼運作機制。例如,想要針對當 Age 屬性有異動的時候,可以自動執行某個方法,這裡需要使用這樣的方法簽章來命名
public void On_PropertyName_Changed(){...}
其中,對於方法名稱的最前面需要有 On 這個字串,緊接著是這個屬性的名稱,最後使用 Changed 最為結尾,一旦設計出這個方法之後,只要 Age 的屬性有異動的時候,將會自動執行 OnAgeChanged 這個方法。
現在,這個專案設計工作已經完成了,現在可以來比對這三種的不同作法,將會發現到這裏所提出的作法,所設計出來的程式碼最為簡單與清爽,而且還增加了許多不錯的功能可以來應用。
底下將會是執行結果
@page "/"
<h1>在 Blazor 透過 PropertyChanged.Fody 套件,使用 INotifyPropertyChanged 事件,設計屬性變更的需求</h1>
<div class="form-group">
<label for="FirstName">名</label>
<input id="FirstName" class="form-control" @bind="MyPerson.FirstName" />
</div>
<div class="form-group">
<label for="LastName">姓</label>
<input id="LastName" class="form-control" @bind="MyPerson.LastName" />
</div>
<div class="form-group">
<label for="Age">年紀</label>
<input type="number" id="Age" class="form-control"
@bind="MyPerson.Age" @bind:event="oninput" />
</div>
<button type="submit" class="btn btn-primary" disabled="@MyPerson.IsDisabled">Submit</button>
<div class="text-danger">
@MyPerson.Age
</div>
<div class="text-danger">
@MyPerson.AgeRange
</div>
@code{
class Person : System.ComponentModel.INotifyPropertyChanged
{
public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
public string FirstName { get; set; }
public string LastName { get; set; }
public int Age { get; set; }
public string AgeRange { get; set; }
public bool IsDisabled { get; set; } = true;
public void OnAgeChanged()
{
if (Age < 13)
{
AgeRange = "你是小朋友";
IsDisabled = true;
}
else if (Age < 20)
{
AgeRange = "你是年青人";
IsDisabled = false;
}
else if (Age < 28)
{
AgeRange = "你是青年人";
IsDisabled = false;
}
else
{
AgeRange = "你是成年人";
IsDisabled = false;
}
}
}
Person MyPerson = new Person();
}
public void On_PropertyName_Changed(){...}