【Winform】-透過DataBinding實現樣板(Template)切離(上)

  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一起運用可以發揮真正的價值。