【问题标题】:Binding datagrid columns to viewmodel将数据网格列绑定到视图模型
【发布时间】:2019-04-30 20:26:40
【问题描述】:

我有一个完美运行的视图模型,它基本上具有以下属性:

    -ItemCollection
       - Name
       - Type
       - DayCollection
           - DateOfDay
           - Value

我现在正试图让视图按如下方式呈现数据:

Name | Type  | DateOfDay1 | DateOfDay2 | DateOfDay3 ...
Dave | Actual| 1.2        | 1.8        | 5.4
Mary | Actual| 3.4        | 2.3        | 6.4
...

问题是当我将 ItemCollection 绑定到视图中的 Datagrid 时,视图显示:

Name | Type  | DayCollection
Dave | Actual| (Collection)
Mary | Actual| (Collection)

至关重要 - 与下面的一些示例不同,我的用例更简单 - ItemCollection 中的所有项目在 DayCollection 中的天数总是完全相同 - 即我的表的所有行都将具有相同的天数列。虽然请注意,不同的 ItemCollection 可能有不同的天数(但在该 ItemCollection 中,所有项目的天数都相同) - 所以我不能只是展平上述视图模型并拥有一个 ItemCollection,例如:

-ItemCollection
    - Name
    - Type
    - Day1
    - Day2
    - Day3

因为一个 ItemCollection(表)可能有 5 天,另外 30 天。

我发现以下文章讨论了动态添加列 - 我在编辑期间无需添加或删除列 - 列数是固定的:

https://www.codeproject.com/Articles/899637/Dynamic-Columns-in-a-WPF-DataGrid-Control-Part

http://www.unixresources.net/faq/320089.shtml

https://www.codeproject.com/Tips/676530/How-to-Add-Columns-to-a-DataGri

我发现上述示例过于复杂,因为我认为这是一个非常简单的用例 - 项目(行)数量可变,但每行中的列数固定(预定)[上述最后一个虽然文章写得很好]。我有点希望我可以使用重复的 xaml 简单地定义列,例如:

<DataGrid ...>
    <DataGrid.Columns>
         <DataGridTextColumn Header="Name" Binding="{Binding Name}" />
         <DataGridTextColumn Header="Type" Binding="{Binding Type}" />    
         <Repeater ItemsSource="{Binding DayCollection}">
             <DataGridTextColumn Header="{Binding DateOfDay}" Binding="{Binding Value}" />
         </Repeater>
    </DataGrid.Columns>
</DataGrid>

类似上面的方法是一个简单用例的简单解决方案,但显然不支持

我确信这个简单的问题有一个简单的解决方案,但我找不到任何方法来实现上述目标 - 上面引用的文章似乎过多,并且是为更复杂的用例(可变数量的列和可以动态添加和删除的列)- 我不需要这种灵活性,我不想仅仅为了得到固定数量的列而实现它。

我唯一的想法是在我的视图模型中拥有一个诸如 ToDataTable() 之类的属性,它将从我的视图模型中输出一个数据表,其中包含完整的行和列,我可以将数据网格绑定到,但又一次这似乎是一种浪费——然后检查更新并将这些更新引导到上述工作视图模型中已经存在的业务逻辑会变得更加困难。

是否有一个简单的解决方案,或者我是否会为一个简单的用例投入大量代码?

【问题讨论】:

  • 实际上另一个想法是我可以将我的名称和类型添加到我的 DayCollection(将其重命名为 ColumnCollection)并绑定到它,这似乎又是一个肮脏的修复,因为理想情况下我会将它们分开我的日子里有不适用于名称和类型的业务逻辑
  • 编辑——实际上这只会让我的问题变得更糟——记住你不绑定列而是绑定行,所以我最终会得到一列带有 (Collection) 的列 ColumnCollection。跨度>

标签: c# wpf mvvm datagrid


【解决方案1】:

视图模型的全部意义在于以视图可以轻松使用的格式准备数据。如果您的视图模型没有这样做,那么它就不是“完美地工作”,实际上它甚至没有做它应该做的一项工作!您需要做的是创建一个中间结构,将您的数据以正确的格式解包,然后use data-binding to create your columns dynamically

