2010-02-03 30 views
8

Sto cercando di capire come associare un'intestazione di colonna di WPF DataGrid ei dati principali a un'origine dati utilizzando un modello MVVM. Il risultato che sto cercando sarebbe simile a questa:Intestazioni di colonne personalizzate per il binding Datagrid WPF

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

ho nello stile di successo le intestazioni qui, ma io sono sicuro come associare i valori nelle intestazioni. In particolare, la proprietà IsChecked della casella di controllo, l'indice selezionato della casella combinata e il valore della casella di testo.

In precedenza utilizzavo un semplice DataTable per popolare i dati della griglia principale, ma avrò bisogno di qualcosa di più complesso per contenere sia i dati della griglia che i valori per ogni colonna. O forse posso memorizzarli interamente come entità separate.

Quindi, qualcuno ha qualche idea di come potrei tirare fuori questa associazione? Una limitazione è che le colonne devono essere generate automaticamente poiché non ho idea di cosa saranno fino al runtime. L'applicazione carica semplicemente il modulo dati in un foglio di calcolo Excel e potrebbe essere presente un numero qualsiasi di colonne.

Grazie, Brian

risposta

7

Ecco quello che ho finito per fare per utilizzare questo con il modello MVVM:

ho due insiemi di dati per la rilegatura sul mio modello di vista: uno per i dati della griglia effettivi e uno per le intestazioni delle colonne .Attualmente questi sono esposti come due proprietà:

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

Il trucco per lavorare con due diversi set di dati è nella griglia. Ho sottoclassato il DataGrid e fornito alla griglia un'ulteriore fonte di dati chiamata ColumnSource, come proprietà di dipendenza. Questo è ciò che è associato a ColumnData sul mio modello di vista. Quindi imposto l'intestazione di ogni colonna generata automaticamente ai dati indicizzati in modo appropriato nell'origine dati ColumnSource. Il codice è il seguente:

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

} 

ora posso eseguire dati normali vincolante nell'intestazione su modelli dei miei articoli, che saranno tutti legano contro i dati nella proprietà ColumnData del mio modello di vista.

AGGIORNAMENTO: Mi è stato chiesto di mostrare XAML per la mia griglia. E 'davvero semplice, ma qui è:

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

Ed ecco l'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

Sarebbe interessante vedere l'intera soluzione. Ad esempio, che cosa è ImporterDataGridColumn? –

+0

È possibile ignorare ImporterDataGridColumn e utilizzare semplicemente DataGridTextColumn. È solo una colonna standard che ho creato una sottoclasse pensando che avrei bisogno di funzionalità aggiuntive, ma non l'ho fatto. L'intero codice della griglia è sopra. Il modello di visualizzazione espone le proprietà GridData e ColumnData sopra elencate, con i dati popolati da qualsiasi luogo. XAML aggancia quindi i due:

+0

Potrebbe (abbastanza per favore) aggiungere il vedi xaml per questo? –

0

Sono sicuramente un noob WPF/MVVM/associazione dati, ma hanno lavorato duramente su questa roba ultimamente. Non so cosa abbiate cablato finora, ma prima dovrete impostare DataContext per la vostra Vista. Dato che stai utilizzando MVVM, presumo che tu abbia un ViewModel, quindi dovrebbe essere DataContext per la tua vista.

cioè se avete il vostro CREATE VIEW/possedere il ViewModel, potrebbe essere simile a questa:

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

È possibile DataBind facilmente CheckBox, ComboBox e TextBox per immobili a vostro ViewModel. Ho trovato che il modo più semplice è quello di rendere il tuo ViewModel ereditato da una classe viewmodel di base, come lo one that Josh Smith wrote. Questo ti darà un metodo per chiamare internamente quando vuoi che ViewModel notifichi alla GUI eventuali cambiamenti di valori.

Supponendo di avere immobili come ImportColumn, Cognome, e LastNameText (tutti # proprietà C, non campi che chiamiamo OnPropertyChanged di conseguenza), allora il vostro XAML sarebbe simile a questa:

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

Spero che questo aiuta sei fuori. In caso contrario, si prega di commentare e cercherò di fare il meglio che posso per provare altre cose.

+0

Sto usando Caliburn e ho il mio ViewModel già creato e funzionante. Il problema è come dovrei usare il databinding per legare sia le intestazioni che i dati della griglia allo stesso tempo. Questo è dove sto lottando. –

0

Facciamo qualcosa di simile nella nostra app.

Quello che ho fatto è derivato dal mio tipo di colonna (DataGridSearchableBooleanColumn), quindi sostituisco il modello DataGridColumnHeader, ho messo due presentatori di contenuto. il primo che lego al contenuto (lo stesso del modello predefinito) il secondo che lego alla colonna. Io uso un modello di dati per la colonna (ne ho alcuni per diversi tipi di ricerca (testo, combo, booleano), quindi aggiungo le proprietà extra alla colonna in modo da poterli associare. Verifica se questo codice ha senso.

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

Questo è interessante, ma non sono sicuro che risolva il mio problema. Anch'io ho creato una nuova colonna personalizzata e sto sostituendo OnAutoGeneratingColumn e sostituendo la colonna esistente con la mia versione personalizzata. Il sistema sembra funzionare, ma penso che sia necessario scorrere le colonne per ottenere i dati, mentre sto cercando di collegarmi a un'origine dati esterna utilizzando il pattern MVVM. Se potessi ottenere l'indice della colonna corrente, penso che avrei una soluzione che utilizza una seconda fonte di dati. –

+0

questo è dove la griglia cade, non è completamente in grado in un modello mvvm perché la raccolta di colonne è di sola lettura (è possibile aggiungere rimuovere ma non impostare) I sottoclasse la griglia, dargli una proprietà extra che è un'interfaccia, quindi legalo al mio modello di vista. quindi lascia che gestisca le colonne nel codice. Un'altra soluzione è quella di creare una sottoclasse della griglia, assegnandole un'altra raccolta di colonne a cui è possibile collegarsi e osservare le modifiche su questo per aggiungere/rimuovere le colonne. (quindi il tuo modello di vista potrebbe legare direttamente) –