[英]Automatically create DataGridViewComboBoxCell from object's collection property
使用WinForms,我試圖編寫一種方法來檢查綁定到DataGridView中的行的數據項是否包含IList
作為屬性,然后自動將DataGridViewTextBoxCell
轉換為綁定到該列表的DataGridViewComboBoxCell
作為數據源。 目的是要有一個下拉菜單,每行的值都不同,這取決於找到的對象的list屬性中的元素。 因此,例如在第一行中,下拉列表可能具有3個ObjA
類型的ObjA
作為選項,第二行可能具有5個ObjC
類型的ObjC
,依此類推。
這就是我所擁有的:
private void dgv_DataBindingComplete(object sender, DataGridViewBindingCompleteEventArgs e)
{
foreach (DataGridViewRow row in dgv.Rows)
{
object obj = row.DataBoundItem;
if (obj != null)
{
IEnumerable listProperties = obj.GetType().GetProperties().Where(p => p.GetValue(obj) is IList);
foreach (PropertyInfo list in listProperties)
{
DataGridViewComboBoxCell cell = new DataGridViewComboBoxCell();
IList source = (IList)list.GetValue(obj, null);
cell.DataSource = source;
cell.ValueMember = "id";
cell.DisplayMember = "name";
cell.ValueType = source.GetType().GetProperty("Item").PropertyType;
cell.Value = source[0].GetType().GetProperty("id").GetValue(source[0]);
(row.Cells[list.Name]) = cell;
}
}
}
}
這是將初始數據綁定到DataGridView
的代碼:
IBindingList source = new BindingList<Models.InputValue>(datasource);
inputsDgv.AutoGenerateColumns = true;
inputsDgv.AllowUserToAddRows = false;
inputsDgv.AllowUserToDeleteRows = false;
inputsDgv.DataSource = source;
問題:加載DataGridView時出現“ DataGridViewComboBoxCell值無效”錯誤。 我注意到在運行該行之后(row.Cells[list.Name]) = cell;
, cell.ValueType
從System.Collections.Generic.IList1[[roco.Models.ISnapshots]]
更改為System.Int32
。 我認為那一定是問題所在。
有誰知道我該如何解決這個錯誤?
謝謝!
PS: row.DataBoundItem
的類型為InputValue
,列表屬性為ProjectionSnapshot
對象的集合
public class InputValue : IValues
{
private int _id;
public int id { get { return _id; } }
private IList<ISnapshots> _snapshots;
public IList<ISnapshots> snapshots
{
get { return _snapshots; }
set { _snapshots = value; }
}
}
public class ProjectionSnapshot : ISnapshots
{
private int _id;
public int id { get { return _id; } }
private string _name;
public string name
{
get { return _name; }
set
{
if (value.Length > 255)
Console.WriteLine("Error! SKU snapshot name must be less than 256 characters!");
else
_name = value;
}
}
}
public interface ISnapshots
{
int id { get; }
string name { get; set; }
}
TL; DR:跳到“ 解決方案”部分。
更改DataGridViewTextBoxCell
成DataGridViewComboBoxCell
是可能的,但會造成問題,如果組合框的值有不同的類型不是ValueType
的列集。 這是因為,如@Loathing和@Mohit Shrivastava所述, DataGridViewColumns
與對應的Cell類緊密耦合。
閱讀@Loathing的代碼示例后,我嘗試在對DataGridViewTextBoxCell
進行更改之前將Column的ValueType
設置為typeof(Object)
。 這是行不通的,因為當您將對象綁定到DataGridView
並使用AutoGenerateColumns
有一種機制會自動設置Column的ValueType
以反映綁定對象中屬性的類型。 如果您生成自己的列並將Column的DataPropertyName
設置為對象屬性名稱,情況也是如此。
解決方案 :
1)生成自己的列映射對象屬性到DataGridViewTextBoxCell
但不至DataGridViewComboBoxCell
:
PS:我不再檢查IList,而是檢查任何IEnumerable(有關更多詳細信息,請參見此答案: https : //stackoverflow.com/a/9434921/5374324 )
public void loadGrid<T>(IList<T> datasource)
{
generateDataGridViewColumns<T>(datasource);
IBindingList source = new BindingList<T>(datasource);
inputsDgv.AutoGenerateColumns = false;
inputsDgv.AllowUserToAddRows = false;
inputsDgv.AllowUserToDeleteRows = false;
inputsDgv.DataSource = source;
}
private void generateDataGridViewColumns<T>(IList<T> datasource)
{
dgv.Columns.Clear();
if (datasource != null)
{
foreach (PropertyInfo property in typeof(T).GetProperties())
{
DataGridViewColumn col;
var displayNameObj = property.GetCustomAttributes(typeof(DisplayNameAttribute), true).Cast<DisplayNameAttribute>().FirstOrDefault();
string displayName = (displayNameObj == null) ? property.Name : displayNameObj.DisplayName;
if (property.PropertyType.GetInterface(typeof(IEnumerable<>).FullName) != null && property.PropertyType != typeof(string))
{
col = new DataGridViewComboBoxColumn();
(col as DataGridViewComboBoxColumn).AutoComplete = false;
(col as DataGridViewComboBoxColumn).ValueType = typeof(Object);
}
else
{
col = new DataGridViewTextBoxColumn() { DataPropertyName = property.Name };
}
col.Name = property.Name;
col.HeaderText = displayName;
ReadOnlyAttribute attrib = Attribute.GetCustomAttribute(property, typeof(ReadOnlyAttribute)) as ReadOnlyAttribute;
col.ReadOnly = (!property.CanWrite || (attrib != null && attrib.IsReadOnly));
inputsDgv.Columns.Add(col);
}
}
}
2)使用DataBindingComplete事件來填充組合框:
private void dgv_DataBindingComplete(object sender, DataGridViewBindingCompleteEventArgs e)
{
foreach (DataGridViewRow row in dgv.Rows)
{
object obj = row.DataBoundItem;
if (obj != null)
{
IEnumerable listProperties = obj.GetType().GetProperties().Where(p => p.GetValue(obj) is IList);
foreach (PropertyInfo list in listProperties)
{
IList source = (IList)list.GetValue(obj, null);
DataGridViewComboBoxCell cell = (row.Cells[list.Name] as DataGridViewComboBoxCell);
cell.DataSource = source;
cell.ValueType = source.GetType().GetProperty("Item").PropertyType;
ValueMember valueMember = (ValueMember)obj.GetType().GetProperty(list.Name).GetCustomAttribute(typeof(ValueMember));
DisplayMember displayMember = (DisplayMember)obj.GetType().GetProperty(list.Name).GetCustomAttribute(typeof(DisplayMember));
if(valueMember != null && displayMember != null)
{
cell.ValueMember = valueMember.Value;
cell.DisplayMember = displayMember.Value;
}
cell.Value = source[0].GetType().GetProperty("id").GetValue(source[0]);
}
}
}
}
3)創建ValueMember和DisplayMember屬性類:
[System.AttributeUsage(System.AttributeTargets.Property)]
public class ValueMember : System.Attribute
{
public string Value { get; private set; }
public ValueMember(string valueMember)
{
this.Value = valueMember;
}
}
[System.AttributeUsage(System.AttributeTargets.Property)]
public class DisplayMember : System.Attribute
{
public string Value { get; private set; }
public DisplayMember(string displayMember)
{
this.Value = displayMember;
}
}
4)使用屬性:
public class InputValue
{
public string id{ get; set; }
public string name{ get; set; }
[DisplayName("Values")]
[ValueMember("id")]
[DisplayMember("name")]
public IList<IValue> values{ get; set; }
}
正如@Loathing所建議的, DataGridViewColumns與相應的Cell類緊密耦合。 在文本列中不能使用組合單元格。
您可以使用GridView ,該GridView用於按列排列數據,並為ListView添加布局和設計支持。 GridView用作ListView的補充控件,以提供樣式和布局。 GridView沒有與控件相關的屬性,例如背景和前景色,字體屬性,大小和位置。 容器ListView用於提供所有控件相關的屬性。 閱讀有關WPF中GridView的更多信息
如果要基於選定的行在下拉列表中具有不同的值,則將需要使用CellBeginEdit
事件並更改組合框編輯控件的DataSource
。
參見以下示例答案: 如何為每個記錄的DataGridViewComboBox分配不同的數據源?
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.