2010-04-20 14 views
8

He intentado todo y no he llegado a ninguna parte, así que espero que alguien me pueda dar el momento. Simplemente no puedo obtener el enlace para extraer los datos en la cuadrícula de datos con éxito.Encuadernación WPF DataGrid a DataTable usando TemplateColumns

I tienen una DataTable que contiene varias columnas con de MyDataType

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

MyDataType tiene 2 propiedades (una cadena, un booleano) he creado una prueba 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; 
} 

tengo una WPF DataGrid que quiero mostrar mi DataTable. Pero todo lo que quiero hacer es cambiar cómo se representa cada celda para mostrar [TextBlock] [Button] por celda con valores vinculados al objeto MyData y aquí es donde tengo una tonelada de problemas.

Mi XAML se parece a esto

<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> 

Ahora todo lo que hago una vez cargado es tratar de obligar a la DataTable a la WPF DataGrid

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

El TextBlock y Button se presenta, pero Don 't bind, que los deja en blanco. ¿Alguien podría decirme si tienen alguna idea de cómo solucionar esto? Esto debería ser simple, eso es lo que Microsoft nos hace creer. He configurado el Column.CellTemplate durante el evento AutoGenerating y todavía no obtengo ningún enlace.

Por favor ayuda!

+0

¿Usted intentó establecer la tabla de datos de GetDummyData como el DataGrid.DataContext? – Ragepotato

+0

Ya lo hice y sigue siendo lo mismo. No hay cambios en los elementos de UIE ya que parecen estar libres. He intentado utilizar el Evento AutoGeneratingColumn para establecer mi propia Column.CellTemplate y aún nada. ¿Alguna otra idea? Gracias Gracias –

+1

Pruebe esto: [C# Lea Excel y muestre en WPF DataGrid] (http://www.codearsenal.net/2012/06/c-sharp-read-excel-and-show-in-wpf.html) –

Respuesta

14

Editar: Se ha actualizado para reflejar la entrada de Aran Mulholland (ver comentario)

Al parecer, el DataGrid está pasando todo el DataRowView a cada célula. Es por eso que el enlace no funciona. Su DataTemplate espera que el DataContext sea del tipo MyData, pero en su lugar es del tipo DataRowView. Mi solución propuesta (algo hack-ish) para obtener el DataContext que desea es crear un DataGridTemplateColumn personalizado que extraerá el elemento necesario del DataRowView. El código es el siguiente:

<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: Este enfoque sólo parece funcionar con la virtualización de contenedores apagado o en modo estándar. Si el VirtualizationMode está configurado para Reciclar, la plantilla no se aplica.

+0

la cuadrícula de datos pasa la fila a cada columna para generar la celda, este es el comportamiento estándar, el enlace en la columna define el contenido de las celdas. Creo que tu problema es que estás haciendo todo en código. ¿tiene cantidades dinámicas de columnas? si no solo configura tus columnas y sus enlaces en xaml. –

+1

Estoy de acuerdo. Si este fuera mi proyecto, trataría de evitar hacer las cosas de esta manera. Lamentablemente, los requisitos del proyecto (por ejemplo, columnas dinámicas) a veces no le dejan mucho espacio para hacer las cosas de la manera "limpia". –

+0

Oh WOW. INCREÍBLE. Funciona ... Gracias. Eso responde la pregunta al 100%. Me había dado cuenta de que era la fila que se pasa, pero no pude encontrar la manera de llegar al elemento en esa vista para enlazar. Muchas gracias. Lamentablemente, no veo cómo evitaría hacer las cosas de esta manera sin conocer los datos por adelantado. Lo cual un poco derrota el objetivo de tener una cuadrícula de datos flexible que funciona con columnas sin stock. La definición de su propia DataTemplateColumn para las instancias en las que el tipo de datos es un tipo de columna sin stock, habría pensado que saldría de la caja. Puedo soñar, supongo. –

8

Después de encontrar este hilo y tener problemas con el código que se muestra aquí, me encontré con este hilo en MSDN, ¡y funciona mucho mejor! Sin problemas de virtualización, por lo que he visto.

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

Código:

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; 
    } 
}