正如您已经发现的那样,这个问题的通用解决方案确实存在使用诸如行为之类的东西。行为通常比代码隐藏更简洁和通用,但它们仍然是视图层的一部分,所以这些解决方案是真的只是创可贴,试图将坏的视图模型数据塞进视图。您应该做的是解决更根本的潜在问题,并在您的视图模型层解决问题。

有许多不同的方法可以做到这一点,但一个简单的解决方案是将所有数据打包到 DataTable 中,然后将 DataGrid 绑定到该数据表。这段代码应该给出一个非常粗略的想法:

<DataGrid ItemsSource="{Binding MyTable}" />

...

public class MainViewModel : ViewModelBase
{
    public DataTable MyTable { get; } = new DataTable();
    private int NumColumns = 3;

    public MainViewModel()
    {
        this.MyTable = new DataTable();
        this.MyTable.Columns.Add("Name");
        this.MyTable.Columns.Add("Type");
        for (int i = 1; i <= NumColumns; i++)
            this.MyTable.Columns.Add($"Day {i}");

        var row = this.MyTable.NewRow();
        row.ItemArray = new object[] { "Tom", "Type A", "1.2", "2.3", "3.4" };
        this.MyTable.Rows.Add(row);

        row = this.MyTable.NewRow();
        row.ItemArray = new object[] { "Dick", "Type B", "2.3", "3.4", "4.5" };
        this.MyTable.Rows.Add(row);

        row = this.MyTable.NewRow();
        row.ItemArray = new object[] { "Harry", "Type C", "3.4", "4.5", "5.6" };
        this.MyTable.Rows.Add(row);
    }
}

【讨论】:

  • 一个有效的尝试和提议,但我认为你错过了他们正在尝试的东西。在第 3 列中,只有一个控件(网格、可滚动列表等),因为一个 Tom 可能有 3 天的活动,但 Dick 有 5 天,而 Harry 有 11 天。没有像您在这里一样为每个人固定 3 个。
  • @DRapp 不,我明白了。我要说的是,他需要手动将数据解压缩到 DataTable 中。当然,他可以编写一个转换器来完成它,但它最终会成为基本上完全相同的逻辑代码,只是在错误的层中。此外,这并不是说它有任何真正的区别,但是虽然列数是可变的,但对于任何给定表格上的每个人来说,它都是相同的,正如他的 cmets 所证明的那样“ItemCollection 中的所有项目将始终具有完全相同的天数在 DayCollection 中,即我表的所有行都将具有相同数量的列”。
  • 我同意 - 当我说它运行良好时,我的意思是从业务逻辑的角度来看,显然存在我无法将数据放入视图的问题 - 我实际上认为这是 DataGrid 控件的问题 - 我发现它是一个很大的 limfac,您无法绑定列 - 一个选择是编写或找到另一个 DataGrid 类型的控件。从我的原始文本中,我考虑使用 DataTable 解决这个问题“我唯一的想法是在我的视图模型中有一个属性,例如 ToDataTable()..”。但这在我看来是一种解决 UI 控件局限性的方法,
  • 我现在不得不承认,在我探索过的那些方法中,它似乎是更简单的解决方法,我唯一的问题是它似乎还有很多额外的工作来满足绑定 - 我想要能够更新 DataGrid 中的数据并更新我的 viewmodel 对象 - 我再次确定这不是不可克服的,但对于一个简单的用例来说,感觉需要付出很多额外的努力。
  • 我的一些补充要求是,并非所有字段都应该是可更新的(例如,在我的示例中只有十进制值,而不是文本值),并且还要根据它们是否为各种列和单元格着色是可编辑的或类型。如果 DataGrid 允许绑定列数据,这又会相对容易,因为单元格可以根据另一个绑定属性着色(我实际上已经有 IsReadOnly 和 Type 的属性)。
【解决方案2】:

在寻找更好的解决方案几天后,我终于选择了上面的第三个链接。我仍然觉得这是一个非常复杂的解决方案,本来应该很简单,但是它可以工作并且非常可重用。

https://www.codeproject.com/Tips/676530/How-to-Add-Columns-to-a-DataGri

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2010-12-21
    • 1970-01-01
    • 1970-01-01
    • 2014-01-17
    • 1970-01-01
    • 2011-01-01
    • 2013-09-25
    • 1970-01-01
    相关资源
    最近更新 更多