DataGridTemplateColumns,AutoGenerateColumns=true并绑定到DataTable

3

我正在处理一系列问题。

  1. 我有一个动态数据集,我手动将其组装成DataTable。
  2. 我必须自动生成列,因为数据不是静态的。
  3. 我需要将组合框的ItemsSource绑定到每个单元格中定义的Observable集合。

尽管我认为这很容易,但ComboBox无法看到DataView中的DataItem,而是尝试直接绑定到DataView。

我在这里提供了一个示例项目:

https://github.com/5flags/DataGridBindingIssue

现在,这显然是为了演示问题而刻意制造的。我无法在此时更改数据结构,因此任何解决方案都必须在XAML中完成。

要查看问题,请使用Snoop(或等效工具)查看ComboBoxes上的绑定错误。

DataGrid的设置如下:

<DataGrid AutoGenerateColumns="True" AutoGeneratingColumn="DataGrid_OnAutoGeneratingColumn" CanUserAddRows="False" x:Name="TheDataGrid" ItemsSource="{Binding Data}">
    <DataGrid.Resources>
        <DataTemplate x:Key="dataItemCellTemplate">
            <ComboBox SelectedValue="{Binding Path=SelectedOption, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
                  ItemsSource="{Binding Options}"/>
        </DataTemplate>
    </DataGrid.Resources>
</DataGrid>

自动生成的事件处理程序如下:

private void DataGrid_OnAutoGeneratingColumn(object sender, DataGridAutoGeneratingColumnEventArgs e)
{
    if (e.PropertyType == typeof(string))
    {
        var col = new DataGridTextColumn {Binding = new Binding(e.PropertyName), Header = e.PropertyName};
        e.Column = col;
    }
    else if (e.PropertyType == typeof(DataItem))
    {
        var col = new DataGridTemplateColumn
        {
            CellTemplate = (DataTemplate) TheDataGrid.FindResource("dataItemCellTemplate"),
            CellEditingTemplate = (DataTemplate)TheDataGrid.FindResource("dataItemCellTemplate"),
            Header = e.PropertyName
        };
        e.Column = col;
    }
}

下拉框绑定错误:

System.Windows.Data Error: 40 : BindingExpression路径错误:'Options'属性在'object''DataRowView'(HashCode=22264221)'上未找到。BindingExpression:Path=Options; DataItem='DataRowView' (HashCode=22264221); 目标元素为'ComboBox' (Name=''); 目标属性为'ItemsSource' (type 'IEnumerable')

System.Windows.Data Error: 40 : BindingExpression路径错误:'SelectedOption'属性在'object''DataRowView'(HashCode=22264221)'上未找到。BindingExpression:Path=SelectedOption; DataItem='DataRowView' (HashCode=22264221); 目标元素为'ComboBox' (Name=''); 目标属性为'SelectedValue' (type 'Object')


编辑了问题以包含更多信息。 - RBT
单元格中的DataItem具有SelectedOption和Options。 - RBT
“DataItem”是什么意思?我记得你说你在使用“DataTable”……你是指“DataRow”吗?你怎么知道“Binding”已经到达了“DataView”?在Visual Studio的“Output”窗口中有什么错误? - Sheridan
啊哈!我明白了... 试试在你的 Binding.Path 值前面加上 'DataContext.'{Binding Path=DataContext.SelectedOption, ...}。这样就可以从 DataRowView 移动到你的数据项了。 - Sheridan
如所述,我无法更改数据方面的内容,只能更改 WPF 方面的内容。 - RBT
显示剩余9条评论
3个回答

3

Dusan的回答让我找到了正确的方向。因为我直到运行时才知道列名,所以我也必须在运行时创建数据模板。实际上并不难。

private DataTemplate GetDataTemplate(string columnName)
{
    string xaml = "<DataTemplate><ComboBox SelectedValue=\"{Binding Path=[" + columnName +
                  "].SelectedEnumeratedElementItem, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}\"" +
                  " ItemsSource=\"{Binding Path=[" + columnName +
                  "].Items}\" DisplayMemberPath=\"Name\"/></DataTemplate>";

    var sr = new MemoryStream(Encoding.ASCII.GetBytes(xaml));
    var pc = new ParserContext();
    pc.XmlnsDictionary.Add("", "http://schemas.microsoft.com/winfx/2006/xaml/presentation");
    pc.XmlnsDictionary.Add("x", "http://schemas.microsoft.com/winfx/2006/xaml");
    var datatemplate = (DataTemplate)XamlReader.Load(sr, pc);

    return datatemplate;
}

2

我不知道如何使用从XAML资源中创建的DataTemplate,但是对于我在代码中创建的DataTemplate,它能够正常工作。

private void DataGrid_AutoGeneratingColumn(object sender, DataGridAutoGeneratingColumnEventArgs e)
{
    System.Windows.Controls.DataGridTemplateColumn templateColumn = new System.Windows.Controls.DataGridTemplateColumn();
    templateColumn.Header = e.PropertyName;

    DataTemplate template = new DataTemplate();
    FrameworkElementFactory factory = new FrameworkElementFactory(typeof(StackPanel));
    template.VisualTree = factory;
    FrameworkElementFactory childFactory = new FrameworkElementFactory(typeof(TextBox));
    childFactory.SetBinding(TextBox.TextProperty, new Binding(e.PropertyName));
    factory.AppendChild(childFactory);

    templateColumn.CellEditingTemplate = template;

    template = new DataTemplate();
    factory = new FrameworkElementFactory(typeof(StackPanel));
    template.VisualTree = factory;
    childFactory = new FrameworkElementFactory(typeof(TextBlock));
    childFactory.SetBinding(TextBlock.TextProperty, new Binding(e.PropertyName));
    factory.AppendChild(childFactory);
    templateColumn.CellTemplate = template;

    e.Column = templateColumn;
}

2

请修改您的XAML如下:

<DataGrid AutoGenerateColumns="True" AutoGeneratingColumn="DataGrid_OnAutoGeneratingColumn" CanUserAddRows="False" x:Name="TheDataGrid" ItemsSource="{Binding Data}">
    <DataGrid.Resources>
        <DataTemplate x:Key="dataItemCellTemplate">
            <ComboBox ItemsSource="{Binding [Option].Options}" SelectedValue="{Binding [Option].SelectedOption, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
        </DataTemplate>
    </DataGrid.Resources>
</DataGrid>

[Option] 是指您在 DataView 中存储自定义 DataItem 对象的列。


谢谢,这在我创建的演示应用程序中非常有效,但在实际应用程序中,我直到运行时才知道列。不过你的答案让我找对了方向。 - RBT

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接