11.24.2016

WinForm表單控制項狀態管理 - MVVM(with ReactiveUI) way

用物件導向工具開發程式1

最早最早,所有code都寫在Form中時,狀態的變更,落在各個被觸發的事件中,假設目前我們有一個TextBox,一個Button,程式需求TextBox中至少要n個字元才可以觸發Button的Click事件,於是我們在TextBox的TextChanged事件中加上判斷,再設定按鈕Enabled,搞定。

但事情發生在多個控制項,多個狀態時,複雜度隨之而來。

開發物件導向程式

於是,換個方向,使用狀態Property,在它的setter中寫判斷邏輯,看起來很好,但還是跟Form綁太緊了。尋找進階版,Mediator浮現,嗯嗯,符合SRP原則,可是好像不夠OCP,沒關係,再抽象一次…
發現有人寫好了(UI State Synchronization of WinForm Controls)

_stateManager
    .AddCommand(CMD_INDEX_CHECKING, UIObject.CreateObject(this.btnRemTarget));
_stateManager
    .AddCommand(CMD_INDEX_CHECKING, UIObject.CreateObject(this.btnSpiralTest));
_stateManager
    .AddCommand(CMD_INDEX_CHECKING, UIObject.CreateObject(this.btnIndexCheck));

_stateManager
    .AddCommand(CMD_SPIRAL_CHECKING, UIObject.CreateObject(this.btnRemTarget));
_stateManager
    .AddCommand(CMD_SPIRAL_CHECKING, UIObject.CreateObject(this.btnSpiralTest));
_stateManager
    .AddCommand(CMD_SPIRAL_CHECKING, UIObject.CreateObject(this.btnIndexCheck));

UI層的狀態維護,交給Mediator負責,而這個可重用的Mediator中透過字典記錄使用者定義的命令及相對應的控制項,並依需要設定控制項的狀態,這種方式下再配合Model-View-Presenter模式,UI和Presenter不再耦合,Presenter中也不用再出現WinForm相關的參考,perfect!

那…為什麼要ReactiveUI?

因為它支援.Net目前大多數的Presentation,in MVVM way.
GitHub測試

View

var context = SynchronizationContext.Current;
VM = new ViewModel2(
    context,
    this.WhenAnyValue(x => x.textBox1.Text),
    this.WhenAnyValue(x => x.textBox2.Text)));

// bind ViewModel's property to control
this.Bind(VM, x => x.UserName, x => x.textBox1.Text);
this.Bind(VM, x => x.Password, x => x.textBox2.Text);

// extra bind, let a property in ViewModel determinate the state of a control
VM.CanUserLogin.BindTo(this, x => x.btnLogin.Visible);

ReactiveUI提供了一個額外的綁定功能,如上將CanUserLogin綁定至控制項的屬性中。而其它的Bind動作,若是在xaml系的表單上,可以直接指定。

ViewModel, 實作商業邏輯

name.ToProperty(this, x => x.UserName, out _userName);
password.ToProperty(this, x => x.Password, out _password);

CanUserLogin = this.WhenAnyValue(
    x => x.UserName, x => x.Password,
    (user, pass) =>
        !string.IsNullOrWhiteSpace(user) &&
        !string.IsNullOrWhiteSpace(pass) &&
        user.Length >= 2 && pass.Length >= 3)
    .DistinctUntilChanged();

Written with StackEdit.

沒有留言:

張貼留言