[料理佳餚] Windows Forms 三種 Binding 的連動方式:TwoWayBinding、OneWayBinding、OneWayToSourceBinding

日常的工作當中,還是難免遇到需要維護 Windows Forms 的程式,基本上 Windows Forms 控制項的互動能夠用 Binding 的方式去處理,我就儘量用 Binding 的方式去處理,讓我自己能夠有更多的精神放在商業邏輯上,這篇文章想跟大家分享 Windows Forms 的三種 Binding 的連動方式。

TwoWayBinding

TwoWayBinding(雙向綁定)是 Windows Forms 預設的綁定方式,控制項跟資料源綁定上之後,不管是控制項上面的值異動了,還是資料源的值異動了,都會彼此連動,當我們有需要對使用者的輸入,在同一個控制項上去做出回應的時候,就可以用 TwoWayBinding。

假定我的畫面有一個 TextBox 及一個 Button,然後我想 TextBox 的 Text 屬性跟 MyViewModel 的 Id 屬性做雙向綁定。

OneWayBinding

OneWayBinding(單向綁定)我把它定義為資料源的值異動了,會連動控制項的值,反過來則不會,這個 Binding 需要將 DataSourceUpdateMode 設定為 DataSourceUpdateMode.Never,意思就是說「不要更新 DataSource」,OneWayBinding 通常用在對於使用者來說,這個控制項所代表的資料源是唯讀的時候。

OneWayToSourceBinding

最後是 OneWayToSourceBinding(單向來源綁定),它是與 OneWayBinding 相反的連動方式,控制項的值異動了,會連動資料源的值,反過來則不會,通常用在不需要去驗證輸入格式的控制項上,或是說之後的運算會去異動資料源,但不希望這個異動反應到控制項上,要做到 OneWayToSourceBinding 我們需要將 ControlUpdateMode 設定為 ControlUpdateMode.Never

最後跟大家分享一個小技巧,從剛剛的範例就可以看到,觸發資料源更新是在焦點從 TextBox 移開之後,但是有時候我們想所見即所得,隨著使用者一邊輸入,一邊就更新資料源,這時候我們只要將 DataSourceUpdateMode 設定為 DataSourceUpdateMode.OnPropertyChanged 就可以了。

完整原始碼

public partial class Form1 : Form
{
    private readonly MyViewModel myViewModel;

    public Form1()
    {
        this.InitializeComponent();

        this.myViewModel = new MyViewModel();

        // TwoWayBinding
        //this.textBox1.DataBindings.Add("Text", this.myViewModel, nameof(MyViewModel.Id));


        // OneWayBinding
        //var binding = new Binding("Text", this.myViewModel, nameof(MyViewModel.Id))
        //              {
        //                  DataSourceUpdateMode = DataSourceUpdateMode.Never
        //              };

        //this.textBox1.DataBindings.Add(binding);


        // OneWayToSourceBinding
        //var binding = new Binding("Text", this.myViewModel, nameof(MyViewModel.Id))
        //              {
        //                  ControlUpdateMode = ControlUpdateMode.Never
        //              };

        //this.textBox1.DataBindings.Add(binding);

        // 所見即所得
        var binding = new Binding("Text", this.myViewModel, nameof(MyViewModel.Id))
                      {
                          ControlUpdateMode = ControlUpdateMode.Never, DataSourceUpdateMode = DataSourceUpdateMode.OnPropertyChanged
                      };

        this.textBox1.DataBindings.Add(binding);
    }

    private void button1_Click(object sender, EventArgs e)
    {
        this.myViewModel.Id = new Random(Guid.NewGuid().GetHashCode()).Next(100);
    }
}

public class MyViewModel : INotifyPropertyChanged
{
    private int id;

    public event PropertyChangedEventHandler PropertyChanged;

    public int Id
    {
        get => this.id;
        set
        {
            if (value == this.id) return;

            this.id = value;
            this.OnPropertyChanged();

            Console.WriteLine($"{nameof(MyViewModel.Id)} change to {value}.");
        }
    }

    [NotifyPropertyChangedInvocator]
    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

相關資源

C# 指南
ASP.NET 教學
ASP.NET MVC 指引
Azure SQL Database 教學
SQL Server 教學
Xamarin.Forms 教學