使用 TemplateColumns 将 WPF DataGrid 绑定到 DataTable

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2677446/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-08 21:41:49  来源:igfitidea点击:

Binding WPF DataGrid to DataTable using TemplateColumns

wpfdata-bindingbindingdatagriddatatable

提问by Chris J

I have tried everything and got nowhere so I'm hoping someone can give me the aha moment. I simply cannot get the binding to pull the data in the datagrid successfully.

我已经尝试了一切,但一无所获,所以我希望有人能给我带来惊喜的时刻。我根本无法获得绑定以成功提取数据网格中的数据。

I have a DataTable that contains multiple columns with of MyDataType

我有一个包含多个列的数据表,其中包含 MyDataType

public class MyData
{
    string nameData {get;set;}
    bool showData {get;set;}
}

MyDataType has 2 properties (A string, a boolean) I have created a test DataTable

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;
}

I have a WPF DataGrid which I want to show my DataTable. But all I want to do is to change how each cell is rendered to show [TextBlock][Button] per cell with values bound to the MyData object and this is where I'm having a tonne of trouble.

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

My XAML looks like this

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

Now all I do once loaded is to attempt to bind the DataTable to the WPF DataGrid

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

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

The TextBlock and Button show up, but they don't bind, which leaves them blank. Could anyone let me know if they have any idea how to fix this. This should be simple, thats what Microsoft leads us to believe. I have set the Column.CellTemplateduring the AutoGeneratingevent and still get no binding.

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

Please help!!!

请帮忙!!!

回答by Joseph Sturtevant

Edit: Updated to reflect the input of Aran Mulholland (see comment)

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

Apparently the DataGridis passing the entire DataRowViewto each cell. That's why the binding doesn't work. Your DataTemplateexpects the DataContextto be of type MyData, but instead it is of type DataRowView. My proposed (somewhat hack-ish) workaround to get the DataContextyou want is to create a custom DataGridTemplateColumnthat will extract the necessary item from the DataRowView. The code is below:

显然DataGrid是将整个传递DataRowView给每个单元格。这就是绑定不起作用的原因。您DataTemplate希望DataContext是 type MyData,但它是 type DataRowView。我提出的(有点hack-ish)解决方法DataContext是创建一个自定义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; }
    }
}

Note: This approach only appears to work with container virtualization off or in Standard mode. If the VirtualizationModeis set to Recycling the template is not applied.

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

回答by Quintalea

After finding this thread and having trouble with the code shown here, I ran across this thread on MSDN, and it works much better! No virtualization problems at all so far as I've seen.

在找到此线程并遇到此处显示的代码时遇到问题后,我在 MSDN 上遇到了此线程,并且效果更好!就我所见,根本没有虚拟化问题。

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

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

Code:

代码:

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;
    }
}