2010-02-03 138 views
8

我想弄清楚如何將WPF DataGrid的列標題和主數據綁定到使用MVVM模式的數據源。我在尋找的結果是這樣的:WPF Datagrid綁定自定義列標題

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

我已經成功地風格的頭在這裏,但我不能確定如何將值綁定的頭。具體而言,複選框的IsChecked屬性,組合框的選定索引和文本框的值。

我以前使用一個簡單的DataTable來填充主要的網格數據,但我將需要更復雜的東西來保存網格數據和每列的值。或者也許我可以將它們完全存儲爲單獨的實體。

那麼,有沒有人有任何想法,我可能會取消這種綁定?一個限制是列必須是自動生成的,因爲我不知道直到運行時它們會是什麼。應用程序只是將數據從Excel電子表格加載,並且可能存在任意數量的列。

感謝, 布賴恩

回答

7

這裏就是我終於實現了與MVVM模式使用此:

我有兩組數據對我的視圖模型綁定:一個用於實際電網數據,一個用於列標題。目前這些作爲兩個屬性公開:

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

處理兩組不同的數據的技巧是在網格中。我已經將DataGrid子類化併爲Grid指定了一個名爲ColumnSource的附加數據源,作爲依賴項屬性。這是我的視圖模型上綁定到ColumnData的內容。然後,我將每個自動生成列的標題設置爲ColumnSource數據源中適當索引的數據。代碼如下:

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

} 

我現在可以執行正常的數據在我的列的模板頭,結合這將針對所有在我的視圖模型的ColumnData屬性的數據綁定。

更新:我被要求爲我的網格顯示XAML。這是很基本的,但在這裏它是:

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

這裏是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

看到整個解決方案會很有趣。例如,什麼是ImporterDataGridColumn? – 2010-09-02 19:01:25

+0

您可以忽略ImporterDataGridColumn並只使用DataGridTextColumn。這只是一個標準專欄,我認爲我需要額外的功能,但沒有。 整個電網代碼在上面。視圖模型公開了上面列出的GridData和ColumnData屬性,數據從任何你想要的位置填充。 XAML然後鉤住了兩個: 2010-09-03 21:59:18

+0

您可以(漂亮請)添加查看這個xaml? – 2012-09-17 20:43:04

0

我絕對是一個WPF/MVVM /綁定小白,但最近一直對這個東西努力。我不知道你到目前爲止已經連接了什麼,但首先你需要爲你的View設置DataContext。既然你使用的是MVVM,我假設你有一個ViewModel,所以應該是View的DataContext。

即,如果您有您的視圖中創建/擁有您的視圖模型,它可能是這個樣子:

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

您可以輕鬆地進行數據綁定你的複選框,組合框,文本框,並在您的視圖模型的屬性。我發現最簡單的方法是讓您的ViewModel從基本視圖模型類繼承,如one that Josh Smith wrote。當你希望ViewModel向GUI通知任何值的變化時,這會給你一個內部調用的方法。

假設你有屬性,如ImportColumn,姓氏,和LastNameText(所有的C#屬性,而不能領域調用相應OnPropertyChanged),那麼你的XAML會是這個樣子:

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

我希望這有助於你出去。如果沒有,請發表評論,我會盡我所能去嘗試其他事情。

+0

我使用卡利和有我的視圖模型已經建立並正常工作。問題是我該如何使用數據綁定來同時綁定標題和網格數據。這是我掙扎的地方。 – 2010-02-03 14:12:43

0

我們在我們的應用程序中做類似的事情。

我所做的是派生自己的列類型(DataGridSearchableBooleanColumn),然後我替換DataGridColumnHeader模板,我把兩個內容演示者放在那裏。第一個綁定到內容(與默認模板相同),第二個綁定到列。我使用列的數據模板(我有幾個爲不同的搜索類型(文本,組合,布爾)),然後我添加額外的屬性到列,以便我可以綁定到他們,看看這段代碼是否有意義。

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

這很有趣,但我不確定它是否解決了我的問題。我也創建了一個新的自定義列,並重寫OnAutoGeneratingColumn,並用我的自定義版本替換現有的列。 你的系統似乎工作,但我認爲你需要遍歷列來獲取數據,而我試圖綁定到使用MVVM模式的外部數據源。 如果我能得到當前列的索引,我想我會有一個使用第二個數據源的解決方案。 – 2010-02-04 17:37:31

+0

這是網格下降的地方,它不能完全在mvvm模式中執行,因爲列集合是隻讀的(您可以添加刪除但未設置)我爲網格創建子類,給它一個額外的屬性,它是一個接口,然後將其綁定到我的視圖模型。然後讓它管理代碼中的列。另一個解決方案是爲網格創建子類,併爲其添加另一個列集合,您可以將它們綁定到這些列集合上並觀察這些變化以添加/刪除列。 (那麼你的視圖模型可以直接綁定) – 2010-02-04 22:31:13