2014-09-03 13 views
5

Ho un DataGrid con un DataTable con come ItemsSource. Il numero di colonne differisce di volta in volta. Se il DataType di una colonna è di classe A, desidero utilizzare un DataTemplate per personalizzare l'aspetto del contenuto della cella.WPF DataGrid - DataBind alla cella DataTable in CellTemplates DataTemplate

Ho regolato

AutoGenerateColumns="True" 

sul DataGrid in modo che tutte le colonne della DataTable verrà generato.

sostituisce la DataGridColumn con un DataGridTemplateColumn se il tipo di dati è di tipo 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 assomiglia a questo.

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

il pulsante radio è mostrato, ma ottengo gli errori vincolanti per tutte le proprietà, come

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

Classe A si presenta come questo

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

Come posso DataBind DataTemplate verso destra cella e proprietà?

(Se si dispone di una soluzione MVVM in cui non devo usare DataGrid_AutoGeneratingColumn sarebbe bello)

EDIT

ho provato questa soluzione anche senza fortuna. Solo il nome di classe viene mostrato nella cella come al solito quando non sa come eseguire il rendering della classe.

<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

Sì, mi dispiace, mio ​​mal ... non funzionerà. – Sheridan

risposta

8

I collegamenti nel modello non funzionano perché DataContext è un DataRowView dal DataTable.

Una soluzione è modificare il modello per impostare DataContext sull'oggetto desiderato (di tipo A), quindi tutti i collegamenti funzioneranno (Nome, NomeGruppo, IsSelected). Per fare ciò, è necessario creare un convertitore e utilizzarlo come modello.

Il DataContext nel modello, è associato al suo antenato DataGridCell che viene passato nel convertitore. Dalla cella, possiamo ottenere DataContext (DataRowView) e possiamo ottenere la colonna della cella. Quando creiamo la colonna in DataGrid_AutoGeneratingColumn, impostiamo SortMemberPath della colonna su e.PropertyName (il nome della colonna nel datatable). Nel convertitore, cerchiamo l'oggetto in DataRowView.Row utilizzando SortMemberPath come indice. Restituiamo questo come DataContext per il modello.

Ecco l'implementazione con una classe A e una classe B. Ho aggiunto due colonne di ogni classe alla mia tabella di dati per mostrare che funziona con più istanze.

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

Avevo provato a scrivere qualsiasi cosa, ma il collegamento DataContext nel DataTemplate. Grazie mille! – AxdorphCoder

+0

Penso che il collegamento a DataGridCell causerà perdite di memoria (a causa di static PropertyChangedTracker). Il collegamento a una fonte non -InotifyPropertyChanged causerà una perdita. – ThumbGen

+0

Come potrei fare per generare un DataTemplate a livello di codice in C#? – mpsyp

Problemi correlati