问题
I have to create a XamDataGrid that shows a dynamic amount of columns for a time frame x to y. Therefore I dont know how many years a user would select to have these columns created upfront.
Now usualy within MVVM you would just populate the data through as many Properties as you would need Columns within your XamDataGrid, and the latter would just autogenerate them.
Obviously I couldn't just create Properties within my ViewModel at runtime unless I did something crazy with Reflection.
How else would I achieve this?
Should I just create unbound fields for the datagrid and fill them through code? I agree I wont need a two way Binding at this stage, since the grid is only readonly...just thinking loud.
Is this approach OK without violating the MVVM pattern? Thanks
回答1:
You can use indexers:
In your ViewModel:
public MyVariableCollection RowData
{
get { return new MyVariableCollection(this); }
}
In MyVariableCollection
:
protected SomeRowViewModel viewModel;
public MyVariableCollection(SomeRowViewModel viewmodel)
{
this.viewModel = viewmodel;
}
public object this[string name]
{
get { return viewModel.GetRowColumnValue(name); }
}
I've tried to keep it brief: but the idea is, you have a new class with an indexer defined, then you can bind like this:
{Binding Path=SomeRowViewModelInstance.RowData["ColumnName"]}
The column collection on the data grid control would be bound - and you could set a column template for each column to bind to the column in question; you needn't use a literal string in the indexer like that.
Hope that provides some food for thought - any questions on this route please leave a comment.
Edit for an additional thought: I have used the contents ComponentModel
namespace to produce a custom TypeDescriptor
. It is fairly in depth but you can make an object 'appear' to have additional or custom properties. It's far more complex than the indexer method I posted above but if you get stuck it's worth a look.
回答2:
I had a similar problem because the user was able to define the columns of the grid at runtime.
I wrote a control containing the xam datagrid and exposing a DataSource dependency property to bind the model for the grid (i.e. a data table).
Every time the source changed (you can add event listeners for PropertyChanged and the grids FieldLayoutInitializing event) the grid was dynamically re-rendered by clearing its datasource and resetting it:
private void ReRenderGrid()
{
XamDataGrid.FieldLayouts.Clear();
XamDataGrid.ClearValue(DataPresenterBase.DataSourceProperty);
XamDataGrid.DataSource = DataSource.Data.DefaultView;
}
The columns are re configured by an event handler on the following event which is raised by xamdatagrid after the grids datasource is reset:
XamDataGrid.FieldLayoutInitializing += LayoutInitializing;
Handler:
private void LayoutInitializing(object sender, FieldLayoutInitializingEventArgs e)
{
const string deletebuttonstyle = "DeleteButtonStyle";
const string requiredinputvalue = "RequiredInputValue";
const string optionalinputvalue = "OptionalInputValue";
const string outputvalue = "OutputValue";
var fieldLayout = e.FieldLayout;
fieldLayout.Fields.Clear();
AddFields(DataSource.InColumns, requiredinputvalue, fieldLayout);
AddSplitter(fieldLayout);
AddFields(DataSource.OptionalInColumns, optionalinputvalue, fieldLayout);
AddSplitter(fieldLayout);
AddFields(DataSource.OutColumns, outputvalue, fieldLayout);
AddUnboundField(fieldLayout, string.Empty, GetStyle(deletebuttonstyle));
}
In my case the datasource contained all columns the user configured. AddFields calls this method for each list entry:
private void AddField(string name, Style style, FieldLayout fieldLayout)
{
var field = new Field {Name = name};
field.Settings.LabelPresenterStyle = style;
field.Settings.CellValuePresenterStyle = GetStyle("StandardCellValueStyle");
fieldLayout.Fields.Add(field);
}
AddSplitter and AddUnboundField are implemented in a similar fashion.
来源:https://stackoverflow.com/questions/3425972/mvvm-how-to-create-columns-at-runtime-for-a-xamdatagrid