WPF/Xaml绑定DataGrid列头和单元格到不同的值

4
以下人为例子说明了我的问题。
我的视图模型:
public class MyViewModel : ViewModel 
{
    public MyViewModel() 
    {
        var table = new DataTable("MyDatatable");
        table.Columns.Add("Some Val", typeof(double));
        table.Columns.Add("Ref");

        var row = table.NewRow();
        row["Some Val"] = 3.14;
        row["Ref"] = new {Title = "My Title", Description = "My Description"};
        table.Rows.Add(row);

        MyDataView = table.DefaultView;
    }

    DataView MyDataView { get; set; }   
}

现在我的问题在于我的Xaml代码。我希望我的其中一个列标题为"My Title",相应的行值为"My Description"...

我的Xaml代码:

<DataGrid ItemsSource="MyDataView" AutoGenerateColumns="False">
  <DataGrid.Columns>
    <!--This column works fine. -->
    <DataGridTextColumn Header="Some Val" Binding="{Binding 'Some Val'}" Width="50"/>
    <!--This column doesn't work... I'm looking for something similar to this: -->
    <DataGridTextColumn Header="{Binding Path='Ref.Title'}" Binding="{Binding Path='Ref.Description'}"/>
  </DataGrid.Columns>
</DataGrid>

这是否有意义?第二列标题应为“My Title”,单元格值为“My Description”。有什么想法如何实现?

1个回答

5
我认为你的第二列应该是:
<DataGridTextColumn Header="{Binding Path=['Ref'].Title}"
    Binding="{Binding Path=['Ref'].Description}"/>

编辑:

好的,看起来DataGridTextColumn不会从DataGrid继承DataContext。实际上,它既不是FrameworkElement也不是Freezable,因此根本没有DataContext。

该列应适用于任意数量的行,即使只有1行。 Header需要对所有行通用,而Binding是特定于行的。但是,您正在尝试将两者都绑定到第一行。

如果您实际上有两行具有不同的标题,则应显示哪个标题?

对于描述,以下内容有效:

public class Test {
    public string Title { get; set; }
    public string Description { get; set; }
}

public class MyViewModel  {
    public MyViewModel() {
        var table = new DataTable("MyDatatable");
        table.Columns.Add("Some Val", typeof(double));
        table.Columns.Add("Ref", typeof(Test));

        var row = table.NewRow();
        row["Some Val"] = 3.14;
        row["Ref"] = new Test() { Title = "My Title", Description = "My Description" };
        table.Rows.Add(row);

        MyDataView = table.DefaultView;
    }

    public DataView MyDataView { get; set; }
}

使用以下XAML:

<DataGrid ItemsSource="{Binding MyDataView}" AutoGenerateColumns="False">
    <DataGrid.Columns>
        <DataGridTextColumn Header="Some Val" Binding="{Binding 'Some Val'}" Width="50" />
        <DataGridTextColumn Header="Test" Binding="{Binding Path=[Ref].Description}" />
    </DataGrid.Columns>
</DataGrid>

如果在定义列时删除typeof(Test)部分,则Path=[Ref]将引用一个字符串,而不是Test类型的对象。因此,我认为您将无法使用匿名类型。
JP Richardson的编辑:
根据CodeNaked的答案,我想起了一些以前遇到过的问题,并且知道如何解决这些问题。首先,可以绑定到匿名数据类型。代码修改应该是:
table.Columns.Add("Ref", typeof(object));

替代:

table.Columns.Add("Ref");

正如CodeNaked所说,如果没有typeof(object),则默认的对象类型被认为是字符串。

此外,正如CodeNaked所说,DataGridTextColumn既不“知道”行的DataContext也不知道DataGrid。对于每个窗口对象(通常是MVVM场景,只有一个),应该放置以下代码:

private static void RegisterDataGridColumnsToDataContext() {
        FrameworkElement.DataContextProperty.AddOwner(typeof(DataGridColumn));
        FrameworkElement.DataContextProperty.OverrideMetadata(typeof(DataGrid), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.Inherits, new PropertyChangedCallback(OnDataContextChanged)));
}

public static void OnDataContextChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) {
        DataGrid grid = d as DataGrid;
        if (grid != null)
            foreach (DataGridColumn col in grid.Columns)
                col.SetValue(FrameworkElement.DataContextProperty, e.NewValue);
}

然后在窗口显示之前调用“RegisterDataGridColumnsToDataContext()”。

将MyViewModel修改为以下内容:

public class MyViewModel : ViewModelBase {
  ...
  public string ColumnTitle { get { return "My Title"; } }
  ...   
}

修改后的Xaml如下所示:
<DataGrid ItemsSource="{Binding MyDataView}" AutoGenerateColumns="False">
  <DataGrid.Columns>
    <DataGridTextColumn Header="Some Val" Binding="{Binding 'Some Val'}" Width="50" />
    <DataGridTextColumn Header="{Binding (FrameworkElement.DataContext).ColumnTitle, RelativeSource={x:Static RelativeSource.Self}}" Binding="{Binding Path=[Ref].Description}" />
  </DataGrid.Columns>
</DataGrid>

注意:最初我在每一行中都有列标题的原因是,我想不到另一种将其绑定到DataGridTextColumn的'Header'属性的方法。我计划使用每个匿名对象的'Title'属性相同。幸运的是,互联网胜利了。


它带有单引号时无法编译。我去掉了单引号,但仍然无法工作。 - JP Richardson
我已经更新了你的答案,并提供了完整的答案。感谢你的帮助。 - JP Richardson

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