【问题标题】:MVVM - How to create columns at runtime for a xamdatagrid?MVVM - 如何在运行时为 xamdatagrid 创建列?
【发布时间】:2010-08-06 16:57:40
【问题描述】:

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

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

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

我还能如何做到这一点?

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

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

【问题讨论】:

    标签: .net wpf mvvm xamdatagrid


    【解决方案1】:

    您可以使用索引器:

    在您的视图模型中:

    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。它相当深入,但您可以使对象“看起来”具有附加或自定义属性。它比我在上面发布的索引器方法复杂得多,但如果您遇到困难,值得一看。

    【讨论】:

    • 感谢您的出色回应基伦。不过,我发现您的模型存在三个问题。首先是 ViewModel 的 RowData 属性,它传入自己的 ctor 一个“this”。然而,实际的 ctor 接受的 SomeRowViewModel 类型与之前的“this”类型不兼容。
    • 第二个问题是你绑定单列的方式。在 XamDataGrid 中,您通常会将整个 xamdatagrid 简单地绑定到 BindingList 集合,然后 dit 会自动生成这些行。除非您手动创建列,否则您实际上无法单独绑定到每个列。
    • 第三个问题如下,我可以为时间段 x 到 y 动态自动生成 columnNames,以便我可以按照您的建议在索引器中使用它们。但绑定仍然不能在静态 xaml 代码上。更多它必须在 xaml 视图后面的代码中,以便以可变方式使用列名。除非我在这里遗漏了一些东西......谢谢
    • 嗨,凯夫。它的要点是:您仍然绑定到 ViewModel 对象的集合。但不是将每一列绑定到一个属性,而是将其绑定到代表该行的 ViewModel 上的 RowData["ColumnName"] 属性/索引器。这种方法的优点是您可以使用另一个绑定/属性路径来指定列名。至于绑定列集合,我指的是普通网格上的 Columns 属性 - 认为 Infragistics 可能将它们称为字段。这有帮助吗?
    • (从另一个角度来看,你说'我不能在运行时在我的 ViewModel 中创建属性,除非我对反射做了一些疯狂的事情'。我是说'你可以做使用索引器 - 而不是 obj.Property 使用 obj["Property"].'。如果这不好,您可以使用我最后提到的 CustomTypeDescriptor 使对象看起来具有不同的属性。跨度>
    【解决方案2】:

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

    我编写了一个包含 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 以类似的方式实现。

    【讨论】:

    • Zebi,非常感谢您提供这些代码片段。我相信它的工作原理,它看起来确实很有希望。然而,这些只是代码 sn-ps。我不能跟随状态。 XamDataGrid.DataSource = DataSource.Data.DefaultView;在这种情况下什么是数据,我似乎没有。方法 AddFields 似乎与您的 AddField 具有不同的签名。我还不清楚这应该如何工作。一个简单的工作示例将不胜感激。
    • 我示例中的 DataSource 属性是指我制作的自定义类型,用于保存数据表(.Data 属性)和用户定义的列(他可以定义 3 种类型的列、输入、可选输入和输出列)。您必须有一些数据表用作网格的数据源。重置此表。我现在无法创建工作示例,因为代码来自工作中的项目。我明天看看能不能做。
    • 感谢 Zebi,非常感谢。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-11-20
    • 1970-01-01
    • 1970-01-01
    • 2012-06-21
    相关资源
    最近更新 更多