【问题标题】:WPF/Xaml Binding DataGrid Column Header and Cell to Different ValuesWPF/Xaml 将 DataGrid 列标题和单元格绑定到不同的值
【发布时间】:2011-05-25 18:01:08
【问题描述】:

以下人为的示例说明了我的问题。

我的视图模型:

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 代码。我希望我的列标题之一是“我的标题”,相应的行值是“我的描述”...

我的 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>

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

【问题讨论】:

    标签: c# wpf xaml mvvm datagrid


    【解决方案1】:

    我认为您的第二列应该是:

    <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。对于每个 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);
    }
    

    然后在窗口显示之前调用“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 的“标题”属性。我计划让每个匿名对象的“标题”属性相同。幸运的是,互联网盛行。

    【讨论】:

    • 它不能用单引号编译。我删除了单引号,它仍然不起作用。
    • 我用完整的答案更新了你的答案。感谢您的帮助。
    猜你喜欢
    • 1970-01-01
    • 2011-09-02
    • 2011-04-13
    • 2010-10-24
    • 2013-12-27
    • 1970-01-01
    • 1970-01-01
    • 2013-08-21
    • 1970-01-01
    相关资源
    最近更新 更多