2010-02-03 27 views
8

Estoy tratando de averiguar cómo vincular un encabezado de columna WGT DataGrid y los datos principales a una fuente de datos utilizando un patrón MVVM. El resultado Busco sería el siguiente:WPF Datagrid vinculando encabezados de columna personalizados

alt text http://brian.vallelunga.com/files/datagrid-custom-headers.PNG

he labrado con éxito las cabeceras aquí, pero estoy seguro de cómo se unen los valores en las cabeceras. Específicamente, la propiedad IsChecked de la casilla de verificación, el índice seleccionado del cuadro combinado y el valor del cuadro de texto.

Antes estaba usando una DataTable simple para completar los datos de la grilla principal, pero voy a necesitar algo más complejo para contener los datos de la grilla y los valores para cada columna. O tal vez puedo almacenarlos como entidades separadas por completo.

Entonces, ¿alguien tiene alguna idea de cómo podría llevar a cabo esta unión? Una limitación es que las columnas deben generarse automáticamente ya que no tengo idea de lo que serán hasta el tiempo de ejecución. La aplicación simplemente carga los datos de una hoja de cálculo de Excel y puede haber cualquier cantidad de columnas presentes.

Gracias, Brian

Respuesta

7

esto es lo que terminé haciendo a usar esto con el patrón MVVM:

que tienen dos conjuntos de datos para la unión en mi modelo de vista: uno para los datos de la cuadrícula reales y otra para los encabezados de columna .Actualmente éstas son expuestas como dos propiedades:

// INotifyPropertyChanged support not shown for brevity 
public DataTable GridData { get; set; } 
public BindingList<ImportColumnInfo> ColumnData { get; set; } 

El truco para trabajar con dos conjuntos diferentes de datos se encuentran en la red. He subclasificado el DataGrid y dado a la cuadrícula una fuente de datos adicional llamada ColumnSource, como una propiedad de dependencia. Esto es lo que está ligado a ColumnData en mi modelo de vista. Luego configuro el encabezado de cada columna generada automáticamente en los datos adecuadamente indexados en la fuente de datos de ColumnSource. El código es el siguiente:

public class ImporterDataGrid : DataGrid 
{ 
    protected override void OnAutoGeneratingColumn(DataGridAutoGeneratingColumnEventArgs e) 
    { 
     base.OnAutoGeneratingColumn(e); 

     int columnIndex = this.Columns.Count; 
     var column = new ImporterDataGridColumn(); 
     column.Header = ColumnSource[columnIndex]; 
     column.Binding = new Binding(e.PropertyName) { Mode = BindingMode.OneWay }; 
     e.Column = column; 
    } 

    public IList ColumnSource 
    { 
     get { return (IList)GetValue(ColumnSourceProperty); } 
     set { SetValue(ColumnSourceProperty, value); } 
    } 

    public static readonly DependencyProperty ColumnSourceProperty = DependencyProperty.Register("ColumnSource", typeof(IList), typeof(ImporterDataGrid), new FrameworkPropertyMetadata(null)); 

} 

ahora puedo realizar la unión de datos normales en la cabecera de plantilla de mis columnas, que se unen todos en contra de los datos en la propiedad ColumnData de mi modelo de vista.

ACTUALIZACIÓN: Me pidieron que mostrara el XAML para mi grilla. Es muy básico, pero aquí está:

<Controls:ImporterDataGrid 
    AutoGenerateColumns="True" x:Name="previewDataGrid" 
    VerticalScrollBarVisibility="Visible" 
    HorizontalScrollBarVisibility="Visible" 
    IsReadOnly="True" 
    SelectionMode="Extended" 
    HeadersVisibility="Column" 
    ItemsSource="{Binding PreviewData}" 
    ColumnSource="{Binding PreviewColumnData}" 
    Style="{StaticResource ImporterDataGridStyle}" 
    Background="White" CanUserReorderColumns="False" CanUserResizeRows="False" 
    CanUserSortColumns="False" AlternatingRowBackground="#FFFAFAFA" AllowDrop="True" /> 

Y aquí es la ImporterColumnHeaderStyle:

