2010-04-20 8 views
8

Ho provato di tutto e non sono riuscito a ottenere nulla, quindi spero che qualcuno possa darmi l'aha momento. Semplicemente non riesco a ottenere il binding per estrarre correttamente i dati nel datagrid.Associazione WPF DataGrid a DataTable utilizzando TemplateColumns

Ho un DataTable che contiene più colonne con di MyDataType

public class MyData 
{ 
    string nameData {get;set;} 
    bool showData {get;set;} 
} 

MyDataType ha 2 proprietà (una stringa, un valore booleano) ho creato un test DataTable

DataTable GetDummyData() 
{ 
    DataTable dt = new DataTable("Foo"); 
    dt.Columns.Add(new DataColumn("AnotherColumn", typeof(MyData))); 
    dt.Rows.Add(new MyData("Row1C1", true)); 
    dt.Rows.Add(new MyData("Row2C1", false)); 
    dt.AcceptChanges(); 
    return dt; 
} 

Ho WPF DataGrid che voglio mostrare il mio DataTable. Ma tutto quello che voglio fare è cambiare il modo in cui ogni cella viene renderizzata per mostrare [TextBlock] [Button] per cella con valori legati all'oggetto MyData e questo è dove sto avendo una tonnellata di problemi.

mio XAML assomiglia a questo

<Window.Resources> 
    <ResourceDictionary> 
     <DataTemplate x:Key="MyDataTemplate" DataType="MyData"> 
      <StackPanel Orientation="Horizontal" > 
       <Button Background="Green" HorizontalAlignment="Right" VerticalAlignment="Center" Margin="5,0,0,0" Content="{Binding Path=nameData}"></Button> 
       <TextBlock Background="Green" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="5,0,0,0" Text="{Binding Path=nameData}"></TextBlock> 
      </StackPanel> 
     </DataTemplate> 
    </ResourceDictionary> 
</Window.Resources> 
<Grid> 
    <dg:DataGrid Grid.Row="1" ItemsSource="{Binding}" AutoGenerateColumns="True" 
       x:Name="dataGrid1" SelectionMode="Single" CanUserAddRows="False" 
       CanUserSortColumns="true" CanUserDeleteRows="False" AlternatingRowBackground="AliceBlue" 
       AutoGeneratingColumn="dataGrid1_AutoGeneratingColumn" /> 
</Grid> 

Ora tutto io una volta caricato è quello di tentare di impegnare la DataTable al WPF DataGrid

dt = GetDummyData(); 
dataGrid1.ItemsSource = dt.DefaultView; 

Il TextBlock e Button si presentano, ma don 'legare, che li lascia vuoti. Qualcuno potrebbe farmi sapere se hanno qualche idea su come risolvere il problema. Questo dovrebbe essere semplice, questo è ciò che Microsoft ci porta a credere. Ho impostato il Column.CellTemplate durante l'evento AutoGenerating e non ottengo ancora alcun legame.

Si prega di aiutare !!!

+0

Hai provato l'impostazione della datatable da GetDummyData come il DataGrid.DataContext? – Ragepotato

+0

Già fatto e ancora la stessa cosa. Nulla cambia su UIElements in quanto sembrano non essere associati. Ho provato a utilizzare l'evento AutoGeneratingColumn per impostare il mio Column.CellTemplate e ancora nulla. Altre idee? Grazie Grazie –

+1

Prova questo: [C# Leggi Excel e Mostra in WPF DataGrid] (http://www.codearsenal.net/2012/06/c-sharp-read-excel-and-show-in-wpf.html) –

risposta

14

Edit: aggiornato per riflettere l'ingresso del Aran Mulholland (vedi commento)

A quanto pare il DataGrid sta passando l'intera DataRowView a ciascuna cella. Ecco perché la rilegatura non funziona. Il tuo DataTemplate si aspetta che il DataContext sia di tipo MyData, ma invece è di tipo DataRowView. La mia proposta (un po 'hack-ish) soluzione alternativa per ottenere il DataContext che si desidera è creare un DataGridTemplateColumn personalizzato che estrarrà l'elemento necessario dallo DataRowView. Il codice è qui sotto:

<Window x:Class="DataGridTemplateColumnSample.Window1" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    xmlns:dg="clr-namespace:Microsoft.Windows.Controls;assembly=WPFToolkit" 
    Title="Window1" Height="300" Width="300"> 
    <Window.Resources> 
     <ResourceDictionary> 
      <DataTemplate x:Key="MyDataTemplate" DataType="DataRowView"> 
       <StackPanel Orientation="Horizontal"> 
        <Button Background="Green" HorizontalAlignment="Right" VerticalAlignment="Center" Margin="5,0,0,0" Content="{Binding Path=nameData}"></Button> 
        <TextBlock Background="Green" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="5,0,0,0" Text="{Binding Path=nameData}"></TextBlock> 
       </StackPanel> 
      </DataTemplate> 
     </ResourceDictionary> 
    </Window.Resources> 
    <Grid> 
     <dg:DataGrid Grid.Row="1" AutoGenerateColumns="True" x:Name="dataGrid1" SelectionMode="Single" 
        CanUserAddRows="False" CanUserSortColumns="true" CanUserDeleteRows="False" 
        AlternatingRowBackground="AliceBlue" AutoGeneratingColumn="dataGrid1_AutoGeneratingColumn" 
        ItemsSource="{Binding}" VirtualizingStackPanel.VirtualizationMode="Standard" /> 
    </Grid> 
