我有一个带有DataTable的DataGrid和ItemsSource。列数随时间而不同。如果列的DataType是A类,我想使用DataTemplate来自定义单元格内容的外观。
我已经设置了
AutoGenerateColumns="True"
在DataGrid上,以便生成DataTable中的所有列。
如果DataType是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看起来像这样。
<DataTemplate x:Key="ATemplate">
<RadioButton Content="{Binding Name}" GroupName="{Binding GroupName}" IsChecked="{Binding IsSelected}" />
</DataTemplate>
显示了radiobutton,但是我得到了所有属性的绑定错误,例如
BindingExpression path error: 'IsSelected' property not found on 'object' ''DataRowView'
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>
答案 0 :(得分:10)
模板中的绑定不起作用,因为DataContext是DataTable中的DataRowView。
一种解决方案是更改模板以将DataContext设置为所需的对象(类型A),然后所有绑定都可以使用(Name,GroupName,IsSelected)。为此,您需要制作转换器并让模板使用它。
模板中的DataContext绑定到它的DataGridCell祖先,它传递给转换器。从单元格中,我们可以获取DataContext(DataRowView),我们可以获取单元格的列。当我们在DataGrid_AutoGeneratingColumn中创建列时,我们将列的SortMemberPath设置为e.PropertyName(数据表中列的名称)。在转换器中,我们使用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
}
}