MVVM - 如何在运行时为 xamdatagrid 创建列?

发布于 2024-09-13 09:11:38 字数 334 浏览 8 评论 0原文

我必须创建一个 XamDataGrid,它显示时间范围 x 到 y 的动态列数。因此,我不知道用户会选择提前创建这些列多少年。

现在,通常在 MVVM 中,您只需通过 XamDataGrid 中所需的尽可能多的属性填充数据,后者将自动生成它们。

显然,我不能在运行时在 ViewModel 中创建属性,除非我对 Reflection 做了一些疯狂的事情。

我还能怎样实现这个目标呢?

我应该为数据网格创建未绑定的字段并通过代码填充它们吗?我同意在这个阶段我不需要双向绑定,因为网格只是只读的......只是大声思考。

在不违反 MVVM 模式的情况下,这种方法可行吗? 谢谢

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

温馨耳语 2024-09-20 09:11:38

您可以使用索引器:

在 ViewModel 中:

public MyVariableCollection RowData
{
    get { return new MyVariableCollection(this); }
}

MyVariableCollection 中:
受保护的 SomeRowViewModel 视图模型;

public MyVariableCollection(SomeRowViewModel viewmodel)
{
    this.viewModel = viewmodel;
}

public object this[string name]
{
    get { return viewModel.GetRowColumnValue(name); }
}

我试图保持简短:但想法是,您有一个定义了索引器的新类,然后您可以像这样绑定:

{Binding Path=SomeRowViewModelInstance.RowData["ColumnName"]}

数据网格控件上的列集合将被绑定 - 并且您可以设置一个列模板将每一列绑定到相关列;您不需要在索引器中使用这样的文字字符串。

希望这能提供一些思考——关于这条路线的任何问题请发表评论。


编辑附加想法:我使用内容 ComponentModel 命名空间来生成自定义 TypeDescriptor。它相当深入,但您可以使对象“看起来”具有附加或自定义属性。它比我上面发布的索引器方法复杂得多,但如果您遇到困难,值得一看。

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.

紙鸢 2024-09-20 09:11:38

我遇到了类似的问题,因为用户能够在运行时定义网格的列。

我编写了一个包含 xam 数据网格的控件,并公开了一个 DataSource 依赖属性来绑定网格模型(即数据表)。

每次源更改时(您可以为 PropertyChanged 和网格 FieldLayoutInitializing 事件添加事件侦听器),网格都会通过清除其数据源并重置来动态重新呈现:

private void ReRenderGrid()
{
    XamDataGrid.FieldLayouts.Clear();
    XamDataGrid.ClearValue(DataPresenterBase.DataSourceProperty);
    XamDataGrid.DataSource = DataSource.Data.DefaultView;
}

列由引发以下事件的事件处理程序重新配置重置网格数据源后通过 xamdatagrid:

XamDataGrid.FieldLayoutInitializing += LayoutInitializing;

处理程序:

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));
}

在我的情况下,数据源包含用户配置的所有列。 AddFields 为每个列表条目调用此方法:

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 和 AddUnboundField 以类似的方式实现。

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文