LoginSignup
3
6

More than 5 years have passed since last update.

c# Windows.Forms コレクション要素のデータバインディング

Last updated at Posted at 2018-10-09

1 はじめに

フォームアプリケーションで下図のように配列とかコレクションの要素をコントロールにバインドしたいと思ったのですが、フォームデザイナーではできないようです。
Q1.png
List内の現在位置(CurrencyManager#Position)だけならデザイナーからバインドできることは確認しました。
またコードを書くなら色々できそうなことは判りました。
色々試してみたのでその結果を書いてみます。

2 コードによるデータバインディング

コードによるデータバインディングはControl​Bindings​Collection.​Addを使用しました。
Add は System.Windows.Forms.Bindingを返すメソッドで以下のように記述します。

Control.DataBindings.Add (string propertyName, object dataSource, string dataMember);

propertyNameString
バインドするコントロール プロパティの名前。

dataSourceObject
データ ソースを表す Object。

dataMemberString
バインド先のプロパティまたはリスト。

デザイナーが作成するオートコードForm1.Designer.csを見ると上記dataSourceObjectはSystem.Windows.Forms.BindingSourceのインスタンスが指定されています。これはデータソースのインスタンスが存在しなくてもデザイナーでバインディングできるようにするクラスと思ったのですが、データソースを直接指定した場合と一部結果が異なるようです。
よって以下のパターンについてコードによるデータバインディングを試して見ました。
(1)dataSourceObjectにSystem.Windows.Forms.BindingSource(binds[])を指定
(2)dataSourceObjectにList(_food.fruits[])を指定
(3)dataSourceObjectにインデクサ(_food[])を指定
(4)その他(ComboBoxとの連携など)

Formおよびデータソースのコードは以下の通りです。

Form1.cs
public partial class Form1 : Form
    {
        private CurrencyManager currencyManager1 = null;
        private Food _food;
        public Form1()
        {                       
            InitializeComponent();            
            _food = new Food();
            foodBindingSource.DataSource = _food;
            BindingSource binds = new BindingSource();
            binds.DataSource = _food.fruits;
            //List<Fruits> fruitsの現在行を管理
            currencyManager1 = (CurrencyManager)this.BindingContext[_food.fruits];
            //dataSourceObjectにSystem.Windows.Forms.BindingSource(binds[])を指定
            textBoxName1.DataBindings.Add("Text", binds[0], "Name");
            textBoxQuant1.DataBindings.Add("Text", binds[0], "Quantity");
            //dataSourceObjectにList(food.fruits[])を指定
            textBoxName2.DataBindings.Add("Text", _food.fruits[1], "Name");
            textBoxQuant2.DataBindings.Add("Text", _food.fruits[1], "Quantity");
            //dataSourceObjectにインデクサ(food[])を指定
            textBoxName3.DataBindings.Add("Text", _food[2], "Name");
            textBoxQuant3.DataBindings.Add("Text", _food[2], "Quantity");
            textBoxCurName.DataBindings.Add("Text", _food.fruits, "Name");
            textBoxCurQuant.DataBindings.Add("Text", _food.fruits, "Quantity");
            textBoxCurQuant2.DataBindings.Add("Text", binds, "Quantity");
            comboBox1.DataSource = _food.fruits;
            comboBox1.DisplayMember = "Name";
        }

        private void buttonMovePosition_Click(object sender, EventArgs e)
        {
            if (currencyManager1.Position < (_food.fruits.Count - 1))
                currencyManager1.Position++;
            else
                currencyManager1.Position = 0;
        }

        private void buttonCountUp_Click(object sender, EventArgs e)
        {
            _food.fruits[currencyManager1.Position].Quantity += 1;
        }
Food.cs
public class Food
    {
        public List<Fruit> fruits;

        public Food()
        {
            fruits = new List<Fruit>();
            fruits.Add(new Fruit("apple",10));
            fruits.Add(new Fruit("orange", 20));
            fruits.Add(new Fruit("peach", 30));
        }

        public Fruit this[int index]
        {
            get
            {
                return fruits[index];
            }
        }

        public class Fruit : INotifyPropertyChanged
        {
            private string _name;
            private int _quantity;
            public Fruit(string name, int quantity)
            {
                _name = name;
                _quantity = quantity;               
            }

            public string Name
            {
                get
                {
                    return _name;
                }
                set
                {
                    _name = value;
                    OnPropertyChanged();
                }
            }

            public int Quantity
            {
                get
                {
                    return _quantity;
                }
                set
                {
                    _quantity = value;
                    OnPropertyChanged();
                }
            }

            public event PropertyChangedEventHandler PropertyChanged;
            private void OnPropertyChanged([CallerMemberName] string propertyName = null)
            {
                PropertyChangedEventHandler handler = PropertyChanged;
                if (handler != null)
                {
                    handler(this, new PropertyChangedEventArgs(propertyName));
                }
            }            
        }
    }

3 結果

以下の観点で評価しました。
(1)初期表示が意図した表示になるか(○:なる、×:ならない)
(2)プロパティ変更時コントロールに反映されるか(○:なる、×:ならない、-:該当しない等)
(3)現在行変更時関連するコントロールの表示が連動するか(○:なる、×:ならない、-:該当しない等)

コントロール データソース バインド先 初期表示 プロパティ変更時 現在行変更時
textBoxQuant1 binds[0] Quantity -
textBoxQuant2 _food.fruits[1] Quantity -
textBoxQuant3 _food[2] Quantity -
comboBox1 _food.fruits - -
textBoxCurQuant _food.fruits Quantity ×
textBoxCurQuant2 binds Quantity

以下が確認した画面ですが、「数量更新」でQuantityプロパティを変更しても何故かtextBoxCurQuantの値が更新されません。
Q3.png
まあ、BindingSource を使えば問題は回避されるのですが、まだまだ双方向バインディングとか理解不足です。

4 主な参考サイト

  1. Data Binding in .NET / C# Windows Forms
    https://www.akadia.com/services/dotnet_databinding.html

5 環境

開発環境 --- Visual Studio Community 2017

ターゲットフレームワーク --- .NET Fremawork 4.6.1

3
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
6