2010-02-03 21 views
8

Ich versuche herauszufinden, wie eine WPF DataGrid-Spaltenüberschrift und Hauptdaten an eine Datenquelle mit einem MVVM-Muster zu binden. Das Ergebnis ich suche würde wie folgt aussehen:WPF-Datagrid-Bindung benutzerdefinierte Spaltenüberschriften

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

Ich habe erfolgreich die Header hier gestylt, aber ich bin nicht sicher, wie die Werte in dem Header zu binden. Insbesondere die IsChecked-Eigenschaft des Kontrollkästchens, der ausgewählte Index des Kombinationsfelds und der Wert des Textfelds.

Ich habe zuvor eine einfache DataTable verwendet, um die Hauptgitterdaten zu füllen, aber ich brauche etwas Komplexeres, um sowohl die Gitterdaten als auch die Werte für jede Spalte aufzunehmen. Oder vielleicht kann ich sie vollständig als separate Entitäten speichern.

Also, hat jemand eine Idee, wie ich diese Bindung abziehen könnte? Eine Einschränkung besteht darin, dass die Spalten automatisch generiert werden müssen, da ich bis zur Laufzeit keine Ahnung habe, was sie sein werden. Die Anwendung lädt einfach die Daten aus einer Excel-Tabelle und es kann eine beliebige Anzahl von Spalten vorhanden sein.

Danke, Brian

Antwort

7

Hier ist, was ich das mit dem Muster MVVM tat am Ende zu verwenden:

ich habe zwei Sätze von Daten für auf meiner Ansicht nach Modell Bindung: ein für die tatsächlichen Netzdaten und eine für die Spaltenüberschriften .Gegenwärtig sind diese als zwei Eigenschaften exponiert:

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

Der Trick zum Arbeiten mit zwei unterschiedlichen Datensätzen ist im Raster. Ich habe das DataGrid unterklassifiziert und dem Gitter eine zusätzliche Datenquelle namens ColumnSource als Abhängigkeitseigenschaft gegeben. Dies ist, was an die ColumnData in meinem Ansichtsmodell gebunden ist. Ich setze dann den Header jeder automatisch generierten Spalte auf die entsprechend indizierten Daten in der ColumnSource-Datenquelle. Der Code lautet wie folgt:

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

} 

ich jetzt normale Datenbindung in den Templat-Header meiner Spalten durchführen können, das wird alle binden gegen die Daten in der ColumnData Eigenschaft meiner Ansicht Modell.

UPDATE: Ich wurde gebeten, den XAML für mein Raster zu zeigen. Es ist wirklich einfach, aber hier ist es:

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

Und hier ist der 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

Wäre interessant, wenn Sie die ganze Lösung sehen. Was ist zum Beispiel "ImporterDataGridColumn"? –

+0

Sie können ImporterDataGridColumn ignorieren und nur DataGridTextColumn verwenden. Es ist nur eine Standardspalte, die ich unterklassifiziert habe und dachte, dass ich zusätzliche Funktionalität benötigen würde, aber nicht. Der gesamte Netzcode ist oben. Das Ansichtsmodell stellt die oben aufgeführten Eigenschaften GridData und ColumnData zur Verfügung, wobei Daten von wo immer Sie möchten aufgefüllt werden. Das XAML hakt dann die beiden nach oben:

+0

Könnten Sie (hübsch bitte) hinzufügen Ansicht xaml dafür? –

0

Ich bin auf jeden Fall ein WPF/MVVM/Databinding noob, aber in letzter Zeit haben hart an diesem Zeug gearbeitet. Ich weiß nicht, was Sie bisher verdrahtet haben, aber zuerst sollten Sie den DataContext für Ihre Ansicht einstellen. Da Sie MVVM verwenden, nehme ich an, Sie haben ein ViewModel, also sollte dies der DataContext für Ihre View sein.

das heißt, wenn Sie die Ansicht erstellen lassen/besitzt Ihr Ansichtsmodell, es könnte wie folgt aussehen:

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

Sie können leicht Databind Ihre CheckBox, ComboBox und TextBox zu Immobilien in Ihrem Ansichtsmodell. Ich habe festgestellt, der einfachste Weg besteht darin, Ihr ViewModel von einer Basis-Viewmodel-Klasse wie der one that Josh Smith wrote zu erben. Dadurch erhalten Sie eine Methode, intern aufzurufen, wenn das ViewModel die GUI über Änderungen der Werte benachrichtigen soll.

Angenommen, Sie haben Eigenschaften wie ImportColumn, Nachname und LastNameText (alle # Eigenschaften C, nicht Felder, die entsprechend OnPropertyChanged nennen), dann XAML würde wie folgt aussehen:

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

Ich hoffe, das hilft du bist raus. Wenn nicht, bitte Kommentar und ich werde versuchen, so gut ich kann, um andere Dinge auszuprobieren.

+0

ich Caliburn verwende und meine Viewmodel haben bereits erstellt und arbeiten. Das Problem ist, wie ich Datenbindung verwenden soll, um sowohl die Header- als auch die Rasterdaten gleichzeitig zu binden. Hier kämpfe ich. –

0

Wir machen etwas ähnliches in unserer App.

Was ich getan habe, ist mein eigener Spaltentyp (DataGridSearchableBooleanColumn) abgeleitet, dann ersetze ich die DataGridColumnHeader Vorlage, ich lege zwei Content Presenter drin. die erste bind ich an den Inhalt (die gleiche wie die Standardvorlage) die zweite ich binde an die Spalte. Ich benutze eine Datenvorlage für die Spalte (ich habe ein paar von ihnen für verschiedene Sucharten (Text, Combo, Boolean). Dann füge ich die zusätzlichen Eigenschaften der Spalte, so dass ich an sie binden kann. Sehen Sie, wenn dieser Code Sinn macht.

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

Das ist interessant, aber ich bin mir nicht sicher, ob es mein Problem löst. Ich habe auch eine neue benutzerdefinierte Spalte erstellt und überschreibe OnAutoGeneratingColumn und ersetze die vorhandene Spalte durch meine benutzerdefinierte Version. Ihr System scheint zu funktionieren, aber ich denke, Sie müssen die Spalten durchlaufen, um die Daten zu erhalten, während ich versuche, mit dem MVVM-Muster an eine externe Datenquelle zu binden. Wenn ich den Index der aktuellen Spalte erhalten könnte, würde ich eine Lösung mit einer zweiten Datenquelle haben. –

+0

Dies ist, wo das Raster fällt, es ist nicht vollständig in einem MVVM-Muster möglich, weil die Spalten Sammlung schreibgeschützt ist (Sie können hinzufügen, aber nicht gesetzt) ​​Ich Unterklasse das Gitter, geben Sie eine zusätzliche Eigenschaft, die eine Schnittstelle ist, Dann binden Sie das an mein Ansichtsmodell. dann lass es die Spalten im Code verwalten. Eine andere Lösung besteht darin, das Gitter zu untergliedern, ihm eine andere Spaltensammlung zu geben, an die Sie binden können, und nach Änderungen zu suchen, um Ihre Spalten hinzuzufügen/zu entfernen. (Dann könnte Ihr Ansichtsmodell direkt binden) –

Verwandte Themen