C# WPF DataGrid-数据绑定到CellTemplates DataTemplate中的DataTable单元格

C# WPF DataGrid-数据绑定到CellTemplates DataTemplate中的DataTable单元格,c#,wpf,mvvm,datagrid,C#,Wpf,Mvvm,Datagrid,我有一个DataGrid和一个DataTable,其中包含as ItemsSource。列的数量有时会有所不同。如果列的数据类型为a类,我希望使用DataTemplate自定义单元格内容的外观 我已经准备好了 AutoGenerateColumns="True" 以生成DataTable中的所有列 如果数据类型为a类型,我将用DataGridTemplateColumn替换DataGridColumn private void DataGrid_AutoGeneratingColumn(ob

我有一个DataGrid和一个DataTable,其中包含as ItemsSource。列的数量有时会有所不同。如果列的数据类型为a类,我希望使用DataTemplate自定义单元格内容的外观

我已经准备好了

AutoGenerateColumns="True" 
以生成DataTable中的所有列

如果数据类型为a类型,我将用DataGridTemplateColumn替换DataGridColumn

private void DataGrid_AutoGeneratingColumn(object sender, system.Windows.Controls.DataGridAutoGeneratingColumnEventArgs e)
{
    if (e.PropertyType == typeof(A))
    {
        e.Column = new DataGridTemplateColumn
        {
            CellTemplate = (DataTemplate)Resources["ATemplate"],
            Header = e.Column.Header,
            HeaderTemplate = e.Column.HeaderTemplate,
            HeaderStringFormat = e.Column.HeaderStringFormat
        };
    }
}
数据模板如下所示

<DataTemplate x:Key="ATemplate">
   <RadioButton Content="{Binding Name}" GroupName="{Binding GroupName}" IsChecked="{Binding IsSelected}" />
</DataTemplate>
A类看起来像这样

public class A
{
    public string Name { get; set; }
    public string GroupName { get; set; }
    public bool IsSelected { get; set; }
}
如何将DataTemplate数据绑定到正确的单元格和属性

(如果您有一个MVVM解决方案,其中我不必使用DataGrid_AutoGeneratingColumn,那就太好了)

编辑

我也尝试过这个解决方案,但运气不佳。当不知道如何呈现类时,单元格中通常只显示类名

<DataGrid AutoGenerateColumns="True" ItemsSource="{Binding Items}">
   <DataGrid.Resources>
      <DataTemplate DataType="{x:Type viewModel:A}">
         <RadioButton Content="{Binding Path=Name}" GroupName="{Binding Path=GroupName}" IsChecked="{Binding Path=IsSelected}" />
      </DataTemplate>
   </DataGrid.Resources>
</DataGrid>

模板中的绑定不起作用,因为DataContext是DataTable中的DataRowView

一种解决方案是更改模板,将DataContext设置为所需的对象(类型为A),然后所有绑定都可以工作(名称、组名、IsSelected)。为此,您需要制作一个转换器,并让模板使用它

模板中的DataContext绑定到传递到转换器的DataGridCell祖先。从单元格中,我们可以获得DataContext(DataRowView)和单元格的列。在DataGrid_AutoGeneratingColumn中创建列时,我们将列的SortMemberPath设置为e.PropertyName(datatable中的列名称)。在转换器中,我们使用SortMemberPath作为索引查找DataRowView.Row中的对象。我们将其作为模板的DataContext返回

下面是一个类a和类B的实现。我在数据表中添加了每个类的两列,以显示它可以处理多个实例

MainWindow.xaml:

<Window x:Class="WpfApplication17.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:viewModel="clr-namespace:WpfApplication17"
        Title="MainWindow" Height="350" Width="525">
    <Window.Resources>
        <viewModel:DataRowViewConverter x:Key="drvc" />
        <DataTemplate x:Key="ATemplate">
            <RadioButton DataContext="{Binding RelativeSource={RelativeSource AncestorType=DataGridCell}, Converter={StaticResource drvc}}" Content="{Binding Path=Name}" GroupName="{Binding Path=GroupName}" IsChecked="{Binding Path=IsSelected}" />
        </DataTemplate>
        <DataTemplate x:Key="BTemplate">
            <CheckBox DataContext="{Binding RelativeSource={RelativeSource AncestorType=DataGridCell}, Converter={StaticResource drvc}}" Content="{Binding Path=FullName}" IsChecked="{Binding Path=IsChecked}" />
        </DataTemplate>
    </Window.Resources>
    <Grid>
        <DataGrid AutoGenerateColumns="True" ItemsSource="{Binding Items}" AutoGeneratingColumn="DataGrid_AutoGeneratingColumn" CanUserAddRows="False">
        </DataGrid>
    </Grid>