<Style x:Key="ImporterDataGridColumnHeaderStyle" TargetType="{x:Type toolkit:DataGridColumnHeader}"> 
    <Setter Property="Template"> 
     <Setter.Value> 
      <ControlTemplate TargetType="{x:Type toolkit:DataGridColumnHeader}"> 
       <Grid> 
        <toolkit:DataGridHeaderBorder Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Padding="{TemplateBinding Padding}" IsClickable="{TemplateBinding CanUserSort}" IsHovered="False" IsPressed="False" SortDirection="{TemplateBinding SortDirection}"> 
         <Grid> 
          <CheckBox Height="16" Margin="6,6,16,0" Name="importCheckBox" IsChecked="{Binding Path=Import}" VerticalAlignment="Top">Import Column</CheckBox> 
          <StackPanel IsEnabled="{Binding Path=Import}"> 
           <ComboBox Height="24" Margin="6,29,6,0" Name="columnTypeComboBox" VerticalAlignment="Top" SelectedValue="{Binding ColumnType}" ItemsSource="{Binding Source={local:EnumList {x:Type Models:ImportColumnType}}}"> 
           </ComboBox> 
           <TextBox Height="23" Margin="6,6,6,33" Name="customHeadingTextBox" VerticalAlignment="Bottom" Text="{Binding Path=CustomColumnName}" IsEnabled="{Binding ColumnType, Converter={StaticResource ColumnTypeToBooleanConverter}}" /> 
          </StackPanel> 
          <TextBlock Height="20" Margin="6,0,6,7" Name="originalHeadingTextBlock" Text="{Binding Path=OriginalColumnName}" VerticalAlignment="Bottom" Foreground="Gray" /> 
         </Grid> 
        </toolkit:DataGridHeaderBorder> 

        <Thumb x:Name="PART_LeftHeaderGripper" HorizontalAlignment="Left"> 
         <Thumb.Style> 
          <Style TargetType="{x:Type Thumb}"> 
           <Setter Property="Width" Value="8"/> 
           <Setter Property="Background" Value="Transparent"/> 
           <Setter Property="Cursor" Value="SizeWE"/> 
           <Setter Property="Template"> 
            <Setter.Value> 
             <ControlTemplate TargetType="{x:Type Thumb}"> 
              <Border Background="{TemplateBinding Background}" Padding="{TemplateBinding Padding}"/> 
             </ControlTemplate> 
            </Setter.Value> 
           </Setter> 
          </Style> 
         </Thumb.Style> 
        </Thumb> 
        <Thumb x:Name="PART_RightHeaderGripper" HorizontalAlignment="Right"> 
         <Thumb.Style> 
          <Style TargetType="{x:Type Thumb}"> 
           <Setter Property="Width" Value="8"/> 
           <Setter Property="Background" Value="Transparent"/> 
           <Setter Property="Cursor" Value="SizeWE"/> 
           <Setter Property="Template"> 
            <Setter.Value> 
             <ControlTemplate TargetType="{x:Type Thumb}"> 
              <Border Background="{TemplateBinding Background}" Padding="{TemplateBinding Padding}"/> 
             </ControlTemplate> 
            </Setter.Value> 
           </Setter> 
          </Style> 
         </Thumb.Style> 
        </Thumb> 
       </Grid> 
      </ControlTemplate> 
     </Setter.Value> 
    </Setter> 
</Style> 
+1

Sería interesante ver toda la solución. Por ejemplo, ¿qué es ImporterDataGridColumn? –

+0

Puede ignorar ImporterDataGridColumn y simplemente usar DataGridTextColumn. Es solo una columna estándar que subclasé pensando que necesitaría funcionalidad adicional, pero no lo hice. Todo el código de cuadrícula es anterior. El modelo de vista expone las propiedades GridData y ColumnData enumeradas anteriormente, con datos llenos desde donde desee. El XAML luego engancha los dos:

+0

¿Podría (bastante) agregar el ver xaml para esto? –

0

Definitivamente soy un novato WPF/MVVM/enlace de datos, pero han estado trabajando duro en esto últimamente. No sé lo que ha cableado hasta ahora, pero primero querrá configurar el DataContext para su Vista. Como está utilizando MVVM, supongo que tiene un ViewModel, por lo que debería ser el DataContext para su View.

