Winform算是一個成熟且存在已久的應用程式框架,然而部分觀念可能是因為已經失傳或是沒有人特別重新講述,導致多數出現的程式碼醜陋骯髒難聞不堪。本文將介紹現今可謂是開發常識樣板(Template)切離,於Winform這個框架中實現的方式。讓Winform的樣板回歸樣板,而邏輯處理的部分回歸邏輯處理。讓開發與維護系統時不再需要找一個控制項的開關爬2000行程式碼(然後還改錯)。
前言
資料繫結(Databinding),顧名思義就是將物件與樣板(Template)做綁定,讓物件的的內容修改可以直接反應到樣板的控制項呈現(包含內容值、顯示隱藏、啟用停用、長寬設定等),簡單來說就是可以透過操作這個物件的方式來控制樣板呈現。而此時這個物鑑於實務開發中有個別名ViewModel(View的Model),類似東西可以在前端JS有套框架程式碼看到(變數簡稱為vm)。
至於為何要這樣處理?答案就是"簡單",因為後續進行資料處理時始終也是物件轉拋,更別說處理的資料有時還會影響控制項使用,若沒有進行綁定的話,那關於畫面操作就至少會出現幾個步驟"資料清除(Clear/Init),資料設定(Render),資料收集(Set Object)",然後更別說搭配上幾個DataGridView與ComboBox之類的東西後(因為Value為型別object,還需判斷轉型鬼問題),程式碼就會變成一團災難。
反之,如果一開始就直接採取繫結進行樣板分離的話,唉嘿,我只需要管好這個ViewModel就好了,其他的事情通通都在繫結中處理妥妥善善的了。(JOJO,我再也不追爛扣了!!)
實作
首先準備一個樣板,以及對應的ViewModel類別(需套用並實作INotifyPropertyChanged),接著於樣板(Form)的建構值的位置進行繫結設置。
資料繫結所使用Mehod (DataBindings.Add ),存在於Control那層,因此沒有意外的話99%得情況是可以直接綁定的。
至於nameof,基本上只是用來取得類別中prop(如Text,Enabled)之類的變數名稱字串。使用該方法可以消除Magic String,並提升程式的可維護性(變數參考檢索)。
public class DemoViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public string Name
{
get => _Name;
set
{
if (_Name != value)
{
_Name = value;
NotifyPropertyChanged();
}
}
}
string _Name;
public string Job
{
get => _Job;
set
{
if (_Job != value)
{
_Job = value;
NotifyPropertyChanged();
}
}
}
string _Job;
public bool IsWarrior => _Job == "戰士";
public string Title
{
get => _Title;
set
{
if (_Title != value)
{
_Title = value;
NotifyPropertyChanged();
}
}
}
string _Title;
public string Skill
{
get => _Skill;
set
{
if (_Skill != value)
{
_Skill = value;
NotifyPropertyChanged();
}
}
}
string _Skill;
public string Info => $@"
名稱:【{Name}】
職業:【{Job}】
稱號:【{Title}】
技能:【{Skill}】
";
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
=> PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public partial class DataBinding : Form
{
DemoViewModel _ViewModel { get; }
= new DemoViewModel();
public DataBinding()
{
InitializeComponent();
tbName.DataBindings.Add(nameof(tbName.Text), _ViewModel, nameof(_ViewModel.Name));
tbJob.DataBindings.Add(nameof(tbJob.Text), _ViewModel, nameof(_ViewModel.Job));
tbTitle.DataBindings.Add(nameof(tbTitle.Text), _ViewModel, nameof(_ViewModel.Title));
tbSkill.DataBindings.Add(nameof(tbSkill.Text), _ViewModel, nameof(_ViewModel.Skill));
tbInfo.DataBindings.Add(nameof(tbInfo.Text), _ViewModel, nameof(_ViewModel.Info), true
, DataSourceUpdateMode.Never);
btnFunctionWarrior.DataBindings.Add(nameof(btnFunctionWarrior.Enabled), _ViewModel, nameof(_ViewModel.IsWarrior));
}
private void btnModelWarrior_Click(object sender, System.EventArgs e)
{
_ViewModel.Name = "卓別林";
_ViewModel.Job = "戰士";
_ViewModel.Title = "搞笑的";
_ViewModel.Skill = "大獨裁者";
}
}
接著....就做完了。連同按鈕的啟用開關(戰士職業專屬按鈕),也一併會跟著內容異動,程式碼資料處理只要關注在ViewModel本身即可。
常犯錯誤
1.綁定的類別沒有引用並實作INotifyPropertyChanged,這樣會導致程式碼改變ViewModel內容後不會反映到Control中。詳細方法與觀念可以參考MSDN延伸閱讀。
2.ViewModel沒有確保單一實體,就是於綁定之後又將ViewModel的物件替換成 new ViewModel()。簡單來說就是"物件的Reference特性"(資料綁定時所使用的那個ViewModel物件跑走了)。
延伸閱讀
【實作 INotifyPropertyChanged 介面】(https://docs.microsoft.com/zh-tw/dotnet/framework/winforms/controls/raise-change-notifications--bindingsource)
備註
1.你說INotifyPropertyChanged 的實作麻煩且開發工作很大?這是當然的阿,實務上不會是這樣開發的,這篇是<上>文,真實的開發(偷懶)方式將於<下>文章說明。
2.這東西與MVVM好像有點相似?那你肯定誤會了甚麼,這篇文章只是用來說明解救深陷Winform莫名其妙爛扣中的苦難工程師一種方式。
3.該技巧與UserControl一起運用可以發揮真正的價值。