在进行数据分析、知识发现和趋势分析等任务时,经常需要处理结构化但未定义的数据集。在这种情况下,需要一种通用的方法来展示任何类型的数据集。本文将介绍如何在WPF中处理这种情况,特别是当ListView的列在运行时才能确定时。
在WPF中,数据绑定是一个强大的功能,它允许UI元素与数据源进行交互。然而,当数据列在运行时才能确定时,传统的数据绑定方法可能无法直接应用。为了解决这个问题,可以使用一个名为DataMatrix的自定义数据结构。
首先,需要定义DataMatrix类,它将作为数据源。DataMatrix类需要实现IEnumerable接口,以便能够被ListView绑定。
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的属性。
为了在运行时将DataMatrix绑定到ListView,需要创建一个依赖属性。这个属性将被添加到ListView中,以便在设置DataMatrixSource时,可以将未定义的列绑定到ListView。
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;
gridView.Columns.Clear();
foreach (var col in dataMatrix.Columns)
{
gridView.Columns.Add(new GridViewColumn
{
Header = col.Name,
DisplayMemberBinding = new Binding(string.Format("[{0}]", count))
});
count++;
}
}
}
这个依赖属性将被附加到ListView,如下所示:
<ListView cc:ListViewExtension.MatrixSource="{Binding MyDataMatrix}" />
这样,在运行时,列将被动态添加到ListView的GridView中。
为了演示如何使用DataMatrix,可以使用一个通用的Northwind数据库,并将其绑定到Orders表(按月/年计算的订单数量)。然后,可以构建一个简单的DataMatrix,通过交叉表的方式将年份(行源)与月份(列源)与订单数量的总和进行对比。
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,如下所示:
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;
}
然后,可以使用这个结果集在运行时动态绑定到ListView。