将 ListView 绑定到数据矩阵





5.00/5 (22投票s)
将 WPF ListView 绑定到运行时确定的列的数据矩阵。
引言
本文主要展示如何将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}"/>
就是这样。 在运行时,您的列将动态添加到 ListView
的 GridView
。 请注意,ListView
的 ItemsSource
属性需要一个 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 日:初始版本。