いろいろ備忘録日記

主に .NET とか Go とか Flutter とか Python絡みのメモを公開しています。

コントロールのデータ連結機能(Control.DataBindings, Binding, データバインディング)


System.Windows.Forms.Controlクラスには、データバインディング
行えるようにDataBindingsプロパティが公開されています。


これを行うと、データオブジェクトの特定のプロパティと
コントロールのデータが連結(バインディング)されます。
つまり、オブジェクトの値とコントロールが同期するようになります。

やり方は、簡単で以下のようにします。

class Model{
    string _val;

    public string Val{
        get{ return _val;  }
        set{ _val = value; }
    }
}

Model _m = new Model();

TextBox t1 = new TextBox();
t1.DataBindings.Add(new Binding("Text", _m, "Val"));

これで、バインディングが行われるようになります。

t1.DataBindings.Add(new Binding("Text", _m, "Val"));

の部分で、TextBoxのTextプロパティと_mのValプロパティの
値をバインディングしています。
こんな感じ。

コントロール.DataBindings.Add(new Binding(コントロール側のプロパティ名, バインド先オブジェクト, バインド先オブジェクトのプロパティ名);


Javaでいうと、JGoodiesのBindingフレームワークのような感じです。


これをうまく使うと、よくある

_m.Val = t1.Text;

(以下、入力項目の値をモデルオブジェクトに移し変えるコードが続く)

のような作業がなくなります。
画面で入力されたデータがそのままモデルオブジェクトにも連結されますので。


(補足)
ただし、上記の場合ModelオブジェクトはINotifyPropertyChangedインターフェースを
実装していないので、コントロール側からデータソース側(Modelオブジェクト)側への
連携(変更通知)は行われますが、データソース側の値を変更した場合に、コントロール側への変更通知
をおこなうことはできません。


以下サンプルです。

// vim:set ts=4 sw=4 et ws is nowrap ft=cs:

using System;
using System.Windows.Forms;

namespace Gsf.Samples.WinForms{

    /// <summary>
    /// サンプルデータモデルクラスです。
    /// </summary>
    class Model{
        string _textValue1;
        string _textValue2;

        public string TextValue1{
            get{
                return _textValue1;
            }
            set{
                _textValue1 = value;
            }
        }

        public string TextValue2{
            get{
                return _textValue2;
            }
            set{
                _textValue2 = value;
            }
        }

        public override string ToString(){
            return string.Format("{0}, {1}", _textValue1, _textValue2);
        }
    }

    /// <summary>
    /// Controlのデータバインディングのサンプルです。
    /// </summary>
    /// <remarks>
    /// Controlクラスは、データ連結が出来るように
    /// DataBindingsプロパティを公開しています。
    /// このプロパティにBindingインスタンスを追加することで
    /// 特定のオブジェクトおよびデータソースとコントロール
    /// を連結することができます。
    /// </remarks>
    public class DataBindingSample : Form{

        Model _model;

        public DataBindingSample(){
            _model = new Model();

            InitializeComponent();
        }

        protected void InitializeComponent(){

            TextBox t1 = new TextBox();
            t1.Dock    = DockStyle.Fill;
            //
            // データバインディング設定
            //
            // 以下の設定を行うことで、Modelインスタンスの
            // TextValue1プロパティとTextBoxのTextプロパティが
            // 連結されます。
            //
            // つまり、画面上で入力されたデータがTextBoxにも
            // Modelインスタンスにも設定されるようになります。
            //
            t1.DataBindings.Add(new Binding("Text", _model, "TextValue1"));

            TextBox t2 = new TextBox();
            t2.Dock    = DockStyle.Fill;
            //
            // データバインディング設定.
            //
            t2.DataBindings.Add(new Binding("Text", _model, "TextValue2"));

            Button b1  = new Button();
            b1.Text    = "Push";
            b1.Click  += delegate(object s, EventArgs e){
                //
                // ちゃんとモデルオブジェクトにもデータが格納されているか確認.
                //
                MessageBox.Show(_model.ToString());
            };

            ///////////////////////////////////////
            //
            // TableLayoutPanel
            //
            TableLayoutPanel p = new TableLayoutPanel();

            p.Dock        = DockStyle.Fill;
            p.ColumnCount = 2;

            p.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 50F));
            p.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 50F));

            p.Controls.Add(t1);
            p.Controls.Add(t2);
            p.Controls.Add(b1, 1, 2);

            Controls.Add(p);

            MaximizeBox = false;
            Size        = new System.Drawing.Size(200, 100);
        }

        [STAThread]
        static void Main(){
            Application.Run(new DataBindingSample());
        }
    }
}


[補足]
Binding作成時に指定するプロパティ名ですが、単一のプロパティだけでなく
そのプロパティが示すオブジェクトのプロパティも指定できます。
つまり、階層をたどって指定することができます。
ナビゲーションパスはドットで区切って表現します。


例)

class Address{
    string address1;
    string address2;

    (略)

    public string Address1{
        (略)
    }
}
class User{
    string name;
    Address address;

    (略)

    public Address UserAddress{
        (略) 
    }
}

という構造になっているとして、バインディング先オブジェクトにUserのUserAddressプロパティ
から取得できるAddressクラスのAddress1プロパティを指定したい場合は、

new Binding("Text", user, "UserAddress.Address1");

と指定します。
javaでいうと、OGNLでナビゲーションパスを指定しているのと似ています。