WPF DataGrid - Binding data to a DataTable cell in a CellTemplates DataTemplate

I have a DataGrid with a DataTable using ItemsSource. The number of columns varies from time to time. If the column's DataType is class A, I want to use a DataTemplate to customize the appearance of the contents of the cell.

I have installed

AutoGenerateColumns="True" 

in the DataGrid so that all columns in the DataTable are generated.

I am replacing a DataGridColumn with a DataGridTemplateColumn if the DataType is of type A

 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 is as follows.

 <DataTemplate x:Key="ATemplate"> <RadioButton Content="{Binding Name}" GroupName="{Binding GroupName}" IsChecked="{Binding IsSelected}" /> </DataTemplate> 

A radio object is displayed, but I get binding errors for all properties, for example

 BindingExpression path error: 'IsSelected' property not found on 'object' ''DataRowView' 

Class A looks like this:

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

How can I bind a DataTemplate to the right cell and property?

(If you have an MVVM solution in which I don't need to use DataGrid_AutoGeneratingColumn, it would be great)

EDIT

I also tried this solution, no luck. Only the cool name is displayed in the cell, as usual, when it does not know how to display the class.

 <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> 
+5
source share
1 answer

Template binding does not work because the DataContext is a DataRowView of a DataTable.

One solution is to modify your template to set the DataContext to the object you want (type A), then all your bindings will work (Name, GroupName, IsSelected). To do this, you will need to make a converter and use it.

The DataContext in the template is bound to this DataGridCell ancestor, which is passed to the converter. From the cell, we can get the DataContext (DataRowView), and we can get the Column cell. When we create a column in the DataGrid_AutoGeneratingColumn, we set the SortMemberPath column to e.PropertyName (column name in datatable). In the converter, we view the object in DataRowView.Row, using SortMemberPath as an index. We return this as a DataContext for the template.

Here is an implementation with class A and class B. I added two columns of each class to my data table to show that it works with multiple instances.

enter image description here

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 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 } } 
+8
source

Source: https://habr.com/ru/post/1201725/


All Articles