使用 TemplateColumns 将 WPF DataGrid 绑定到 DataTable

Posted

技术标签:

【中文标题】使用 TemplateColumns 将 WPF DataGrid 绑定到 DataTable【英文标题】:Binding WPF DataGrid to DataTable using TemplateColumns 【发布时间】:2011-02-10 06:29:42 【问题描述】:

我已经尝试了所有方法,但一无所获,所以我希望有人能给我带来欢呼的时刻。 我根本无法通过绑定成功拉取数据网格中的数据。

我有一个包含多个 MyDataType 列的 DataTable

public class MyData

    string nameData get;set;
    bool showData get;set;

MyDataType 有 2 个属性(一个字符串,一个布尔值) 我创建了一个测试数据表

DataTable GetDummyData()

    DataTable dt = new DataTable("Foo");
    dt.Columns.Add(new DataColumn("AnotherColumn", typeof(MyData)));
    dt.Rows.Add(new MyData("Row1C1", true));
    dt.Rows.Add(new MyData("Row2C1", false));
    dt.AcceptChanges();
    return dt;

我有一个 WPF DataGrid,我想显示我的 DataTable。 但我想要做的就是更改每个单元格的呈现方式,以显示每个单元格的 [TextBlock][Button] 值绑定到 MyData 对象,这就是我遇到麻烦的地方。

我的 XAML 看起来像这样

<Window.Resources>
    <ResourceDictionary>
        <DataTemplate x:Key="MyDataTemplate" DataType="MyData">
            <StackPanel Orientation="Horizontal" >
                <Button Background="Green" HorizontalAlignment="Right" VerticalAlignment="Center" Margin="5,0,0,0" Content="Binding Path=nameData"></Button>
                <TextBlock Background="Green" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="5,0,0,0" Text="Binding Path=nameData"></TextBlock>
            </StackPanel>
        </DataTemplate>
    </ResourceDictionary>
</Window.Resources>
<Grid>
    <dg:DataGrid Grid.Row="1" ItemsSource="Binding" AutoGenerateColumns="True"
                 x:Name="dataGrid1" SelectionMode="Single" CanUserAddRows="False"
                 CanUserSortColumns="true" CanUserDeleteRows="False" AlternatingRowBackground="AliceBlue"
                 AutoGeneratingColumn="dataGrid1_AutoGeneratingColumn" />
</Grid>

现在我加载后所做的就是尝试将 DataTable 绑定到 WPF DataGrid

dt = GetDummyData();
dataGrid1.ItemsSource = dt.DefaultView;

TextBlock 和 Button 出现了,但它们没有绑定,因此它们是空白的。 任何人都可以让我知道他们是否知道如何解决这个问题。 这应该很简单,这就是微软让我们相信的。 我在AutoGenerating 事件期间设置了Column.CellTemplate,但仍然没有绑定。

请帮忙!!!

【问题讨论】:

您是否尝试将 GetDummyData 中的数据表设置为 DataGrid.DataContext? 已经做过而且还是一样。 UIElements 上没有任何变化,因为它们似乎未绑定。我尝试使用 AutoGeneratingColumn 事件来设置我自己的 Column.CellTemplate ,但仍然没有。还有其他想法吗?谢谢谢谢 试试这个:C# Read Excel and Show in WPF DataGrid 【参考方案1】:

编辑:更新以反映 Aran Mulholland 的意见(见评论)

显然DataGrid 将整个DataRowView 传递给每个单元格。这就是绑定不起作用的原因。您的DataTemplate 期望DataContext 的类型为MyData,但实际上它的类型为DataRowView。我提出的(有点骇人听闻的)解决方法是创建一个自定义的DataGridTemplateColumn,以从DataRowView 中提取必要的项目。代码如下:

<Window x:Class="DataGridTemplateColumnSample.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:dg="clr-namespace:Microsoft.Windows.Controls;assembly=WPFToolkit"
    Title="Window1" Height="300" Width="300">
    <Window.Resources>
        <ResourceDictionary>
            <DataTemplate x:Key="MyDataTemplate" DataType="DataRowView">
                <StackPanel Orientation="Horizontal">
                    <Button Background="Green" HorizontalAlignment="Right" VerticalAlignment="Center" Margin="5,0,0,0" Content="Binding Path=nameData"></Button>
                    <TextBlock Background="Green" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="5,0,0,0" Text="Binding Path=nameData"></TextBlock>
                </StackPanel>
            </DataTemplate>
        </ResourceDictionary>
    </Window.Resources>
    <Grid>
        <dg:DataGrid Grid.Row="1" AutoGenerateColumns="True" x:Name="dataGrid1" SelectionMode="Single" 
                     CanUserAddRows="False" CanUserSortColumns="true" CanUserDeleteRows="False" 
                     AlternatingRowBackground="AliceBlue"  AutoGeneratingColumn="dataGrid1_AutoGeneratingColumn"
                     ItemsSource="Binding" VirtualizingStackPanel.VirtualizationMode="Standard" />
    </Grid>
