![](/img/trans.png)
[英]How to bind a Navigation Property (second level properties) in DataGridView using BindingSource?
[英]Show Properties of a Navigation Property in DataGridView (Second Level Properties)
我正在嘗試在winforms應用程序中的DataGridView
上顯示來自相關實體的多個屬性。 這對我來說似乎很普通,但我很難找到例子。 這是一個訂單輸入操作。 OrderSheet 數據、訂單的 ID 和取貨日期,然后是網格中的行項目(以下模型中的 OrderSheetItems)。 訂單行項目具有基於 ProductId 的導航屬性 Product。 我可以使用 DataGridViewComboBoxColumn,其中 ProductId 作為 ValueMember,另一個字段作為 DisplayMember。 但我想在其他列、尺寸、顏色、材料等中包含更多數據。
這是加載數據的代碼
try
{
_context.OrderSheets.Include(o => o.OrderSheetItems.Select(i => i.Product)).Load();
orderSheetBindingSource.DataSource = _context.OrderSheets.Local.ToBindingList();
}
catch (Exception ex)...
ProductId 位於單獨的列中,僅用於試驗,稍后將用作組合框。 那么有沒有辦法將其他列綁定到 OrderSheetItem 的 Product 導航屬性中的數據,或者我是否必須處理產品 id 上的 CellValueChanged 以物理設置其他列中的數據? 如果有一種方法可以綁定列,那么是通過 OnLoad 中的代碼還是網格視圖列設計器中的某個地方?
TIA,邁克
您可以使用以下任一選項:
DataGridViewComboBoxColumn
Linq
調整查詢以包含導航屬性的屬性CellFormatting
事件獲取子屬性有界列的值ToString()
顯示對象的字符串表示TypeDescriptor
啟用到子屬性的數據綁定。選項 1 - 使用 DataGridViewComboBoxColumn
用法:這種方法在您希望保持控件可編輯的情況下特別有用。
在這種方法中,您可以使用DataGridViewComboBoxColumn
來顯示導航屬性的任何字段。 要在網格中顯示導航屬性的多個字段子屬性,請使用多個綁定到具有不同DisplayMember
相同導航屬性的DataGridViewComboBoxColumn
在這種方法中,除了ProductId
列之外,還向網格添加更多DataGridViewComboBoxColumn
,然后為所有其他組合列執行這些設置:
DataPropertyName
設置為ProductId
DataSource
屬性設置為您用於主ProductId
列的完全相同的數據源,例如productBindingSource
ValueMember
設置為您為產品 ID 列設置的相同值成員,它是您的產品表的關鍵列。( ProductId
)DisplayMember
設置為要顯示的列,例如,將其中一個設置為 Name。 一個是價格,一個是尺寸,...... 這樣您就可以顯示相關的實體字段。ReadOnly
屬性設置為true
。 它使單元格只讀。DisplayStyle
屬性設置為Nothing
。 它刪除了下拉樣式。 如果要保持ProductId
可編輯,請將其DisplayStyle
保持為DropDownButton
。 這樣,當您使用組合框更改ProductId
列的值時,當您離開該行並移至下一行時,您將看到該行的其他單元格,顯示所選產品的其他屬性。 此外,由於其他組合框列是只讀的並且沒有組合框樣式,因此用戶無法更改它們的值,並且它們的作用就像只讀文本框列,顯示相關實體的其他屬性。
選項 2 - 為子實體部分類添加相應的屬性
用法:當您不需要編輯值時,此方法很有用。
在這種方法中,您可以在子實體部分類中定義屬性,返回父實體對應屬性的值。 例如對於產品名稱,在訂單項部分類中定義此屬性:
public string ProductName
{
get
{
if (this.Product != null)
return this.Product.Name;
else
return string.Empty;
}
}
然后您可以在選擇訂單項時簡單地包含產品並將網格列綁定到訂單項的相應屬性。
選項 3 - 調整查詢以包含導航屬性的屬性
用法:當您不需要編輯值時,此方法很有用。
您可以調整查詢以包含導航屬性的屬性。 您可以簡單地使用匿名對象或查看模式,例如:
var list = db.OrderDetails.Include("Products").Where(x=>x.OrderId==1)
.Select(x=> new OrderDetailVM() {
Id = x.Id,
ProductId = x.ProductId,
ProductName = x.Product.Name,
Price = x.Product.Price
}).ToList();
選項 4 - 使用 CellFormatting 事件獲取子屬性有界列的值
用法:當您不需要編輯值時,此方法很有用。
在這種方法中,您可以使用DataGridView
CellFormatting
事件。 您可以簡單地根據列索引設置e.Value
。 例如:
void dataGridView1_CellFormatting(object sender, DataGridViewCellFormattingEventArgs e)
{
//I Suppose you want to show product name in column at index 3
if(e.RowIndex>=0 && e.ColumnIndex==3)
{
var orderLineItem= (OrderLineItem)(this.dataGridView1.Rows[e.RowIndex]
.DataBoundItem);
if (order!= null && orderLineItem.Product != null)
e.Value = orderLineItem.Product.Name);
}
}
您可以使用不同的條件來處理不同的列並顯示不同的子屬性。
您還可以使用反射使其更加動態和可重用。 您可以使用反射提取導航屬性的子屬性的值。 為此,您應該創建列並將DataPropertyName
設置為Product.Name
等子屬性,然后在CellFormatting
事件中使用反射獲取列的值。 這是 Antonio Bello 關於這種方法的一篇好文章:
選項 5 - 通過覆蓋ToString()
顯示對象的字符串表示
用法:當您不需要編輯值時,此方法很有用。
如果您只想顯示單列導航屬性,您可以簡單地覆蓋導航屬性類的ToString()
方法並返回合適的值。 這樣,當在網格中顯示該類型的屬性時,您將看到一個友好的文本。 例如在Product
部分類中,您可以編寫:
public override string ToString()
{
return this.Name;
}
選項 6 - 使用自定義 TypeDescriptor 啟用數據綁定到子屬性
用法:當您不需要編輯值時,此方法很有用。
在這種方法中,您可以創建一個自定義 TypeDescriptor,使您能夠對二級屬性執行數據綁定。 這是 Linda Liu 關於這種方法的一篇好文章:
使用 CellFormatting 和 CellParsing 在 DataGridView 中顯示和編輯嵌套屬性
特征:
這個怎么運作:
DataPropertyName
中將被視為嵌套屬性。CellFormatting
事件以獲取嵌套屬性的值。CellParsing
事件將被處理以使用遞歸函數設置嵌套屬性的值。以下是方法:
public object GetPropertyValue(object source, string name)
{
if (name.Contains("."))
{
var nameParts = name.Split(new[] { '.' }, 2);
return GetPropertyValue(GetPropertyValue(source, nameParts[0]), nameParts[1]);
}
else
{
var property = TypeDescriptor.GetProperties(source)[name];
return property.GetValue(source);
}
}
public void SetPropertyValue(object source, string name, object value)
{
if (name.Contains("."))
{
var nameParts = name.Split(new[] { '.' }, 2);
SetPropertyValue(GetPropertyValue(source, nameParts[0]), nameParts[1], value);
}
else
{
var property = TypeDescriptor.GetProperties(source)[name];
property.SetValue(source, value);
}
}
這是事件處理程序:
private void CellFormatting(object sender, DataGridViewCellFormattingEventArgs e)
{
if (e.ColumnIndex < 0 || e.RowIndex < 0) return;
var dg = (DataGridView)sender;
var propertyName = dg.Columns[e.ColumnIndex].DataPropertyName;
if (propertyName.Contains("."))
{
var dataObject = dg.Rows[e.RowIndex].DataBoundItem;
e.Value = GetPropertyValue(dataObject, propertyName);
}
}
private void CellParsing(object sender, DataGridViewCellParsingEventArgs e)
{
var dg = (DataGridView)sender;
var propertyName = dg.Columns[e.ColumnIndex].DataPropertyName;
if (propertyName.Contains("."))
{
var dataObject = dg.Rows[e.RowIndex].DataBoundItem;
SetPropertyValue(dataObject, propertyName, e.Value);
}
}
這是示例:
var categories = new List<Category>() {
new Category{ Id= 1, Name = "C1"},
new Category{ Id= 2, Name = "C2"}
};
var products = new List<Product>() {
new Product(){ Id = 1, Name ="P1", Category = categories[0]},
new Product(){ Id = 2, Name ="P2", Category = categories[0]},
new Product(){ Id = 3, Name ="P3", Category = categories[1]},
};
var dg = new DataGridView();
dg.AutoGenerateColumns = false;
dg.Columns.Add(new DataGridViewTextBoxColumn()
{
HeaderText = "Id",
DataPropertyName = "Id"
});
dg.Columns.Add(new DataGridViewTextBoxColumn()
{
HeaderText = "Name",
DataPropertyName = "Name"
});
dg.Columns.Add(new DataGridViewTextBoxColumn()
{
HeaderText = "CategoryId",
DataPropertyName = "Category.Id"
});
dg.Columns.Add(new DataGridViewTextBoxColumn()
{
HeaderText = "CategoryName",
DataPropertyName = "Category.Name"
});
dg.Dock = DockStyle.Fill;
dg.DataSource = products;
this.Controls.Add(dg);
dg.CellFormatting += CellFormatting;
dg.CellParsing += CellParsing;
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.