Specifying and displaying columns of a DataGrid in WPF declaratively using a binding?
Is there a way to specify DataGrid columns declaratively using a binding? (And to set the columns' properties using this binding?) Idealy, I would have a list of objects (e.g. rows) databound to the ItemsSource and one of their properties would be a dictionary (or a list of objects of a certain class or whatever) with a name and a value. I would like the DataGrid to automatically create these additional columns without having some code behind. Is that even possible? Or how would you get around this?
The array holding the extra values can change over time but would be the same for all the items in the list.
It would be possible (and clean) to supply the DataGrid with a different list just for it to create the columns from. But for that I would need something like ColumnsSource or something...
The only thing that I could come开发者_运维技巧 up with was creating a subclass of the DataGrid...
Any ideas?
EDIT: The point is achieving this without any code behind...
At the risk of sounding controversial...
I think this is one example where the View really does need a bit of a code "boost", and the 'no code-behind' guideline can be put to one side - as long as you remember to keep your concerns separated.
I have, in the past, dynamically created DataGrid controls by grabbing the relevant data from the ViewModel, and writing a 'BuildDataGrid' method, similar to Partial's answer, in the code-behind. I believe this is justified, because the code was purely concerned with supplementing the View, and I did not mix concerns by having it implement business rules - it just put the columns together, and created the column Bindings, as appropriate.
But if it is more important to keep code-behind clean, then I would recommend your original thought of creating a User Control and using DP's to 'sub-class' the control.
In your XAML bind your datagrid to a ObservableCollection of objects of a certain class that has properties.
XAML:
<WpfToolkit:DataGrid
x:Name="MyDataGrid"
ItemsSource="{Binding Path=Collection}"
HorizontalScrollBarVisibility="Hidden" SelectionMode="Extended"
CanUserAddRows="False" CanUserDeleteRows="False"
CanUserResizeRows="False" CanUserSortColumns="False"
AutoGenerateColumns="False"
RowHeaderWidth="25" RowHeight="25"/>
Next you can create your columns programmatically in C#/VBA and bind each individual column to a property of the class to which the ObservableCollection contains objects of it. By adding objects of the class you will be adding rows to the datagrid. In other words, each object of the class in the ObservableCollection will be a row and the properties of the class will be the columns.
Here is an example to how you can bind your columns programmatically... C#:
ObservableCollection<IData> datagridData = new ObservableCollection< IData >();
Binding items = new Binding();
PropertyPath path = new PropertyPath("Name"); // 'Name' is actually the name of the variable representing the property
items.Path = path;
MyDataGrid.Columns.Add(new DataGridTextColumn()
{
Header = "Names",
Width = 275,
Binding = items
});
//repeat the creation of columns
//...
//- Add some objects to the ObservableCollection
//- Then bind the ItemsSource of the datagrid to the ObservableCollection
datagridData .Add(new Data("Bob", string.Empty));
MyDataGrid.DataContext = new DataModel{ MyData = datagridData };
*Edit: Sorry about that! Here is how you can achieve the same thing entirely in XAML:
<WpfToolkit:DataGrid
x:Name="MyDataGrid"
ItemsSource="{Binding Path=Collection}"
HorizontalScrollBarVisibility="Hidden" SelectionMode="Extended"
CanUserAddRows="False" CanUserDeleteRows="False"
CanUserResizeRows="False" CanUserSortColumns="False"
AutoGenerateColumns="False"
RowHeaderWidth="25" RowHeight="25">
<WpfToolkit:DataGridTextColumn
Header="Names" Width="2*"
Binding="{Binding Path=Name}"/>
<WpfToolkit:DataGridTextColumn
Header="Names" Width="2*"
Binding="{Binding Path=Age}"/>
</WpfToolkit:DataGrid.Columns>
</WpfToolkit:DataGrid>
Edit 2: Here is what the code of the ObservableCollection and class may look like in C#:
public class DataModel
{
public ObservableCollection<IData> MyData{ get; set; }
}
public interface IData
{
string Name{ get; set; }
string Age{ get; set; }
}
public class Data : IData
{
public Data(string name, string age)
{
Name= name;
Age= age;
}
public string Name{ get; set; }
public string Age{ get; set; }
}
i think you would have to subclass the grid. Columns is not a bindable property. if you cant bind to it, you cant dynamically fiddle with it in the xaml.
As for using the items to create the columns, does that mean if there were no items in the grid there would be no columns?
If you have a subclass of the grid that had a columns property you could bind to, then the place to dynamically change the columns would be in your view model.
精彩评论