Binding WPF DataGrid to DataTable using TemplateColumns

后端 未结 2 927
挽巷
挽巷 2020-11-29 09:32

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.

相关标签:
2条回答
  • 2020-11-29 10:28

    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.

    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;
        }
    }
    
    0 讨论(0)
  • 2020-11-29 10:31

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

    Apparently the DataGrid is passing the entire DataRowView to each cell. That's why the binding doesn't work. Your DataTemplate expects the DataContext to be of type MyData, but instead it is of type DataRowView. My proposed (somewhat hack-ish) workaround to get the DataContext you want is to create a custom DataGridTemplateColumn that will extract the necessary item from the DataRowView. The code is below:

    <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 VirtualizationMode is set to Recycling the template is not applied.

    0 讨论(0)
提交回复
热议问题