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

##1 はじめに
フォームアプリケーションで下図のように配列とかコレクションの要素をコントロールにバインドしたいと思ったのですが、フォームデザイナーではできないようです。
![Q1.png](https://qiita-image-store.s3.amazonaws.com/0/293616/90fcfdd2-678c-c8b2-bbf2-97d9053b233b.png)
List内の現在位置（CurrencyManager#Position）だけならデザイナーからバインドできることは確認しました。
またコードを書くなら色々できそうなことは判りました。
色々試してみたのでその結果を書いてみます。

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

```Control.DataBindings.Add (string propertyName, object dataSource, string dataMember);```
<dl><dt>`propertyNameString `<dt><dd>バインドするコントロール プロパティの名前。 </dd>
<dt>`dataSourceObject `</dt><dd>データ ソースを表す Object。 </dd>
<dt>`dataMemberString `</dt><dd>バインド先のプロパティまたはリスト。 </dd></dl>
デザイナーが作成するオートコード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との連携など）
<br>

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)<font color="Red">初期表示</font>が意図した表示になるか（○：なる、×：ならない）
(2)<font color="Red">プロパティ変更時</font>コントロールに反映されるか（○：なる、×：ならない、-：該当しない等）
(3)<font color="Red">現在行変更時</font>関連するコントロールの表示が連動するか（○：なる、×：ならない、-：該当しない等）

| コントロール  | データソース | バインド先   | 初期表示    | プロパティ変更時 | 現在行変更時 |
|:-----------|:-----------|:-----------|:-----------:|:------------:|:------------:|
| 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](https://qiita-image-store.s3.amazonaws.com/0/293616/a62e3873-1217-0efd-f892-a0f69614ecd3.png)
まあ、BindingSource を使えば問題は回避されるのですが、まだまだ双方向バインディングとか理解不足です。

##4 主な参考サイト
1. Data Binding in .NET / C# Windows Forms<br>
https://www.akadia.com/services/dotnet_databinding.html

##5 環境
<dl>
<dt>開発環境 --- Visual Studio Community 2017</dt>
</dl>
ターゲットフレームワーク --- .NET Fremawork 4.6.1