Dynamically-generated GridView Binding in WPF
My boss loves to invent very unusual scenarios to code for, and this one has me stumped. I'm making a WPF application that has to use a GridView to display some very late-bound data. To display data, he wants to be able to first initialize a GridView with some column headers, which isn't a problem. Then, he needs to pass this GridView, a DataTable, and some formatting info into a method that binds the data.
At design time, we don't know what columns in the DataTable we want to bind to. We can't use XAML, because the entire grid is dynamically created. As we bind the data, we have to specify per-column formatting data. I've implemented this so far as an implementation of IValueConverter that takes the formatting class as a parameter in its constructor. The final twist is that it has to support displaying the same column in the data table multiple times, formatted in different ways. Obviously, you can't have multiple copies of a column in one DataTable because they have name collisions.
Here's what I have so far:
/// <summary>
/// Sets the view of the passed ListView to a new GridView with properly formatted headers.
/// The headers are also the field names (column names) of the data table that you will
/// bind to
/// </summary>
/// <param name="displayPanel"></param>
/// <param name="displayFieldList"></param>
public static void InitializeGrid(ListView displayPanel,IDisplayFieldList displayFieldList)
{
开发者_如何转开发 GridView gv = new GridView();
gv.AllowsColumnReorder = true;
for (int i = 0; i < displayFieldList.Count; i++)
{
GridViewColumn col = new GridViewColumn();
col.Header = displayFieldList[i].HeaderText;
gv.Columns.Add(col);
}
displayPanel.View = gv;
}
/// <summary>
/// Binds data to the columns set in the ListView.View. Displays and formats the data.
/// </summary>
/// <param name="displayPanel"></param>
/// <param name="table"></param>
/// <param name="displayFieldList"></param>
public static void BindData(ListView displayPanel, DataTable table, IDisplayFieldList displayFieldList)
{
for(int i=0; i<displayFieldList.Count; i++)
{
GridViewColumn gridCol = ((GridView)displayPanel.View).Columns[i];
DmlDisplayField displayField = displayFieldList[i] as DmlDisplayField;
if(displayField != null)
{
gridCol.DisplayMemberBinding = new Binding()
{
Source = table.Columns[displayField.DataField],
Converter = new MaskConverter(displayField.DataField),
ConverterParameter = displayField
};
}
else
{
gridCol.DisplayMemberBinding = new Binding()
{
Source = table.Columns[displayFieldList[i].Name]
};
}
}
displayPanel.ItemsSource = table.AsDataView();
}
IDisplayFieldList is a collection of DisplayField or DmlDisplayField classes which contain the metadata. The MaskConverter is an IValueConverter that formats a DmlDisplayField column in a variety of ways.
When I run this code, all the rows are blank, but I can select individual rows.
So far I've tried a bunch of different ways of binding and most give me an "Ambiguous Match Exception" from System.Reflection.
If you need me to post the MaskConverter, I will, but it's pretty messy...
Any ideas? Thanks a ton!
i've only done similar things with a winforms DataGridView, but i think the wpf GridView should also be capable of auto-creating its columns on the fly...
the ICustomTypeDescriptor Interface allows you to provide your own set of PropertyDescriptors to the grid. there is no need to bind two columns to the same property(descriptor) since you could provide 2 distinct propertydescriptors for the very same property/column/field/whatever ...
on the other hand, this concept might be overkill and there may be easier ways to do this, so it's just a suggestion
精彩评论