</Window>

MainWindow.xaml.cs:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace WpfApplication17
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public System.Data.DataTable Items { get; set; }

        public MainWindow()
        {
            InitializeComponent();

            System.Data.DataTable dt = new System.Data.DataTable();
            dt.Columns.Add("StringColumn", typeof(string));
            dt.Columns.Add("IntColumn", typeof(int));
            dt.Columns.Add("AColumn1", typeof(A));
            dt.Columns.Add("AColumn2", typeof(A));
            dt.Columns.Add("BColumn1", typeof(B));
            dt.Columns.Add("BColumn2", typeof(B));

            dt.Rows.Add(
                "TestString",
                123,
                new A() { Name = "A1", GroupName = "GroupName", IsSelected = true },
                new A() { Name = "A2", GroupName = "GroupName", IsSelected = false },
                new B() { FullName = "B1", IsChecked=true },
                new B() { FullName = "B2", IsChecked=false }
            );

            Items = dt;
            this.DataContext = this;
        }

        private void DataGrid_AutoGeneratingColumn(object sender, DataGridAutoGeneratingColumnEventArgs e)
        {
            DataTemplate dt = null;
            if (e.PropertyType == typeof(A))
                dt = (DataTemplate)Resources["ATemplate"];
            else if (e.PropertyType == typeof(B))
                dt = (DataTemplate)Resources["BTemplate"];

            if (dt != null)
            {
                DataGridTemplateColumn c = new DataGridTemplateColumn()
                {
                    CellTemplate = dt,
                    Header = e.Column.Header,
                    HeaderTemplate = e.Column.HeaderTemplate,
                    HeaderStringFormat = e.Column.HeaderStringFormat,
                    SortMemberPath = e.PropertyName // this is used to index into the DataRowView so it MUST be the property's name (for this implementation anyways)
                };
                e.Column = c;
            }
        }
    }

    public class A
    {
        public string Name { get; set; }
        public string GroupName { get; set; }
        public bool IsSelected { get; set; }
    }

    public class B
    {
        public string FullName { get; set; }
        public bool IsChecked { get; set; }
    }

    public class DataRowViewConverter : IValueConverter
    {
        #region IValueConverter Members

        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            DataGridCell cell = value as DataGridCell;
            if (cell == null)
                return null;

            System.Data.DataRowView drv = cell.DataContext as System.Data.DataRowView;
            if (drv == null)
                return null;

            return drv.Row[cell.Column.SortMemberPath];
        }

        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            throw new NotImplementedException();
        }

        #endregion
    }
}
使用系统;
使用System.Collections.Generic;
使用System.Linq;
使用系统文本;
使用System.Threading.Tasks;
使用System.Windows;
使用System.Windows.Controls;
使用System.Windows.Data;
使用System.Windows.Documents;
使用System.Windows.Input;
使用System.Windows.Media;
使用System.Windows.Media.Imaging;
使用System.Windows.Navigation;
使用System.Windows.Shapes;
命名空间WpfApplication17
{
/// 
///MainWindow.xaml的交互逻辑
/// 
公共部分类主窗口:窗口
{
public System.Data.DataTable项{get;set;}
公共主窗口()
{
初始化组件();
System.Data.DataTable dt=新的System.Data.DataTable();
添加(“StringColumn”,typeof(string));
添加(“IntColumn”,typeof(int));
添加(“AColumn1”,类型为(A));
添加(“AColumn2”,类型为(A));
添加(“B列1”,类型为(B));
添加(“B列2”,类型为(B));
dt.Rows.Add(
“测试字符串”,
123,
新的A(){Name=“A1”,GroupName=“GroupName”,IsSelected=true},
新的A(){Name=“A2”,GroupName=“GroupName”,IsSelected=false},
新的B(){FullName=“B1”,IsChecked=true},
新的B(){FullName=“B2”,IsChecked=false}
);
项目=dt;
this.DataContext=this;
}
私有void DataGrid_AutoGeneratingColumn(对象发送方,DataGridAutoGeneratingColumnEventArgs e)
{
数据模板dt=null;
如果(如PropertyType==typeof(A))
dt=(DataTemplate)资源[“ATemplate”];
else if(如PropertyType==typeof(B))
dt=(DataTemplate)资源[“BTemplate”];
如果(dt!=null)
{
DataGridTemplateColumn c=新DataGridTemplateColumn()
{
CellTemplate=dt,
页眉=e.Column.Header,
HeaderTemplate=e.Column.HeaderTemplate,
HeaderStringFormat=e.Column.HeaderStringFormat,
SortMemberPath=e.PropertyName//用于索引到DataRowView,因此它必须是属性的名称(无论如何,对于此实现)
};
e、 列=c;
}
}
}
公共A类
{
公共字符串名称{get;set;}
公共字符串组名{get;set;}
公共布尔值被选为{get;set;}
}
公共B级
{
公共字符串全名{get;set;}
已检查公共布尔值{get;set;}
}
公共类DataRowViewConverter:IValueConverter
{
#区域转换器成员
公共对象转换(对象值、类型targetType、对象参数、System.Globalization.CultureInfo区域性)
{
DataGridCell=作为DataGridCell的值;
if(单元格==null)
返回null;
System.Data.DataRowView drv=单元格.DataContext作为System.Data.DataRowView;
如果(drv==null)
返回null;
返回drv.Row[cell.Column.SortMemberPath];
}
公共对象转换回(对象值、类型targetType、对象参数、System.Globalization.CultureInfo区域性)
{
抛出新的NotImplementedException();
}
#端区
}
}