</Window> 

using System.Data; 
using System.Windows; 
using Microsoft.Windows.Controls; 

namespace DataGridTemplateColumnSample 
{ 
    public partial class Window1 
    { 
     public Window1() 
     { 
      InitializeComponent(); 
      DataContext = GetDummyData().DefaultView; 
     } 

     private static DataTable GetDummyData() 
     { 
      var dt = new DataTable("Foo"); 
      dt.Columns.Add(new DataColumn("OneColumn", typeof(MyData))); 
      dt.Columns.Add(new DataColumn("AnotherColumn", typeof(MyData))); 
      dt.Rows.Add(new MyData("Row1C1", true), new MyData("Row1C2", true)); 
      dt.Rows.Add(new MyData("Row2C1", false), new MyData("Row2C2", true)); 
      dt.AcceptChanges(); 
      return dt; 
     } 

     private void dataGrid1_AutoGeneratingColumn(object sender, DataGridAutoGeneratingColumnEventArgs e) 
     { 
      var column = new DataRowColumn(e.PropertyName); 
      column.Header = e.Column.Header; 
      column.CellTemplate = (DataTemplate)Resources["MyDataTemplate"]; 
      e.Column = column; 
     } 
    } 

    public class DataRowColumn : DataGridTemplateColumn 
    { 
     public DataRowColumn(string column) { ColumnName = column; } 
     public string ColumnName { get; private set; } 
     protected override FrameworkElement GenerateElement(DataGridCell cell, object dataItem) 
     { 
      var row = (DataRowView) dataItem; 
      var item = row[ColumnName]; 
      cell.DataContext = item; 
      var element = base.GenerateElement(cell, item); 
      return element; 
     } 
    } 

    public class MyData 
    { 
     public MyData(string name, bool data) { nameData = name; showData = data; } 
     public string nameData { get; set; } 
     public bool showData { get; set; } 
    } 
} 

Nota: Questo approccio sembra funzionare solo con il contenitore di virtualizzazione spento o in modalità standard. Se VirtualizationMode è impostato su Riciclaggio, il modello non viene applicato.

+0

il datagrid passa la riga a ogni colonna per generare la cella, questo è il comportamento standard, il legame sulla colonna definisce il contenuto delle celle. Penso che il tuo problema sia che stai facendo tutto sul codice. hai quantità dinamiche di colonne? se non solo imposta le tue colonne e i loro binding in xaml. –

+1

Sono d'accordo. Se questo fosse il mio progetto, tenterei di evitare di fare le cose in questo modo. Sfortunatamente i requisiti del progetto (ad esempio colonne dinamiche) a volte non ti lasciano molto spazio per fare le cose in modo "pulito". –

+0

Oh WOW. ECCEZIONALE. Funziona ... grazie. Questo risponde alla domanda al 100%. Avevo capito che era la Fila che veniva passata ma non riuscivo a capire come arrivare all'elemento in quella vista a cui legarsi. Molte grazie. Purtroppo non vedo come si eviterà di fare le cose in questo modo senza conoscere i dati in anticipo. Che tipo sconfigge l'obiettivo di avere un datagrid flessibile che funziona con colonne non di magazzino. Definendo il proprio DataTemplateColumn per le istanze in cui il tipo di dati è di tipo non di magazzino, avrei pensato che funzionasse immediatamente. Posso sognare, immagino. –

8

Dopo aver trovato questa discussione e avere problemi con il codice mostrato qui, ho trovato questo thread su MSDN, e funziona molto meglio! Nessun problema di virtualizzazione per quanto ho visto.

http://social.msdn.microsoft.com/Forums/en/wpf/thread/8b2e94b7-3c44-4642-8acc-851de5285062

Codice:

private void dataGrid1_AutoGeneratingColumn(object sender, Microsoft.Windows.Controls.DataGridAutoGeneratingColumnEventArgs e) 
{ 
    if (e.PropertyType == typeof(MyData)) 
    { 
     MyDataGridTemplateColumn col = new MyDataGridTemplateColumn(); 
     col.ColumnName = e.PropertyName; // so it knows from which column to get MyData 
     col.CellTemplate = (DataTemplate)FindResource("MyDataTemplate"); 
     e.Column = col; 
     e.Column.Header = e.PropertyName; 
    } 
} 

public class MyDataGridTemplateColumn : DataGridTemplateColumn 
{ 
    public string ColumnName 
    { 
     get; 
     set; 
    } 

    protected override System.Windows.FrameworkElement GenerateElement(DataGridCell cell, object dataItem) 
    { 
     // The DataGridTemplateColumn uses ContentPresenter with your DataTemplate. 
     ContentPresenter cp = (ContentPresenter)base.GenerateElement(cell, dataItem); 
     // Reset the Binding to the specific column. The default binding is to the DataRowView. 
     BindingOperations.SetBinding(cp, ContentPresenter.ContentProperty, new Binding(this.ColumnName)); 
     return cp; 
    } 
}