簡體   English   中英

如何為用作DataGrid的ItemsSource的項目的collection屬性中的每個項目生成和綁定列

[英]How to generate and bind a column for each item in the collection property of items used as DataGrid's ItemsSource

我的數據網格的項源是對象的集合,例如:

Public Property Quarter As Integer
Public Property MyColumns() As New List(Of MyColumn)

現在我想以某種方式使網格綁定,使最終的網格看起來像

-季度-欄1-欄2-欄3 ....欄X

數據源中的所有項目將具有相同的MyColumns。

有沒有一種方法可以將集合綁定到網格列?

這是解決方案。 它不是最漂亮或最簡單的,但它是可配置的。 它主要基於WPF的想法:DataGrid中的Dictionary <int,List <string >> ,只是變成了更通用的版本,帶有一些Expressions和Reflection。

無論用作ItemsSource的項目在相應的collection屬性中包含的項目數量相同或不同,它都將起作用。


任意長度

以下是必需的組件:

using System.Reflection;
using Expressions = System.Linq.Expressions;

// See - https://stackoverflow.com/questions/2132791/reflecting-over-all-properties-of-an-interface-including-inherited-ones
public static class ReflectionExtensions
{
    public static PropertyInfo GetInterfaceProperty(this Type type, String propName, Type returnType)
    {
        if (propName == null)
            throw new ArgumentNullException("propName");
        if (returnType == null)
            throw new ArgumentNullException("propType");

        return type.GetInterfaces()
            .Select(parentInterface =>
                parentInterface.GetProperty(propName, returnType))
            .Where(prop =>
                prop != null)
            .Single();
    }
}

public static class CollectionPropertyDataGridBindingHelper
{
    public static void RemoveAutoGeneratedColumns(this DataGrid dataGrid, String propertyName)
    {
        if (dataGrid == null)
            throw new ArgumentNullException("dataGrid");
        if (propertyName == null)
            throw new ArgumentNullException("propertyName");

        var autogeneratedColumns = dataGrid
            .Columns
            .OfType<DataGridBoundColumn>()
            .Where(col =>
                (col.Binding as Binding).Path.Path.Equals(propertyName));

        foreach (var autoColumn in autogeneratedColumns)
        {
            dataGrid.Columns.Remove(autoColumn);
        }
    }


    public static void RegenerateColumns<TItem, TPropertyCollectionItem>(
        this DataGrid dataGrid,
        Expressions.Expression<Func<TItem, IEnumerable<TPropertyCollectionItem>>> propertyExpression, 
        IEnumerable<TItem> items)
    {
        RegenerateColumns<TItem, TPropertyCollectionItem>(dataGrid,
            propertyExpression,
            items,
            (index) =>
                String.Format("Column - {0}", index));
    }


    public static void RegenerateColumns<TItem, TPropertyCollectionItem>(
        this DataGrid dataGrid, 
        Expressions.Expression<Func<TItem, IEnumerable<TPropertyCollectionItem>>> collectionPropertyExpression, 
        IEnumerable<TItem> items, 
        Func<Int32, String> formatHeader)
    {
        if (dataGrid == null)
            throw new ArgumentNullException("dataGrid");
        if (collectionPropertyExpression == null)
            throw new ArgumentNullException("propertyExpression");
        if (items == null)
            throw new ArgumentNullException("items");
        if (formatHeader == null)
            throw new ArgumentNullException("formatHeader");

        var collectionPropInfo = GetCollectionPropertyInfoFor<TItem, TPropertyCollectionItem>(collectionPropertyExpression);
        var propertyName = collectionPropInfo.Name;
        var getCount = GetCountGetter<TItem, TPropertyCollectionItem>(
            collectionPropertyExpression.Compile(),
            collectionPropInfo);

        // Remove old autocolumns
        dataGrid.RemoveAutoGeneratedColumns(propertyName);

        Int32 columnsRequired = items.Select(item => getCount(item)).Max();

        // Create new columns
        GenerateColumns(dataGrid,
            formatHeader,
            propertyName,
            columnsRequired);
    }    

    private static void GenerateColumns(DataGrid dataGrid,
        Func<Int32, String> formatHeader,
        String propertyName,
        Int32 columnsRequired)
    {
        for (int columnNumber = 0; columnNumber < columnsRequired; columnNumber++)
        {
            DataGridTextColumn column = new DataGridTextColumn()
            {
                Header = formatHeader(columnNumber),
                Binding = new Binding(String.Format("{0}[{1}]",
                    propertyName,
                    columnNumber))
            };

            dataGrid.Columns.Add(column);
        }
    }


    private static Func<TItem, Int32> GetCountGetter<TItem, TPropertyCollectionItem>(
        Func<TItem, IEnumerable<TPropertyCollectionItem>> getCollection,
        PropertyInfo propInfo)
    {
        if (getCollection == null)
            throw new ArgumentNullException("getCollection");
        if (propInfo == null)
            throw new ArgumentNullException("propInfo");

        var collectionType = propInfo.PropertyType;

        var countGetter = collectionType.GetInterfaceProperty("Count",
            typeof(Int32));

        if (countGetter != null)
        {
            return (item) =>
                (Int32)countGetter.GetMethod.Invoke(getCollection(item), null);
        }

        throw new NotImplementedException("Not implemented: For simple IEnumerables the use of Enumerable.Count() method shall be considered.");
    }