</Window>

using System.Data;
using System.Windows;
using Microsoft.Windows.Controls;

namespace DataGridTemplateColumnSample

    public partial class Window1
    
        public Window1()
        
            InitializeComponent();
            DataContext = GetDummyData().DefaultView;
        

        private static DataTable GetDummyData()
        
            var dt = new DataTable("Foo");
            dt.Columns.Add(new DataColumn("OneColumn", typeof(MyData)));
            dt.Columns.Add(new DataColumn("AnotherColumn", typeof(MyData)));
            dt.Rows.Add(new MyData("Row1C1", true), new MyData("Row1C2", true));
            dt.Rows.Add(new MyData("Row2C1", false), new MyData("Row2C2", true));
            dt.AcceptChanges();
            return dt;
        

        private void dataGrid1_AutoGeneratingColumn(object sender, DataGridAutoGeneratingColumnEventArgs e)
        
            var column = new DataRowColumn(e.PropertyName);
            column.Header = e.Column.Header;
            column.CellTemplate = (DataTemplate)Resources["MyDataTemplate"];
            e.Column = column;
        
    

    public class DataRowColumn : DataGridTemplateColumn
    
        public DataRowColumn(string column)  ColumnName = column; 
        public string ColumnName  get; private set; 
        protected override FrameworkElement GenerateElement(DataGridCell cell, object dataItem)
        
            var row = (DataRowView) dataItem;
            var item = row[ColumnName];
            cell.DataContext = item;
            var element = base.GenerateElement(cell, item);
            return element;
        
    

    public class MyData
    
        public MyData(string name, bool data)  nameData = name; showData = data; 
        public string nameData  get; set; 
        public bool showData  get; set; 
    

注意:这种方法似乎只适用于关闭或处于标准模式的容器虚拟化。如果VirtualizationMode 设置为回收,则不会应用模板。

【讨论】:

数据网格将行传递给每一列以生成单元格,这是标准行为,列上的绑定定义了单元格的内容。我认为您的问题是您在代码上做所有事情。你有动态数量的列吗?如果不只是在 xaml 中设置列​​及其绑定。 我同意。如果这是我的项目,我会尽量避免以这种方式做事。不幸的是,项目要求(例如动态列)有时并没有给您留下太多空间来以“干净”的方式做事。 哦,哇。惊人的。它有效..谢谢。这100%回答了这个问题。我已经弄清楚这是被传递的行,但无法弄清楚如何到达该视图中要绑定的项目。非常感谢。不幸的是,在事先不知道数据的情况下,我看不出你会如何避免以这种方式做事。这有点违背了拥有与非库存列一起使用的灵活数据网格的目标。为数据类型为非库存列类型的实例定义您自己的 DataTemplateColumn,我原以为可以直接使用。我猜我可以做梦。【参考方案2】:

在找到这个线程并遇到这里显示的代码问题后,我在 MSDN 上遇到了这个线程,它工作得更好!据我所知,根本没有虚拟化问题。

http://social.msdn.microsoft.com/Forums/en/wpf/thread/8b2e94b7-3c44-4642-8acc-851de5285062

代码:

private void dataGrid1_AutoGeneratingColumn(object sender, Microsoft.Windows.Controls.DataGridAutoGeneratingColumnEventArgs e)

    if (e.PropertyType == typeof(MyData))
    
        MyDataGridTemplateColumn col = new MyDataGridTemplateColumn();
        col.ColumnName = e.PropertyName;  // so it knows from which column to get MyData
        col.CellTemplate = (DataTemplate)FindResource("MyDataTemplate");
        e.Column = col;
        e.Column.Header = e.PropertyName;
    


public class MyDataGridTemplateColumn : DataGridTemplateColumn

    public string ColumnName
    
        get;
        set;
    

    protected override System.Windows.FrameworkElement GenerateElement(DataGridCell cell, object dataItem)
    
        // The DataGridTemplateColumn uses ContentPresenter with your DataTemplate.
        ContentPresenter cp = (ContentPresenter)base.GenerateElement(cell, dataItem);
        // Reset the Binding to the specific column. The default binding is to the DataRowView.
        BindingOperations.SetBinding(cp, ContentPresenter.ContentProperty, new Binding(this.ColumnName));
        return cp;
    

【讨论】:

以上是关于使用 TemplateColumns 将 WPF DataGrid 绑定到 DataTable的主要内容,如果未能解决你的问题,请参考以下文章

WPF:仅将使用“AddFontMemResourceEx”安装的字体用于进程

C# WPF 想要将数据存储在一个类中,并在不同 wpf 窗口中的多个不同类中使用

将 Winform 应用程序转换为 WPF 应用程序

将 Silverlight 移植到 WPF

在 WinForms 应用程序中使用 WPF,将应用程序资源放在哪里?

将 Windows 窗体嵌入到 WPF 应用程序中