2014-09-03 12 views
5

Mam DataGrid z DataTable z jako ItemsSource. Liczba kolumn różni się od czasu do czasu. Jeśli DataType kolumny jest klasy A, chcę użyć DataTemplate w celu dostosowania wyglądu zawartości komórki.WPF DataGrid - Databind do komórki DataTable w CellTemplates DataTemplate

Mam ustawiony

AutoGenerateColumns="True" 

na DataGrid tak, że zostaną wygenerowane wszystkie kolumny w DataTable.

wymienić DataGridColumn z DataGridTemplateColumn jeśli typ danych jest typu 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 wygląda następująco.

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

radiobutton jest pokazany, ale mam wiążących błędów dla wszystkich właściwości, jak

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

klasy A wygląda to

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

Jak mogę databind DataTemplate w prawo komórka i własność?

(Jeśli masz rozwiązanie MVVM w których nie trzeba używać DataGrid_AutoGeneratingColumn byłoby wspaniale)

EDYCJA

Próbowałem to rozwiązanie też bez powodzenia. Tylko klasa jest wyświetlana w komórce jak zwykle, kiedy nie wie, jak ją wyrenderować.

<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

Tak, przepraszam, moja zła ... to nie zadziała. – Sheridan

Odpowiedz

8

Powiązania w szablonie nie działają, ponieważ DataContext jest DataRowView od DataTable.

Jednym z rozwiązań jest zmiana szablonu w celu ustawienia obiektu DataContext na żądany obiekt (typu A), wtedy wszystkie połączenia będą działały (nazwa, nazwa grupy, wyłączył się). Aby to zrobić, musisz wykonać konwerter i użyć szablonu.

DataContext w szablonie jest związany z jego przodkiem DataGridCell, który jest przekazywany do konwertera. Z komórki możemy uzyskać DataContext (DataRowView) i możemy uzyskać kolumnę komórki. Kiedy tworzymy kolumnę w DataGrid_AutoGeneratingColumn, ustawiamy SortMemberPath kolumny na e.PropertyName (nazwa kolumny w datatable). W konwerterze sprawdzamy obiekt w DataRowView.Row przy użyciu SortMemberPath jako indeksu. Zwracamy to jako DataContext dla szablonu.

Oto implementacja z klasą A i klasą B. Do tabeli z danymi dodałem dwie kolumny z każdej klasy, aby pokazać, że działa z wieloma instancjami.

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'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 
    } 
} 
+2

Próbowałem wszystko, co piszesz, ale powiązanie DataContext w DataTemplate. Dziękuję bardzo, bardzo! – AxdorphCoder

+0

Myślę, że Wiązanie z DataGridCell spowoduje wycieki pamięci (ze względu na static PropertyChangedTracker). Wiązanie ze źródłem innym niż INotifyPropertyChanged spowoduje wyciek. – ThumbGen

+0

W jaki sposób chciałbym programowo wygenerować DataTemplate w C#? – mpsyp

Powiązane problemy