绑定对象 DataGridView C#
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/16869568/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Binding objects DataGridView C#
提问by maxiruani
i have a DataGridView and a list of objects that i would like to show.
我有一个 DataGridView 和一个我想显示的对象列表。
The Objects are these:
对象是这些:
public class Entity
{
public int ID { get; set; }
}
public class Travel: Entity
{
public Service Service { get; set; }
public City Source { get; set; }
public City Destiny { get; set; }
public decimal Price { get; set; }
}
public class Service: Entity
{
public string Name { get; set; }
}
public class City: Entity
{
public string Name { get; set; } // Max 50 chars
}
In my form i bind the list of of Travel Objects like this:
在我的表单中,我绑定了这样的旅行对象列表:
List<Travel> travels = logic.GetAllTravels();
DgvRecorridos.DataSource = travels;
And i get the following:
我得到以下信息:


I would like to get the Name of the Service, Source City and Destiny City instead.
我想获取服务名称、源城和命运城。
Thanks in advance.
提前致谢。
回答by John Woo
List<Travel> travels = logic.GetAllTravels();
var _bind = from a in travels
select new
{
Servicename = a.Service.Name,
SourceName = a.Source.Name,
DestinyName = a.Destiny.Name,
Price = a.Price
};
DgvRecorridos.DataSource = _bind;
or
或者
List<Travel> travels = logic.GetAllTravels();
var _bind = travels.Select(a => new
{
Servicename = a.Service.Name,
SourceName = a.Source.Name,
DestinyName = a.Destiny.Name,
Price = a.Price
});
DgvRecorridos.DataSource = _bind;
回答by King King
Your design is so strange. I have another approach is to override ToString()method of your classes (Service and City) like this:
你的设计太奇怪了。我还有另一种方法是ToString()像这样覆盖你的类(服务和城市)的方法:
public class Service: Entity
{
public string Name { get; set; }
public override string ToString(){
return Name;
}
}
public class City: Entity
{
public string Name { get; set; } // Max 50 chars
public override string ToString(){
return Name;
}
}
And that works OK. Again, your design is a little strange ^_^
这工作正常。再说一遍,你的设计有点奇怪^_^
回答by devpro101
Instead of doing the following codes below:
而不是执行以下代码:
List<Travel> travels = logic.GetAllTravels();
DgvRecorridos.DataSource = travels;
Do this:
做这个:
List<Travel> travels = logic.GetAllTravels();
BindingSource bs = new BindingSource();
bs.DataSource = travels;
DgvRecorridos.AutoGenerateColumn = false;
DgvRecorridos.DataSource = bs;
Then, add the columns manually:
然后,手动添加列:
DataGridViewColumn col1 = new DataGridViewTextBoxColumn();
col1.DataPropertyName = "Service.Name";
col1.HeaderText = "Service Name";
dataGridView1.Columns.Add(col1);
DataGridViewColumn col2 = new DataGridViewTextBoxColumn();
col2.DataPropertyName = "City.Name";
col2.HeaderText = "City Name";
dataGridView1.Columns.Add(col2);
DataGridViewColumn col3 = new DataGridViewTextBoxColumn();
col3.DataPropertyName = "City.Name";
col3.HeaderText = "Destiny Name";
dataGridView1.Columns.Add(col3);
DataGridViewColumn col4 = new DataGridViewTextBoxColumn();
col4.DataPropertyName = "Price";
col4.HeaderText = "Price";
dataGridView1.Columns.Add(col4);
Then, add a cell formatting event handler for the DataGridView:
然后,为 DataGridView 添加一个单元格格式化事件处理程序:
private void dataGridView1_CellFormatting(object sender, DataGridViewCellFormattingEventArgs e)
{
if (dataGridView1.Rows[e.RowIndex].DataBoundItem != null &&
dataGridView1.Columns[e.ColumnIndex].DataPropertyName.Contains("."))
{
e.Value = BindProperty(dataGridView1.Rows[e.RowIndex].DataBoundItem,
dataGridView1.Columns[e.ColumnIndex].DataPropertyName);
}
}
private string BindProperty(object property, string propertyName)
{
string retValue = "";
if (propertyName.Contains("."))
{
PropertyInfo[] arrayProperties;
string leftPropertyName;
leftPropertyName = propertyName.Substring(0, propertyName.IndexOf("."));
arrayProperties = property.GetType().GetProperties();
foreach (PropertyInfo propertyInfo in arrayProperties)
{
if (propertyInfo.Name == leftPropertyName)
{
retValue = BindProperty(propertyInfo.GetValue(property, null),
propertyName.Substring(propertyName.IndexOf(".") + 1));
break;
}
}
}
else
{
Type propertyType;
PropertyInfo propertyInfo;
propertyType = property.GetType();
propertyInfo = propertyType.GetProperty(propertyName);
retValue = propertyInfo.GetValue(property, null).ToString();
}
return retValue;
}
For a complete guide of the cell formatting, browse hereon Antonio Bello'sblog, it's where I got the idea. ^_^ I also asked here on SO the same question two days ago, and got the same answers like you, and I know that it's not what you want to do too. Hope it helps you.
有关单元格格式的完整指南,请在Antonio Bello 的博客上浏览此处,这是我想到的地方。^_^ 两天前我也在这里问了同样的问题,得到了和你一样的答案,我知道这也不是你想做的。希望对你有帮助。
回答by user2105109
All that code in the CellFormatting and BindProperty methods seems excessive. I mean, something's got to basically do that, but I think it's already been done. I implement INotifyPropertyChanged in the object I want to bind to a grid row, and I put those objects into a BindingList. The BindingList is directly used as the datasource for the grid.
CellFormatting 和 BindProperty 方法中的所有代码似乎过多。我的意思是,有些事情必须基本上做到这一点,但我认为它已经完成了。我在要绑定到网格行的对象中实现 INotifyPropertyChanged,并将这些对象放入 BindingList。BindingList 直接用作网格的数据源。
This approach means a little more typing in the basic entity class you're mapping to the grid row but I think it saves a lot more elsewhere. To implement INotifyPropertyChanged in your class:
这种方法意味着在您映射到网格行的基本实体类中输入更多内容,但我认为它可以在其他地方节省更多。要在您的类中实现 INotifyPropertyChanged:
public class Entity: INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public Entity()
{
Selected = false;
}
private bool _selected;
public bool Selected
{
get
{
return _selected;
}
set
{
if (_selected != value)
{
_selected = value;
OnPropertyChanged(nameof(Selected));
}
}
}
protected virtual void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Then put a column in your grid and give it a DataPropertyName of "Selected" to match the name of the property in Entity that should appear in that column. Obviously add whatever properties you want to your grid, matching the properties of the entity. The key is to be sure to implement the property setter with the call to PropertyChanged.
然后在您的网格中放置一列并为其提供“Selected”的 DataPropertyName 以匹配应出现在该列中的实体中的属性名称。显然,将您想要的任何属性添加到网格中,匹配实体的属性。关键是确保通过调用 PropertyChanged 来实现属性设置器。
This gets you two-way binding between the grid and your list of objects.
这使您可以在网格和对象列表之间进行双向绑定。
My personal opinion: even this is way too much code. I find myself constantly writing these kinds of things that do the obvious: take a property by name and map it to something else that knows that name (like the grid column in this example). It's just beyond my comprehension why these things don't just automatically hook up. A list and grid should have enough sense to figure this basic arrangement on their own. zero lines of code. Ok, one line of code. Grid.Datasource = List. So this is how I do it. I'd love to know a less lines of code way to do this.
我的个人意见:即使是这样的代码也太多了。我发现自己一直在写这些显而易见的事情:按名称获取属性并将其映射到知道该名称的其他内容(如本例中的网格列)。我无法理解为什么这些东西不会自动连接起来。一个列表和网格应该有足够的意义来自己计算这个基本的安排。零行代码。好的,一行代码。Grid.Datasource = 列表。所以这就是我的做法。我很想知道用更少的代码行来做到这一点。

