As I understand the question, you have a DataGridView
with AutoGenerateColumns=true
, just set the DataSource
property to a DataTable
and want to be able to customize the auto generated columns.
Let me start with the fact that this is not possible. DataGridView
does not provide any way of customizing the auto generated columns - no event, virtual method, attribute etc. It's all or nothing. And by default it creates only 3 types of columns - check box, image and text, with something like this:
private static DataGridViewColumn DefaultColumnFactory(Type type)
{
if (type == typeof(bool)) return new DataGridViewCheckBoxColumn(false);
if (type == typeof(CheckState)) return new DataGridViewCheckBoxColumn(true);
if (typeof(Image).IsAssignableFrom(type)) return new DataGridViewImageColumn();
var imageConverter = TypeDescriptor.GetConverter(typeof(Image));
if (imageConverter.CanConvertFrom(type)) return new DataGridViewImageColumn();
if (!typeof(System.Collections.IList).IsAssignableFrom(type)) return new DataGridViewTextBoxColumn();
return null;
}
So far so good. Fortunately it's not hard to build that functionality ourselves. First, we will add another "default" column type creation based on the property type inside the above method. And second, we will allow the caller to "override" the default behavior for each data source property, thus enable creaing combo boxes and other type of columns that need additional initialization.
In order to that, we will encapsulate it in a custom extension method with the following signature:
public static void Bind(
this DataGridView view,
object dataSource,
string dataMember = "",
Func<PropertyDescriptor, DataGridViewColumn> columnFactory = null)
The dataSource
and dataMember
represent the corresponding DataGridView
properties, while the columnFactory
delegate is the extension point.
Here is the full implementation:
public static class DataGridViewExtensions
{
public static void Bind(this DataGridView view, object dataSource, string dataMember = "", Func<PropertyDescriptor, DataGridViewColumn> columnFactory = null)
{
var columns = new List<DataGridViewColumn>();
var properties = ListBindingHelper.GetListItemProperties(dataSource, dataMember, null);
for (int i = 0; i < properties.Count; i++)
{
var property = properties[i];
if (!property.IsBrowsable) continue;
var column = (columnFactory != null ? columnFactory(property) : null) ?? DefaultColumnFactory(property.PropertyType);
if (column == null) continue;
column.DataPropertyName = property.Name;
column.Name = property.Name;
column.HeaderText = !string.IsNullOrEmpty(property.DisplayName) ? property.DisplayName : property.Name;
column.ValueType = property.PropertyType;
column.ReadOnly = property.IsReadOnly;
columns.Add(column);
}
view.DataSource = null;
view.Columns.Clear();
view.AutoGenerateColumns = false;
view.Columns.AddRange(columns.ToArray());
view.DataMember = dataMember;
view.DataSource = dataSource;
}
private static DataGridViewColumn DefaultColumnFactory(Type type)
{
if (type == typeof(bool)) return new DataGridViewCheckBoxColumn(false);
if (type == typeof(CheckState)) return new DataGridViewCheckBoxColumn(true);
if (typeof(Image).IsAssignableFrom(type)) return new DataGridViewImageColumn();
var imageConverter = TypeDescriptor.GetConverter(typeof(Image));
if (imageConverter.CanConvertFrom(type)) return new DataGridViewImageColumn();
// Begin custom default factory
if (type == typeof(DateTime)) return new CalendarColumn();
// End custom default factory
if (!typeof(System.Collections.IList).IsAssignableFrom(type)) return new DataGridViewTextBoxColumn();
return null;
}
}
Note: The CalendarColumn
used is from How to: Host Controls in Windows Forms DataGridView Cells MSDN example.
The above method works with DataTable
, DataSet
as well as every data source type (IList
, IBindingList
, IListSource
etc.) supported by the DataGridView
control.
The usage with DataTable
is simply as that:
dataGridView.Bind(dataTable);
Demo::
static class Program
{
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
var form = new Form();
var dg = new DataGridView { Dock = DockStyle.Fill, Parent = form };
dg.Bind(GetData());
Application.Run(form);
}
static DataTable GetData()
{
var dt = new DataTable();
dt.Columns.AddRange(new[]
{
new DataColumn("Id", typeof(int)),
new DataColumn("Name"),
new DataColumn("Description"),
new DataColumn("StartDate", typeof(DateTime)),
new DataColumn("EndDate", typeof(DateTime)),
});
dt.Rows.Add(1, "Foo", "Bar", DateTime.Today, DateTime.Today.AddDays(7));
return dt;
}
}
