65.9K
CodeProject 正在变化。 阅读更多。
Home

将 ListView 绑定到数据矩阵

starIconstarIconstarIconstarIconstarIcon

5.00/5 (22投票s)

2009 年 5 月 15 日

MIT

3分钟阅读

viewsIcon

176698

downloadIcon

4042

将 WPF ListView 绑定到运行时确定的列的数据矩阵。

DynamicListView

引言

本文主要展示如何将WPF ListView绑定到一个DataMatrix(一个具有动态列的未定义数据源),其中ListView的列在运行时才能确定。

本文假设读者已经具备WPF数据绑定和依赖属性的先验知识。有关这些主题的更多信息,请查看CodeProject上的以下文章。

背景

我的大部分工作包括处理未定义(但结构化)的数据集,并生成有意义的信息和趋势 - 即数据分析、知识发现和趋势分析。 因此,我获得的大部分数据实际上并不相似。 在这种情况下,您需要想出一种通用方法来处理基本的东西,例如为任何类型的数据集进行数据演示(报告)。 这促使我在很久以前创建了一个DataMatrix。 但众所周知,在 WPF 中处理匿名数据并不是很明显。

我知道我可以只使用一个常规的DataTable并将其绑定到 WPF DataGrid,但我可以向您保证,当您处理数百万条无法虚拟化的记录时,DataTable会变得非常重,占用大量内存,并导致太多混乱的代码[在背后],到处都是“Magic”字符串。 此外,我们必须尽可能保持 M-V-VM

Using the Code

基本上,我们将要做的是将 ListView 绑定到如下所示的 DataMatrix

public class DataMatrix : IEnumerable
{
    public List<MatrixColumn> Columns { get; set; }
    public List<object[]> Rows { get; set; } 

    IEnumerator IEnumerable.GetEnumerator()
    {
        return new GenericEnumerator(Rows.ToArray());
    }
}

public class MatrixColumn
{
    public string Name { get; set; }
    public string StringFormat { get; set; }
}

请注意MatrixColumn 始终可以使用您需要格式化 GridViewColumn 的所有属性进行扩展。

为了实现这一点,我们需要创建一个 DependencyProperty,它将被添加到 ListView 中,以便在设置 DataMatrixSource 时,我们可以将未定义的列在运行时绑定到 ListView。 以下是包含 DependencyProperty 的类。

public class ListViewExtension
{
    public static readonly DependencyProperty MatrixSourceProperty =
        DependencyProperty.RegisterAttached("MatrixSource",
        typeof(DataMatrix), typeof(ListViewExtension),
            new  FrameworkPropertyMetadata(null,
                new  PropertyChangedCallback(OnMatrixSourceChanged)));

    public static DataMatrix GetMatrixSource(DependencyObject d)
    {
        return (DataMatrix)d.GetValue(MatrixSourceProperty);
    }

    public static void SetMatrixSource(DependencyObject d, DataMatrix value)
    {
        d.SetValue(MatrixSourceProperty, value);
    }

    private static void OnMatrixSourceChanged(DependencyObject d,
        DependencyPropertyChangedEventArgs e)
    {
        ListView listView = d as ListView;
        DataMatrix dataMatrix = e.NewValue as DataMatrix;

        listView.ItemsSource = dataMatrix; 
        GridView gridView = listView.View as GridView; 
        int count = 0; 
        gridView.Columns.Clear();
        foreach (var col in dataMatrix.Columns)
        {
            gridView.Columns.Add(
                new  GridViewColumn
                    {
                        Header = col.Name,
                        DisplayMemberBinding = new Binding(stringM.Format("[{0}]", count))
                    }); 
            count++;
        }
    }
}

然后将其附加到 ListView,如下所示

<ListView cc:ListViewExtension.MatrixSource="{Binding MyDataMatrix}"/>

就是这样。 在运行时,您的列将动态添加到 ListViewGridView请注意ListViewItemsSource 属性需要一个 IEnumerable 作为绑定元素; 这就是为什么矩阵实现这个接口。 这由下面显示的 GenericEnumerator 提供

class GenericEnumerator : IEnumerator
{
    private readonly object[] _list;
    // Enumerators are positioned before the first element
    // until the first MoveNext() call. 

    private int _position = -1;

    public GenericEnumerator(object[] list)
    {
        _list = list;
    }

    public bool MoveNext()
    {
        _position++;
        return (_position < _list.Length);
    }

    public void Reset()
    {
        _position = -1;
    }

    public object Current
    {
        get
        {
            try { return _list[_position]; }
            catch (IndexOutOfRangeException) { throw new InvalidOperationException(); }
        }
    }
}

对于此示例,我使用了通用的 *Northwind* 数据库并绑定到 Orders 表(每月/每年的订单计数); 然后我从年份(行源)与月份(列源)的交叉制表中构建了一个简单的 DataMatrix,用于计算订单数量的总和。

给定 *Orders* 表,您可以生成并绑定到一个众所周知的数据集,以获得“每月订单数”,如下所示

var orders = from o in db.Orders
 group o by new { o.OrderDate.Value.Year, o.OrderDate.Value.Month }
     into g
     select new MonthlyOrderCount() { Year = g.Key.Year, 
            Month = g.Key.Month, NumberOfOrders = g.Count() };

这将导致如下所示

为了从上面的查询中获得更有意义的数据集,我们需要交叉制表年份与月份,以获得订单总数。 因此,我们可以通过报告算法运行数据集并生成一个 DataMatrix,如下所示

注意:这是一个假的 DataMatrixGenerator。 真正的生成器将根据指定的参数(在运行时)确定列。

private static DataMatrix CreateMatrix(IEnumerable<monthlyordercount> orders)
{
    DataMatrix result = new DataMatrix {Columns = new List<matrixcolumn>(), 
                                        Rows = new List<object[]>()};
    result.Columns.Add(new MatrixColumn() { Name = "Year" });
    for (int i = 0; i < 12; i++)
        result.Columns.Add(new MatrixColumn()
        {
            Name = string.Format("{0:MMM}", new DateTime(2009, i + 1, 1))
        });
    for (int i = 1996; i < 1999; i++)
    {
        object[] row = new object[13];
        row[0] = i;
        for (int j = 1; j <= 12; j++)
        {
            int count = (from o in orders
                         where o.Year == i && o.Month == j
                         select o.NumberOfOrders).Sum();
            row[j] = count;
        }
        result.Rows.Add(row);
    }
    return result;
}

这将导致如下所示

然后,我们可以在运行时使用此结果集,使用 DataMatrix 动态绑定到列表视图。

关注点

这种方法也可用于生成流动文档。

历史

  • 2009 年 5 月 15 日:初始版本。
© . All rights reserved.