我试图通过DataTable绑定在WPF DataGrid中显示三维数据.由于DataGrid只有行和列(2D),我的 idea 是模板单元格,使它们承载ListView(或其他控件),并显示垂直堆叠的第三维值.

期望的结果是:

enter image description here

下面是一个非常简化但可复制的模拟数据代码,我try 创建这样一个DataTable:

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();

        ViewModel vm = new();

        MyDataGrid.DataContext = vm.Table.DefaultView;
        MyDataGrid.IsReadOnly = true;
    }

    public class ViewModel
    {
        public DataTable Table { get; init; }

        public ViewModel()
        {
            Table = new DataTable();

            Table.Columns.Add(" ");
            Table.Columns.Add("c1");
            Table.Columns.Add("c2");
            Table.Columns.Add("c3");

            for (int i = 1; i <= 3; i++ )
            {
                DataRow row = Table.NewRow();

                row[0] = new List<object>() { "r" + i } ;
                row[1] = new List<object>() { 10 * i, 20 * i, 25 * i } ;
                row[2] = new List<object>() { 12 * i, 26 * i, 30 * i } ;
                row[3] = new List<object>() { 16 * i, 28 * i, 36 * i };

                Table.Rows.Add(row);
            }
        }
    }
}

如您所见,我将List赋给每个单元格,这样可以创建第三个维度,将多个值存储在一个单元格中.

问题是,我不知道如何在WPF标记中模板化DataGrid,以便正确地解压缩并显示第三个维度,就像上面所示的图像一样.我试过这个:

<DataGrid x:Name="MyDataGrid" ItemsSource="{Binding}">
    <DataGrid.Resources>
        <DataTemplate x:Key="DataGridTemplate">
            <ListView ItemsSource="{Binding}">
                <ListView.ItemTemplate>
                    <DataTemplate>
                        <TextBlock Text="{Binding}"/>
                    </DataTemplate>
                </ListView.ItemTemplate>
            </ListView>
        </DataTemplate>
    </DataGrid.Resources>

    <DataGrid.CellStyle>
        <Style TargetType="DataGridCell">
            <Setter Property="ContentTemplate" Value="{StaticResource DataGridTemplate}"/>
        </Style>
    </DataGrid.CellStyle>
</DataGrid>