es decir, si usted tiene su CREATE VIEW/dueño de su modelo de vista, podría ser algo como esto:

MyViewModel vm = new MyViewModel(); 
this.DataContext = vm; 

Puede databind fácilmente su CheckBox, ComboBox y TextBox a propiedades en el modelo de vista. He encontrado que la manera más fácil es hacer que tu ViewModel herede de una clase de viewmodel base, como el one that Josh Smith wrote. Esto le dará un método para llamar internamente cuando desee que ViewModel notifique a la GUI de cualquier cambio en los valores.

Asumiendo que tiene propiedades como ImportColumn, Apellido, y LastNameText (Todas las propiedades de C#, no campos esa llamada OnPropertyChanged en consecuencia), entonces su XAML sería algo como esto:

<CheckBox IsChecked="{Binding ImportColumn}" /> 
<ComboBox SelectedItem="{Binding LastName}" /> 
<TextBox Text="{Binding LastName Text, Mode=TwoWay}" /> 

Espero que esto ayude vete. Si no, por favor comenten e intentaré hacer lo mejor que pueda para probar otras cosas.

+0

estoy usando Caliburn y tengo mi modelo de vista ya creados y en funcionamiento. El problema es cómo se supone que debo usar el enlace de datos para vincular los datos de los encabezados y la cuadrícula al mismo tiempo. Aquí es donde estoy luchando. –

0

Hacemos algo similar en nuestra aplicación.

Lo que he hecho deriva de mi propio tipo de columna (DataGridSearchableBooleanColumn), luego reemplazo la plantilla DataGridColumnHeader, puse dos presentadores de contenido allí. el primero me enlace al contenido (lo mismo que la plantilla predeterminada) el segundo me enlace a la columna. Utilizo una plantilla de datos para la columna (tengo algunos de ellos para diferentes tipos de búsqueda (texto, combo, booleano). Luego agrego las propiedades adicionales a la columna para poder enlazarlos. Vea si este código tiene sentido.

<!--Style for the datagrid column headers, contains a text box for searching--> 
    <Style 
     x:Key="columnHeaderStyle" 
     TargetType="dg:DataGridColumnHeader"> 
     <Setter 
     Property="Foreground" 
     Value="#FF000000" /> 
     <Setter 
     Property="HorizontalContentAlignment" 
     Value="Left" /> 
     <Setter 
     Property="VerticalContentAlignment" 
     Value="Center" /> 
     <Setter 
     Property="IsTabStop" 
     Value="False" /> 
     <Setter 
     Property="Padding" 
     Value="1,2,1,2" /> 
     <Setter 
     Property="Template"> 
     <Setter.Value> 
      <ControlTemplate 
       TargetType="dg:DataGridColumnHeader"> 
       <Grid 
        x:Name="Root"> 
        <dg:DataGridHeaderBorder 
        Background="{TemplateBinding Background}" 
        BorderBrush="{TemplateBinding BorderBrush}" 
        BorderThickness="{TemplateBinding BorderThickness}" 
        Padding="{TemplateBinding Padding}" 
        IsClickable="{TemplateBinding CanUserSort}" 
        IsHovered="{TemplateBinding IsMouseOver}" 
        IsPressed="{TemplateBinding IsPressed}" 
        SeparatorBrush="{TemplateBinding SeparatorBrush}" 
        SeparatorVisibility="{TemplateBinding SeparatorVisibility}" 
        SortDirection="{TemplateBinding SortDirection}"> 

        <Grid 
         HorizontalAlignment="Stretch" 
         Margin="{TemplateBinding Padding}" 
         VerticalAlignment="{TemplateBinding VerticalContentAlignment}"> 
         <Grid.Resources> 
          <DataTemplate 
           DataType="{x:Type local:DataGridSearchableBooleanColumn}"> 
           <CheckBox 
           Margin="0,5,0,0" 
           IsThreeState="True" 
           IsChecked="{Binding Path=IsChecked}" /> 
          </DataTemplate> 
         </Grid.Resources> 
         <Grid.ColumnDefinitions> 
          <ColumnDefinition /> 
          <ColumnDefinition 
           Width="Auto" /> 
         </Grid.ColumnDefinitions> 
         <Grid.RowDefinitions> 
          <RowDefinition 
           Height="19" /> 
          <RowDefinition 
           Height="Auto" /> 
         </Grid.RowDefinitions> 

         <ContentPresenter 
          Content="{Binding RelativeSource={RelativeSource TemplatedParent}, Mode=OneWay, Path=Content}" /> 

         <Path 
          x:Name="SortIcon" 
          Fill="#FF444444" 
          Stretch="Uniform" 
          HorizontalAlignment="Left" 
          Margin="4,0,0,0" 
          VerticalAlignment="Center" 
          Width="8" 
          Opacity="0" 
          RenderTransformOrigin=".5,.5" 
          Grid.Column="1" 
          Data="F1 M -5.215,6.099L 5.215,6.099L 0,0L -5.215,6.099 Z "> 
          <Path.RenderTransform> 
           <ScaleTransform 
           ScaleX=".9" 
           ScaleY=".9" /> 
          </Path.RenderTransform> 
         </Path> 
         <ContentPresenter 
          x:Name="columnHeaderContentPresenter" 
          Content="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Column}" 
          Grid.Row="1" 
          Grid.ColumnSpan="2" 
          Margin="0,0,0,0" /> 
        </Grid> 
        </dg:DataGridHeaderBorder> 

        <Thumb 
        x:Name="PART_LeftHeaderGripper" 
        HorizontalAlignment="Left"> 
        <Thumb.Style> 
         <Style 
          TargetType="{x:Type Thumb}"> 
          <Setter 
           Property="Width" 
           Value="8" /> 
          <Setter 
           Property="Background" 
           Value="Transparent" /> 
          <Setter 
           Property="Cursor" 
           Value="SizeWE" /> 
          <Setter 
           Property="Template"> 
           <Setter.Value> 
           <ControlTemplate 
            TargetType="{x:Type Thumb}"> 
            <Border 
             Background="{TemplateBinding Background}" 
             Padding="{TemplateBinding Padding}" /> 
           </ControlTemplate> 
           </Setter.Value> 
          </Setter> 
         </Style> 
        </Thumb.Style> 
        </Thumb> 
        <Thumb 
        x:Name="PART_RightHeaderGripper" 
        HorizontalAlignment="Right"> 
        <Thumb.Style> 
         <Style 
          TargetType="{x:Type Thumb}"> 
          <Setter 
           Property="Width" 
           Value="8" /> 
          <Setter 
           Property="Background" 
           Value="Transparent" /> 
          <Setter 
           Property="Cursor" 
           Value="SizeWE" /> 
          <Setter 
           Property="Template"> 
           <Setter.Value> 
           <ControlTemplate 
            TargetType="{x:Type Thumb}"> 
            <Border 
             Background="{TemplateBinding Background}" 
             Padding="{TemplateBinding Padding}" /> 
           </ControlTemplate> 
           </Setter.Value> 
          </Setter> 
         </Style> 
        </Thumb.Style> 
        </Thumb> 
       </Grid> 
      </ControlTemplate> 
     </Setter.Value> 
     </Setter> 
    </Style> 
+0

Esto es interesante, pero no estoy seguro de que resuelva mi problema. Yo también he creado una nueva columna personalizada y estoy anulando OnAutoGeneratingColumn y reemplazando la columna existente con mi versión personalizada. Parece que su sistema funciona, pero creo que debe recorrer las columnas para obtener los datos, mientras que estoy tratando de enlazar a una fuente de datos externa utilizando el patrón MVVM. Si pudiera obtener el índice de la columna actual, creo que tendría una solución con una segunda fuente de datos. –

+0

aquí es donde la rejilla cae, no es totalmente factible en un patrón mvvm porque la colección de columnas es de solo lectura (puede agregar eliminar pero no establecer) Subclase la grilla, le doy una propiedad adicional que es una interfaz, luego ata esto a mi modelo de vista. luego deja que administre las columnas en el código. Otra solución es subclasificar la cuadrícula, darle otra colección de columnas a la que pueda vincularse y observar cambios en esto para agregar/eliminar sus columnas. (entonces su modelo de vista podría enlazar directamente) –