Search code examples
c#winformsdatagridview2-way-object-databinding

Two-way data binding of int[] or List<int> to DataGridView in WinForms


This solution helps to populate dgv but since anonymous types are immutable such grid cannot be edited but even otherwise I dont't think it would have helped with 2-way binding

    List<int> values = GetValues();
    var dataGridView = new DataGridView();
    dataGridView.DataSource =  values.Select(el => new { Value = el }).ToList(); 

Using wrapper class allows me to edit cells but changes are not reflected on the original collection

    public class Wrapper<T>
    {
        public T Value { get; set; }
        public Wrapper(T value) => Value = value;
    }
    ...
    dataGridView.DataSource = new BindingList<Wrapper<int>>(values.Select(value => new Wrapper<int>(value)).ToList());

So how do you make it work?


Solution

  • Two-way databinding works based on change notification of the data source. Here your data source is List<int>, neither List<T> nor int raises change notification, thus two-way databinding doesn't make sense here.

    Assuming you want to implement two-way databinding of BindingList<int> with DataGridView, here is what you can do:

    BindingList<int> originalBindingList;
    private void Form1_Load(object sender, EventArgs e)
    {
        originalBindingList = new BindingList<int>(new List<int> { 1, 2, 3 });
        dataGridView1.DataSource = new ListDataSource<int>(originalBindingList);
    }
    

    Then:

    • DataGridView will show you a Value column and rows containing the originalBindingList values.
    • If you change values in DataGridView the values in originalBindingList will change.
    • If you change values if originalBindingList in code, DataGridView will refresh values.

    And here is ListDataSource:

    public class ListDataSource<T> : BindingSource
    {
        public ListDataSource(BindingList<T> original)
        {
            for (int i = 0; i < original.Count; i++)
            {
                this.Add(new Item(original, i));
            }
            original.ListChanged += (obj, args) =>
                this.OnListChanged(new ListChangedEventArgs(
                    args.ListChangedType, args.NewIndex));
        }
        private class Item : INotifyPropertyChanged
        {
            IList<T> source;
            int index;
            public Item(IList<T> source, int index)
            {
                this.source = source;
                this.index = index;
            }
            public T Value
            {
                get { return source[index]; }
                set
                {
                    source[index] = value;
                    PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Value"));
                }
            }
            public event PropertyChangedEventHandler PropertyChanged;
        }
    }