簡體   English   中英

在 DataGridView 中顯示導航屬性的屬性(二級屬性)

[英]Show Properties of a Navigation Property in DataGridView (Second Level Properties)

我正在嘗試在應用程序中的DataGridView上顯示來自相關實體的多個屬性。 這對我來說似乎很普通,但我很難找到例子。 這是一個訂單輸入操作。 OrderSheet 數據、訂單的 ID 和取貨日期,然后是網格中的行項目(以下模型中的 OrderSheetItems)。 訂單行項目具有基於 ProductId 的導航屬性 Product。 我可以使用 DataGridViewComboBoxColumn,其中 ProductId 作為 ValueMember,另一個字段作為 DisplayMember。 但我想在其他列、尺寸、顏色、材料等中包含更多數據。

帶有相關產品的 OrderSheet 和 OrderSheetItems

訂單輸入

這是加載數據的代碼

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,邁克

您可以使用以下任一選項:

  1. 使用DataGridViewComboBoxColumn
  2. 為子實體部分類添加相應的屬性
  3. 使用Linq調整查詢以包含導航屬性的屬性
  4. 使用CellFormatting事件獲取子屬性有界列的值
  5. 通過覆蓋ToString()顯示對象的字符串表示
  6. 使用自定義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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM