问题描述
我试图通过 datagridview 组件中的列绑定我的对象,但我找不到任何方法来做到这一点.
这是我正在努力实现的一个例子.
我有Emp课程
公共类Emp{公共 int ID { 获取;放;}公共字符串名称 { 获取;放;}公共字符串城市{得到;放;}public Emp(int id, string name, string city){this.ID = id;this.Name = 名称;this.City = 城市;}}
和一组 Emp
var arrEmp = new[] {new Emp(1, Devesh Omar", Noida"),new Emp(2, "Roli", "Kanpur"),new Emp(3, Roli Gupta", Mainpuri"),new Emp(3, Roli Gupta", Kanpur"),new Emp( 3, "Devesh Roli", "Noida"),};
当我将数据绑定到网格时
dataGridView1.DataSource = arrEmp;
我明白了(没关系)
我希望网格只有 3 个固定行(Id、Name、City)和所有值的列.(矩阵转置)此外,如果我向/从 arrEmp 添加或删除元素,该元素将作为列添加.
示例取自
//设置数据源dgv.DataSource = new RotatedListDataSource(list);//隐藏列标题dgv.ColumnHeadersVisible = false;//设置行标题自动调整大小dgv.RowHeadersWidthSizeMode = DataGridViewRowHeadersWidthSizeMode.AutoSizeToAllHeaders;//在RowHeader上显示PropertyNamedgv.RowPrePaint += (o, a) =>{var value = ((RotatedItem)dgv.Rows[a.RowIndex].DataBoundItem).PropertyName;if (a.RowIndex > -1 && $"{dgv.Rows[a.RowIndex].HeaderCell.Value}" != value)dgv.Rows[a.RowIndex].HeaderCell.Value = value;};
如果你喜欢这个想法,以及为了学习目的尝试什么来了解类型描述符是如何工作的,这里是我创建的类型描述符和属性描述符的代码:
使用系统;使用 System.Collections.Generic;使用 System.ComponentModel;使用 System.Linq;公共类 RotatedListDataSource: 列表<RotatedItem>{公共列表<T>列表{获取;}公共 RotatedListDataSource(List list){列表 = 列表;this.AddRange(typeof(T).GetProperties().Select(p =>新的旋转项(p.名称,list.Cast().ToArray(),p.PropertyType)));}}公共类 RotatedItem : CustomTypeDescriptor{公共字符串 PropertyName { 获取;}私有对象[]数据;公共类型类型{获取;}public RotatedItem(string propertyName, object[] data, Type type){this.PropertyName = propertyName;this.data = 数据;this.Type = 类型;}公共覆盖 PropertyDescriptorCollection GetProperties(){返回 this.GetProperties(new Attribute[] { });}公共覆盖 PropertyDescriptorCollection GetProperties(Attribute[] 属性){var properties = new List();properties.Add(new PropertyNameProperty(new Attribute[] {新的 BrowsableAttribute(false)}));for (int i = 0; i < data.Length; i++){properties.Add(new IndexProperty(i, typeof(object), new Attribute[] { }));}返回新的 PropertyDescriptorCollection(properties.ToArray());}公共对象 this[int i]{得到 =>数据[i].GetType().GetProperty(PropertyName).GetValue(data[i]);设置 =>数据[i].GetType().GetProperty(PropertyName).SetValue(数据[i], Convert.ChangeType(value, Type));}}公共类 IndexProperty : PropertyDescriptor{整数索引;类型;公共索引属性(整数索引,类型类型,属性 [] 属性): base(index.ToString(), 属性){this.index = 索引;this.type = 类型;}公共覆盖类型 ComponentType =>typeof(RotatedItem);公共覆盖 bool IsReadOnly =>错误的;公共覆盖类型 PropertyType =>类型;public override bool CanResetValue(object component) =>错误的;公共覆盖对象 GetValue(对象组件) =>((RotatedItem)component)[index];公共覆盖无效重置值(对象组件){}public override void SetValue(object component, object value) =>((RotatedItem)component)[index] = value;public override bool ShouldSerializeValue(object component) =>真的;}公共类 PropertyNameProperty : PropertyDescriptor{公共 PropertyNameProperty(Attribute[] 属性): base(nameof(RotatedItem.PropertyName), 属性) { }公共覆盖类型 ComponentType =>typeof(RotatedItem);公共覆盖 bool IsReadOnly =>真的;公共覆盖类型 PropertyType =>类型(字符串);public override bool CanResetValue(object component) =>错误的;公共覆盖对象 GetValue(对象组件) =>((RotatedItem)component).PropertyName;公共覆盖无效重置值(对象组件){}public override void SetValue(object component, object value) { }public override bool ShouldSerializeValue(object component) =>真的;}
I am trying to bind my objects by columns in the datagridview component but I could not find any way to do it.
This is an example of what I am trying to achieve.
I have the Emp class
public class Emp
{
public int ID { get; set; }
public string Name { get; set; }
public string City { get; set; }
public Emp(int id, string name, string city)
{
this.ID = id;
this.Name = name;
this.City = city;
}
}
and an array of Emp
var arrEmp = new[] {
new Emp( 1, "Devesh Omar", "Noida"),
new Emp( 2, "Roli", "Kanpur"),
new Emp( 3, "Roli Gupta", "Mainpuri"),
new Emp( 3, "Roli Gupta", "Kanpur"),
new Emp( 3, "Devesh Roli ", "Noida"),
};
When I bind the data to the grid
dataGridView1.DataSource = arrEmp;
I get this (which is ok)
I would like that the grid has only 3 fixed rows (Id, Name, City) and the column all the values. (the matrix transpose)Also, if I add or remove an element to/from arrEmp, that the element will be added as a column.
The example was taken from here
There's not such a built-in way or built-in component for that.
You need to create your own component, or if you want to use DataGridView for that purpose, you can achieve it by writing custom code. Here I've achieved that using a custom TypeDescriptor.
Type descriptor provide information about type, including list of properties and getting and setting property values. DataTable
also works the same way, to show list of columns in DataGridView, it returns a list of property descriptors containing properties per column. Here I've used such technique.
As you can see in the screen capture, when you edit the rotated list, you are actually editing the original list:
//Set Data Source
dgv.DataSource = new RotatedListDataSource<Employee>(list);
//Hide Column Headers
dgv.ColumnHeadersVisible = false;
//Set Row Headers Autosize
dgv.RowHeadersWidthSizeMode = DataGridViewRowHeadersWidthSizeMode.AutoSizeToAllHeaders;
//Show PropertyName on RowHeader
dgv.RowPrePaint += (o, a) =>
{
var value = ((RotatedItem)dgv.Rows[a.RowIndex].DataBoundItem).PropertyName;
if (a.RowIndex > -1 && $"{dgv.Rows[a.RowIndex].HeaderCell.Value}" != value)
dgv.Rows[a.RowIndex].HeaderCell.Value = value;
};
If you liked the idea and what to give it a try for learning purpose to see how type descriptor works, here is the code for type descriptor and property descriptors that I created:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
public class RotatedListDataSource<T> : List<RotatedItem>
{
public List<T> List { get; }
public RotatedListDataSource(List<T> list)
{
List = list;
this.AddRange(typeof(T).GetProperties().Select(p =>
new RotatedItem(
p.Name,
list.Cast<object>().ToArray(),
p.PropertyType)));
}
}
public class RotatedItem : CustomTypeDescriptor
{
public string PropertyName { get; }
private object[] data;
public Type Type { get; }
public RotatedItem(string propertyName, object[] data, Type type)
{
this.PropertyName = propertyName;
this.data = data;
this.Type = type;
}
public override PropertyDescriptorCollection GetProperties()
{
return this.GetProperties(new Attribute[] { });
}
public override PropertyDescriptorCollection GetProperties(Attribute[] attributes)
{
var properties = new List<PropertyDescriptor>();
properties.Add(new PropertyNameProperty(new Attribute[] {
new BrowsableAttribute(false)}));
for (int i = 0; i < data.Length; i++)
{
properties.Add(new IndexProperty(i, typeof(object), new Attribute[] { }));
}
return new PropertyDescriptorCollection(properties.ToArray());
}
public object this[int i]
{
get => data[i].GetType().GetProperty(PropertyName).GetValue(data[i]);
set => data[i].GetType().GetProperty(PropertyName).SetValue(
data[i], Convert.ChangeType(value, Type));
}
}
public class IndexProperty : PropertyDescriptor
{
int index;
Type type;
public IndexProperty(int index, Type type, Attribute[] attributes)
: base(index.ToString(), attributes)
{
this.index = index;
this.type = type;
}
public override Type ComponentType => typeof(RotatedItem);
public override bool IsReadOnly => false;
public override Type PropertyType => type;
public override bool CanResetValue(object component) => false;
public override object GetValue(object component) =>
((RotatedItem)component)[index];
public override void ResetValue(object component) { }
public override void SetValue(object component, object value) =>
((RotatedItem)component)[index] = value;
public override bool ShouldSerializeValue(object component) => true;
}
public class PropertyNameProperty : PropertyDescriptor
{
public PropertyNameProperty(Attribute[] attributes)
: base(nameof(RotatedItem.PropertyName), attributes) { }
public override Type ComponentType => typeof(RotatedItem);
public override bool IsReadOnly => true;
public override Type PropertyType => typeof(string);
public override bool CanResetValue(object component) => false;
public override object GetValue(object component) =>
((RotatedItem)component).PropertyName;
public override void ResetValue(object component) { }
public override void SetValue(object component, object value) { }
public override bool ShouldSerializeValue(object component) => true;
}
这篇关于转置的可编辑 DataGridView的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!