Binding List<T> to Datagridview in Winform

Binding List T to DataGridView in WinForm

List does not implement IBindingList so the grid does not know about your new items.

Bind your DataGridView to a BindingList<T> instead.

var list = new BindingList<Person>(persons);
myGrid.DataSource = list;

But I would even go further and bind your grid to a BindingSource

var list = new List<Person>()
{
new Person { Name = "Joe", },
new Person { Name = "Misha", },
};
var bindingList = new BindingList<Person>(list);
var source = new BindingSource(bindingList, null);
grid.DataSource = source;

How to bind List to dataGridview C#

Data binding only work with Set/Get Properties, not public field.

Try to change your FileDetail like this :

public class FileDetail
{
public string filename { get; set; }
public int openConnectionCount { get; set; }
public int closeConnectionCount { get; set; }
}

C# BindingList T as Datasource for DataGrid

Databinding works with properties not fields. The DataGridView won't find Properties for generating columns (i assume you use autocreatecolumns in your grid) in your class and refuses to work without any columns.

So you need to replace your fields with (atleast automatic) properties.

public class Customer
{
public System.Guid GUID { get; set; }
// etc.
}

How to bind list to dataGridView?

Use a BindingList and set the DataPropertyName-Property of the column.

Try the following:

...
private void BindGrid()
{
gvFilesOnServer.AutoGenerateColumns = false;

//create the column programatically
DataGridViewCell cell = new DataGridViewTextBoxCell();
DataGridViewTextBoxColumn colFileName = new DataGridViewTextBoxColumn()
{
CellTemplate = cell,
Name = "Value",
HeaderText = "File Name",
DataPropertyName = "Value" // Tell the column which property of FileName it should use
};

gvFilesOnServer.Columns.Add(colFileName);

var filelist = GetFileListOnWebServer().ToList();
var filenamesList = new BindingList<FileName>(filelist); // <-- BindingList

//Bind BindingList directly to the DataGrid, no need of BindingSource
gvFilesOnServer.DataSource = filenamesList
}

c# WinForm DataGridView bind List in List

You can use CellFormatting event of DataGridView to provide a friendly representation of categories property:

void dataGridView1_CellFormatting(object sender, DataGridViewCellFormattingEventArgs e)
{
//Use zero based index of the cell that contains categories
var cell= 3;

if (e.ColumnIndex == cell && e.RowIndex >= 0)
{
var categories = (List<object>)dataGridView1.Rows[e.RowIndex].Cells[cell].Value;
e.Value = string.Join(", ", categories.Select(x => x.ToString()));
}
}

In this case, categories contains List of product categories names (string). So selecting ToString() is OK. But for a case of List<SomeClass>, you either should override ToString of SomeClass or select one of it's properties.

As another option you can shape the result of query using a new ViewModel or using anonymous type, but in the current situation which Product model doesn't belong to you and it has lot's of properties, previous solution is the most simple option.

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

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;
}
}

DataGridView Databinding to List List T


Implementing IListSource and mapping to DataTabe internally

You can create a custom data source which implements IListSource and set it as data source of DataGridView. To implement the interface properly to satisfy your requirement:

  • In constructor, accept original list and map it to a DataTable.
  • Subscribe to ListChanged event of the DefaultView property of you data table and apply changes to your original list.
  • For GetList method, return the mapped data table.

Then when you bind DataGridView to your new data source, all the editing operations will immediately reflect in your original list:

dataGridView1.DataSource = new FooDataSource(yourListOfListOfFoo);

ListListDataSource Implementation

public class ListListDataSource<T> : IListSource
{
List<List<T>> data;
DataTable table;
public ListListDataSource(List<List<T>> list)
{
this.data = list;
table = new DataTable();
for (int i = 0; i < list.First().Count(); i++)
{
TypeDescriptor.GetProperties(typeof(T)).Cast<PropertyDescriptor>()
.Where(p => p.IsBrowsable).ToList().ForEach(p =>
{
if (p.IsBrowsable)
{
var c = new DataColumn($"[{i}].{p.Name}", p.PropertyType);
c.ReadOnly = p.IsReadOnly;
table.Columns.Add(c);
}
});
}
foreach (var innerList in list)
{
table.Rows.Add(innerList.SelectMany(
x => TypeDescriptor.GetProperties(typeof(T)).Cast<PropertyDescriptor>()
.Where(p => p.IsBrowsable).Select(p => p.GetValue(x))).ToArray());
}
table.DefaultView.AllowDelete = false;
table.DefaultView.AllowNew = false;
table.DefaultView.ListChanged += DefaultView_ListChanged;
}

public bool ContainsListCollection => false;
public IList GetList()
{
return table.DefaultView;
}
private void DefaultView_ListChanged(object sender, ListChangedEventArgs e)
{
if (e.ListChangedType != ListChangedType.ItemChanged)
throw new NotSupportedException();
var match = Regex.Match(e.PropertyDescriptor.Name, @"\[(\d+)\]\.(\w+)");
var index = int.Parse(match.Groups[1].Value);
var propertyName = match.Groups[2].Value;
typeof(T).GetProperty(propertyName).SetValue(data[e.NewIndex][index],
table.Rows[e.NewIndex][e.PropertyDescriptor.Name]);
}
}

Then bind your list to DataGridView like this:

List<List<Foo>> foos;
private void Form1_Load(object sender, EventArgs e)
{
foos = new List<List<Foo>>{
new List<Foo>(){
new Foo() { Id=11, Value="11"}, new Foo() { Id = 12, Value = "12" }
},
new List<Foo>() {
new Foo() { Id=21, Value="21"}, new Foo() { Id = 22, Value = "22" }
},
};
dataGridView1.DataSource = new ListListDataSource<Foo>(foos);
}

Sample Image

And when you edit data in DataGridView, in fact you are editing the original list.

Also if you want to hide a property, it's as easy as adding [Browsable(false)] to the property:

public class Foo
{
[Browsable(false)]
public int Id { get; set; }
public string Value { get; set; }
}

Sample Image



Related Topics



Leave a reply



Submit