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

JP *_*son 4 c# wpf xaml datagrid mvvm

以下人为的例子说明了我的问题.

我的ViewModel:

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; }   
}
Run Code Online (Sandbox Code Playgroud)

现在我的问题在于我的Xaml代码.我希望我的一个列标题为"我的标题",相应的行值为"我的描述"...

我的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>
Run Code Online (Sandbox Code Playgroud)

这有意义吗?第二列标题应为"我的标题",单元格值为"我的描述".有关如何做到这一点的任何想法?

Cod*_*ked 5

我相信你的第二栏应该是:

<DataGridTextColumn Header="{Binding Path=['Ref'].Title}"
    Binding="{Binding Path=['Ref'].Description}"/>
Run Code Online (Sandbox Code Playgroud)

编辑:

好吧,看起来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; }
}
Run Code Online (Sandbox Code Playgroud)

随着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>
Run Code Online (Sandbox Code Playgroud)

如果typeof(Test)在定义列时删除部件,Path=[Ref]则将引用字符串,而不是Test类型的对象.所以我认为你不能使用匿名类型.

JP Richardson的编辑:

根据CodeNaked的回答,我想起了之前我知道解决方案的一些问题,这些让我彻底解决了问题.

首先,可以绑定到匿名数据类型.代码修改应该是:

table.Columns.Add("Ref", typeof(object));
Run Code Online (Sandbox Code Playgroud)

而不是:

table.Columns.Add("Ref");
Run Code Online (Sandbox Code Playgroud)

正如CodeNaked所述,没有typeof(对象),默认对象类型被假定为字符串.

此外,正如CodeNaked所述,DataGridTextColumn不会"知道"该行的DataContext或DataGrid.对于每个Window对象(典型的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);
}
Run Code Online (Sandbox Code Playgroud)

然后在显示Window之前调用"RegisterDataGridColumnsToDataContext()".

将MyViewModel修改为如下所示:

public class MyViewModel : ViewModelBase {
  ...
  public string ColumnTitle { get { return "My Title"; } }
  ...   
}
Run Code Online (Sandbox Code Playgroud)

修改后的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>
Run Code Online (Sandbox Code Playgroud)

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