Привязка WPF DataGrid к DataTable с использованием TemplateColumns - PullRequest
8 голосов
/ 20 апреля 2010

Я перепробовал все и ничего не получил, так что я надеюсь, что кто-то может дать мне момент ага. Я просто не могу получить привязку для успешного извлечения данных в сетке данных.

У меня есть DataTable, который содержит несколько столбцов с MyDataType

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

MyDataType имеет 2 свойства (строка, логическое значение) Я создал тест DataTable

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, которую я хочу показать в своей таблице данных. Но все, что я хочу сделать, - это изменить способ отображения каждой ячейки, чтобы отображать [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 отображаются, но они не привязываются, что оставляет их пустыми. Может ли кто-нибудь дать мне знать, если у них есть идеи, как это исправить. Это должно быть просто, вот что Microsoft заставляет нас верить. Я установил Column.CellTemplate во время события AutoGenerating и все еще не получаю привязки.

Пожалуйста, помогите !!!

Ответы [ 2 ]

16 голосов
/ 22 апреля 2010

Редактировать: Обновлено, чтобы отразить ввод Арана Малхолланда (см. Комментарий)

Очевидно, DataGrid передает все DataRowView каждой ячейке.Вот почему привязка не работает.Ваш DataTemplate ожидает, что DataContext будет иметь тип MyData, но вместо этого он будет иметь тип DataRowView.Мой предложенный (несколько хакерский) обходной путь для получения 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; }
    }
}

Примечание. Этот подход работает только при отключенной виртуализации контейнера или в стандартном режиме.Если для VirtualizationMode установлено значение Переработка, шаблон не применяется.

8 голосов
/ 18 марта 2011

После нахождения этого потока и возникновения проблем с кодом, показанным здесь, я наткнулся на этот поток в 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;
    }
}
...