    private static PropertyInfo GetCollectionPropertyInfoFor<TItem, TPropertyCollectionItem>(
        Expressions.Expression<Func<TItem, 
        IEnumerable<TPropertyCollectionItem>>> propertyExpression)
    {
        if (propertyExpression == null)
            throw new ArgumentNullException("propertyExpression");

        var memberExp = propertyExpression.Body as Expressions.MemberExpression;
        if (memberExp == null)
            throw new ArgumentNullException("propertyExpression");

        var propInfo = memberExp.Member as PropertyInfo;
        if (propInfo == null)
            throw new ArgumentNullException("propertyExpression");

        if (!propInfo.DeclaringType.IsAssignableFrom(typeof(TItem)))
            throw new ArgumentException("propertyExpression");

        return propInfo;
    }
}

這是XAML:

    <DataGrid ItemsSource="{Binding Items}" AutoGenerateColumns="False" Name="dataGrid">
        <DataGrid.Columns>
            <DataGridTextColumn Header="Quarter" Binding="{Binding Quarter}"/>
        </DataGrid.Columns>
    </DataGrid>

這是背后的代碼:

using Expressions = System.Linq.Expressions

public class Item
{
    public Item(Int32 quarter, Int32 repeatColumns)
    {
        this.Quarter = quarter;
        this.MyColumns = Enumerable
            .Range(1, repeatColumns)
            .ToList();
    }

    public Int32 Quarter
    {
        get; set;
    }

    public IList<Int32> MyColumns
    {
        get; set;
    }
}


/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();

        this.Items = GetOriginalItems();

        this.DataContext = this;

        this.ReinitializeColumns();
    }

    private void ReinitializeColumns()
    {
        Expressions.Expression<Func<Item, IEnumerable<Int32>>> exp = 
            obj => 
                obj.MyColumns;

        this.dataGrid.RegenerateColumns(exp,
            this.Items);
    }

    public IEnumerable<Item> Items
    {
        get;
        private set;
    }

    public IEnumerable<Item> GetOriginalItems()
    {
        return new Item[] 
        {
            new Item(1, 3),
            new Item(2, 2),
            new Item(3, 5),
            new Item(4, 2),
        };
    }
}

設定長度

這是將創建指定數量的列的代碼(您可以將其放入一些獨立的類中,因為它是完全獨立的,或者可以將其放入具有任意長度方法的同一類中(在這種情況下,請不要忘記刪除復制生成方法))。 它有點簡單,可以直接滿足您的需求:

    public static void RegenerateColumns<TItem, TPropertyCollectionItem>(
       this DataGrid dataGrid,
       String propertyName,
       Int32 columnsRequired)
    {
        dataGrid.RegenerateColumns<TItem, TPropertyCollectionItem>(propertyName,
            columnsRequired,
            index => String.Format("Column - {0}",
                index));
    }


    public static void RegenerateColumns<TItem, TPropertyCollectionItem>(
        this DataGrid dataGrid,
        String propertyName,
        Int32 columnsRequired,
        Func<Int32, String> formatHeader)
    {
        if (dataGrid == null)
            throw new ArgumentNullException("dataGrid");
        if (propertyName == null)
            throw new ArgumentNullException("propertyName");
        if (columnsRequired < 0)
            throw new ArgumentOutOfRangeException("columnsRequired");
        if (formatHeader == null)
            throw new ArgumentNullException("formatHeader");

        // Remove old autocolumns
        dataGrid.RemoveAutoGeneratedColumns(propertyName);

        GenerateColumns(dataGrid,
            formatHeader,
            propertyName,
            columnsRequired);
    }


    private static void GenerateColumns(DataGrid dataGrid,
        Func<Int32, String> formatHeader,
        String propertyName,
        Int32 columnsRequired)
    {
        for (int columnNumber = 0; columnNumber < columnsRequired; columnNumber++)
        {
            DataGridTextColumn column = new DataGridTextColumn()
            {
                Header = formatHeader(columnNumber),
                Binding = new Binding(String.Format("{0}[{1}]",
                    propertyName,
                    columnNumber))
            };

            dataGrid.Columns.Add(column);
        }
    }

它是使用它的背后代碼:

    public MainWindow()
    {
        InitializeComponent();

        this.Items = GetOriginalItems();

        this.DataContext = this;

        this.ReinitializeColumns(2);
    }

    private void ReinitializeColumns(Int32 columnsCount)
    {
        this.dataGrid.RegenerateColumns<Item, Int32>("MyColumns",
            columnsCount);
    }

Eugene對動力學和expandoObject給予了我深刻的見識,這就是我為解決當前問題而實現的方法。

這是我的解決方案-

我們有來自模型的收藏,它的結構相當嚴格。 因此,要將其綁定到UI網格,我們需要另一個對象,該對象以視覺上不同的方式映射當前列表。

-使用Expando對象-

您可以創建ExpandoObjects的集合,並將其動態屬性映射到您的集合的屬性。

Dim pivotItems As Object = New ExpandoObject()
    ' set properties for object'
     pivotItems.Quarter = developmentQuarters.Key
     pivotItems.Name = developmentQuarters.Key.Name

     ' since expando obj is a dictionary for prop name and its value we can set property names dynamically like this'
     For Each developmentModel As DevelopmentModel In developmentModels
         Dim pivotAsDict As IDictionary(Of String, Object) = pivotItems
         pivotAsDict.Add(developmentModel.BusinessGroupName + " " + developmentModel.Currency.Code, developmentModel.DevelopmentPercentage)
      Next
      ReModelledItems.Add(pivotItems)

因此,現在我們將嵌套對象展平為一個簡單的集合,該集合具有基於初始集合中的值生成的動態列/屬性。

我們現在可以簡單地綁定ExpandoObjects的此集合

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

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