但是当我编译并运行它时,DataGrid只显示空单元格,即使没有绑定错误.如果我不使用任何模板,单元格显示System.Data.DataRowView System.Collections.Generic.List1 [System. Object]`.

如果有人能看一看(我上面提供的代码是完全可重现的例子),让我知道如何创建正确的模板,我会非常感激的.或者,如果我在这方面走错了路,我也愿意接受其他建议.

编辑:澄清:在我的完整应用程序中,列和行的数量以及它们的头名称是事先不知道的,并且可能在运行时发生变化,因此必须以允许在修改或重新创建底层DataTable时反映这些变化的方式绑定DataGrid.

推荐答案

您所拥有的代码存在一些问题.

1 - Add the Type for the complex DataTable columns

Issue 1 is that your columns are not typed, and this seems to be resulting in DataRow["c1"] etc showing as a string value of the type System.Collections.Generic.List1[System.Int32] rather than the collection itself. See the debugger screenshot below: enter image description here

您将需要更新这些列并添加如下所示的类型(注意,我还命名了第一列以便我们可以引用它).

// row headers
Table.Columns.Add("c0", typeof(string)); 
// three-dimensional data
Table.Columns.Add("c1", typeof(IList));
Table.Columns.Add("c2", typeof(IList));
Table.Columns.Add("c3", typeof(IList));

2 - Use DataGridTemplateColumn to build the complex DataGrid columns

第二个挑战是,我不认为您可以使用自动生成的列来完成您想要的.如果我将CellTemplate改为绑定到TextBlock,你可以看到这些单元格的数据上下文实际上是整个DataRowView对象,而不仅仅是绑定的列:

<DataGrid.Resources>
    <DataTemplate x:Key="DataGridTemplate">
        <TextBlock Text="{Binding}" />
    </DataTemplate>
</DataGrid.Resources>

enter image description here

更新你的DataGrid如下所示,我能够生成我相信你正在寻找的.

<DataGrid x:Name="MyDataGrid"
            ItemsSource="{Binding}"
            AutoGenerateColumns="False">
    <DataGrid.Columns>
        <DataGridTextColumn Binding="{Binding c0}" />
        <DataGridTemplateColumn Header="c1">
            <DataGridTemplateColumn.CellTemplate>
                <DataTemplate>
                    <ListView ItemsSource="{Binding c1}" />
                </DataTemplate>
            </DataGridTemplateColumn.CellTemplate>
        </DataGridTemplateColumn>
        <DataGridTemplateColumn Header="c2">
            <DataGridTemplateColumn.CellTemplate>
                <DataTemplate>
                    <ListView ItemsSource="{Binding c2}" />
                </DataTemplate>
            </DataGridTemplateColumn.CellTemplate>
        </DataGridTemplateColumn>
        <DataGridTemplateColumn Header="c3">
            <DataGridTemplateColumn.CellTemplate>
                <DataTemplate>
                    <ListView ItemsSource="{Binding c3}" />
                </DataTemplate>
            </DataGridTemplateColumn.CellTemplate>
        </DataGridTemplateColumn>
    </DataGrid.Columns>
</DataGrid>

结果

enter image description here

更新:3—如果需要自动生成列...

如果您的用例要求您自动生成列,如注释中所提到的,您可以覆盖那些特定列的列生成.这种方法不太干净,依赖于在代码后面构建这些列.下面的例子引用了这两个链接:

How to: Customize Auto-Generated Columns in the DataGrid Control. https://stackoverflow.com/a/1755556/15534202.

XAML:添加自动生成事件处理程序

<DataGrid x:Name="MyDataGrid"
            ItemsSource="{Binding}"
            AutoGenerateColumns="True"
            AutoGeneratingColumn="MyDataGrid_AutoGeneratingColumn">
</DataGrid>

Code Behind:生成List

private void MyDataGrid_AutoGeneratingColumn(object sender, DataGridAutoGeneratingColumnEventArgs e)
{
    // Optional: Remove the column header for the row headers
    if (e.Column.Header == "c0") e.Column.Header = "";

    // Let the non-list data auto-generate normally
    if (!e.PropertyType.IsAssignableFrom(typeof(IList))) return;

    // create a new column
    var newColumn = new DataGridTemplateColumn();
    newColumn.Header = e.Column.Header;
            
    // create a new binding
    var binding = new Binding(e.PropertyName);
    binding.Mode = BindingMode.TwoWay;

    // setup the cell template
    var elementFactory = new FrameworkElementFactory(typeof(ListBox));
    elementFactory.SetValue(ListBox.ItemsSourceProperty, binding);
    var newCellTemplate = new DataTemplate();
    newCellTemplate.VisualTree = elementFactory;
    newColumn.CellTemplate = newCellTemplate;
            
    // assign the new column
    e.Column = newColumn;
}

Csharp相关问答推荐

错误NU 1301:无法加载源的服务索引

禁用AutoSuggestBox项目更改时的动画?

无法从具有一对多关系的C#类中使用Swagger创建记录

使用yaml将Azure函数代码部署到FunctionApp插槽时出现问题(zip未找到)

System. InvalidOperationException:无法将数据库中的字符串值i转换为映射的ItemType枚举中的任何值''''

碰撞检测与盒碰撞器,其isTrigger on问题

Blazor EventCallback<;MyType<;T>;>;

WinForms在Linux上的JetBrains Rider中的应用

使用Dapper映射联接查询对象数据到使用SplitOn;

EF核心区分大小写的主键

.NET 8在appsettings.json中核心使用词典URI、URI&>

MSI无法将快捷方式添加到启动文件夹

实体框架-IsRequired()与OnDelete()

如何强制新设置在Unity中工作?

JSON串行化程序问题.SQLite中的空值

如何从Azure函数使用Graph API(SDK 5.35)中的[FindMeetingTimes]

KeyDown从我的文本框中删除输入,如何停止?

如何在绑定到数据库的datagridview中向上或向下移动行

根据优先级整理合同列表

部署Aspnet Blazor服务器时出现未处理的Microsoft.AspNetCore.Diagnostics.ExceptionHandlerMiddleware[1]异常