是的,对不起,我的错。。。那不行。我试过你写的每一篇文章,但是DataTemplate中的DataContext绑定除外。非常感谢你!我认为绑定到DataGridCell会导致内存泄漏(由于静态属性ChangedTracker)。绑定到非INotifyProper
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace WpfApplication17
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public System.Data.DataTable Items { get; set; }

        public MainWindow()
        {
            InitializeComponent();

            System.Data.DataTable dt = new System.Data.DataTable();
            dt.Columns.Add("StringColumn", typeof(string));
            dt.Columns.Add("IntColumn", typeof(int));
            dt.Columns.Add("AColumn1", typeof(A));
            dt.Columns.Add("AColumn2", typeof(A));
            dt.Columns.Add("BColumn1", typeof(B));
            dt.Columns.Add("BColumn2", typeof(B));

            dt.Rows.Add(
                "TestString",
                123,
                new A() { Name = "A1", GroupName = "GroupName", IsSelected = true },
                new A() { Name = "A2", GroupName = "GroupName", IsSelected = false },
                new B() { FullName = "B1", IsChecked=true },
                new B() { FullName = "B2", IsChecked=false }
            );

            Items = dt;
            this.DataContext = this;
        }

        private void DataGrid_AutoGeneratingColumn(object sender, DataGridAutoGeneratingColumnEventArgs e)
        {
            DataTemplate dt = null;
            if (e.PropertyType == typeof(A))
                dt = (DataTemplate)Resources["ATemplate"];
            else if (e.PropertyType == typeof(B))
                dt = (DataTemplate)Resources["BTemplate"];

            if (dt != null)
            {
                DataGridTemplateColumn c = new DataGridTemplateColumn()
                {
                    CellTemplate = dt,
                    Header = e.Column.Header,
                    HeaderTemplate = e.Column.HeaderTemplate,
                    HeaderStringFormat = e.Column.HeaderStringFormat,
                    SortMemberPath = e.PropertyName // this is used to index into the DataRowView so it MUST be the property's name (for this implementation anyways)
                };
                e.Column = c;
            }
        }
    }

    public class A
    {
        public string Name { get; set; }
        public string GroupName { get; set; }
        public bool IsSelected { get; set; }
    }

    public class B
    {
        public string FullName { get; set; }
        public bool IsChecked { get; set; }
    }

    public class DataRowViewConverter : IValueConverter
    {
        #region IValueConverter Members

        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            DataGridCell cell = value as DataGridCell;
            if (cell == null)
                return null;

            System.Data.DataRowView drv = cell.DataContext as System.Data.DataRowView;
            if (drv == null)
                return null;

            return drv.Row[cell.Column.SortMemberPath];
        }

        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            throw new NotImplementedException();
        }

        #endregion
    }
}