none
WPF multi-column super header

    Question

  • I am trying to accomplish something like what you can see in this image:

    http://www.codeproject.com/KB/silverlight/MultipleHeaderSLGrid/FinalResult.PNG

    using the WPF datagrid from the codeplex WPFToolkit,

    Thanks for any help!!
    Friday, May 01, 2009 3:44 PM

Answers

  • Okay, as it turns out, it doesn't look like the WPF DataGrid supports SharedSizeGroups. I guess you could still do it by data binding the width of grid columns to DataGrid columns. Something like this:

     <Grid>
                        <Grid.RowDefinitions>
                            <RowDefinition Height="auto"/>
                            <RowDefinition Height="2*"/>
                        </Grid.RowDefinitions>
                        <Grid Grid.Row="0">
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition Width="{Binding ElementName=datagrid1, Path=RowHeaderWidth}" />
                                <ColumnDefinition Width="{Binding ElementName=Column1, Path=ActualWidth}" />
                                <ColumnDefinition Width="{Binding ElementName=Column2, Path=ActualWidth}" />
                                <ColumnDefinition Width="{Binding ElementName=Column3, Path=ActualWidth}" />
                                <ColumnDefinition Width="{Binding ElementName=Column4, Path=ActualWidth}" />
                            </Grid.ColumnDefinitions>
                            <Border Grid.Column="1" Grid.ColumnSpan="3" BorderBrush="Black" HorizontalAlignment="Stretch" BorderThickness="2">
                                <Label>Super Header!!</Label>
                            </Border>
                        </Grid>
                        <tools:DataGrid AutoGenerateColumns="False"
                                    Name="datagrid1" Grid.Row="1" RowHeaderWidth="10">
                            <tools:DataGrid.Columns>
                                <tools:DataGridTextColumn Header="column 1" Width="100" x:Name="Column1" />
                                <tools:DataGridTextColumn Header="column 2" Width="80" x:Name="Column2"/>
                                <tools:DataGridTextColumn Header="column 3" Width="80" x:Name="Column3"/>
                                <tools:DataGridTextColumn Header="column 4" Width="*" x:Name="Column4"/>
                            </tools:DataGrid.Columns>
                        </tools:DataGrid>
                    </Grid>
    In the example you'll see that the "super header" will sit above columns 1,2 and 3 and will resize as you resize those columns. You could easily break it up further by adding more rows to the grid so you can have, for example, one header covering three columns, then another header under it covering only 2 columns. You could also mess about with styles to make it look more like a natural part of the DataGrid.
    Also, for some wierd reason, it doesn't work if I don't explicitly set the RowHeaderWidth. Without it, all the columns in the grid will be off by the width of the RowHeader.
    • Marked as answer by aesl23 Friday, May 01, 2009 8:19 PM
    Friday, May 01, 2009 5:16 PM

All replies

  • I don't think you can have a column header in the DataGrid span multiple cells like that, however, I'm thinking that what you could probably do is put your DataGrid within a Grid and use SharedSizeGroups to have the Grid column widths match the DataGrid column widths and then use ColumnSpan in the Grid to add those multi-column headers.
    Friday, May 01, 2009 3:54 PM
  • Wjousts,

    Thanks for your reply, do you think you could give me an example? I am not familiar with SharedSizeGroups... (new to WPF)

    Thanks!
    Friday, May 01, 2009 4:03 PM
  • Okay, as it turns out, it doesn't look like the WPF DataGrid supports SharedSizeGroups. I guess you could still do it by data binding the width of grid columns to DataGrid columns. Something like this:

     <Grid>
                        <Grid.RowDefinitions>
                            <RowDefinition Height="auto"/>
                            <RowDefinition Height="2*"/>
                        </Grid.RowDefinitions>
                        <Grid Grid.Row="0">
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition Width="{Binding ElementName=datagrid1, Path=RowHeaderWidth}" />
                                <ColumnDefinition Width="{Binding ElementName=Column1, Path=ActualWidth}" />
                                <ColumnDefinition Width="{Binding ElementName=Column2, Path=ActualWidth}" />
                                <ColumnDefinition Width="{Binding ElementName=Column3, Path=ActualWidth}" />
                                <ColumnDefinition Width="{Binding ElementName=Column4, Path=ActualWidth}" />
                            </Grid.ColumnDefinitions>
                            <Border Grid.Column="1" Grid.ColumnSpan="3" BorderBrush="Black" HorizontalAlignment="Stretch" BorderThickness="2">
                                <Label>Super Header!!</Label>
                            </Border>
                        </Grid>
                        <tools:DataGrid AutoGenerateColumns="False"
                                    Name="datagrid1" Grid.Row="1" RowHeaderWidth="10">
                            <tools:DataGrid.Columns>
                                <tools:DataGridTextColumn Header="column 1" Width="100" x:Name="Column1" />
                                <tools:DataGridTextColumn Header="column 2" Width="80" x:Name="Column2"/>
                                <tools:DataGridTextColumn Header="column 3" Width="80" x:Name="Column3"/>
                                <tools:DataGridTextColumn Header="column 4" Width="*" x:Name="Column4"/>
                            </tools:DataGrid.Columns>
                        </tools:DataGrid>
                    </Grid>
    In the example you'll see that the "super header" will sit above columns 1,2 and 3 and will resize as you resize those columns. You could easily break it up further by adding more rows to the grid so you can have, for example, one header covering three columns, then another header under it covering only 2 columns. You could also mess about with styles to make it look more like a natural part of the DataGrid.
    Also, for some wierd reason, it doesn't work if I don't explicitly set the RowHeaderWidth. Without it, all the columns in the grid will be off by the width of the RowHeader.
    • Marked as answer by aesl23 Friday, May 01, 2009 8:19 PM
    Friday, May 01, 2009 5:16 PM
  • Thank you very much!! worked perfectly
    Friday, May 01, 2009 8:38 PM
  • Any ideas on how to achieve this effect when the grid has frozen columns?
    Wednesday, June 23, 2010 8:28 AM
  • Thank you very much!! worked perfectly

    Yes it does....

    Unless the width of your DataGrid is larger than your WPF window.  

    At which point, when you drag your DataGrid's horizontal scrollbar to the right, all the DataGrid columns shift to the left, but this "fake" header row stays in the same place on the screen.

    Ah heck !!

    Looks like I'm going to have to turn off the horizontal scrollbar in the DataGrid control, and put a horizontal scrollbar on the <Grid> that the <DataGrid> is in instead.

    Come on Microsoft, there must be a nicer way to let us have a second header row in our WPF DataGrids !

     

    Tuesday, July 13, 2010 2:44 PM
  • This thread is really helpful
    Please remember to click “Mark as Answer” on the post that helps you, and to click “Unmark as Answer” if a marked post does not actually answer your question. This will help other members to find the solution easily.
    Thursday, September 02, 2010 3:43 AM
  • Starting with wjoust approch following solution works even if the horizontal scrollbar of the datagrid is visible. 

    Xaml: note the datagrid's LayoutUpdated event handler

     <Grid>
    <Grid.RowDefinitions> <RowDefinition Height="Auto"/> <RowDefinition Height="*"/>
    </Grid.RowDefinitions>
    <StackPanel Orientation="Horizontal">
    <Border x:Name="Label1" BorderBrush="Black" BorderThickness="1" HorizontalAlignment="Stretch">
    <TextBlock Text="Group1">
    </Border>
    <Border x:Name="Label2" BorderBrush="Black" BorderThickness="1" HorizontalAlignment="Stretch">
    <TextBlock Text="Group2">
    </Border>
    </StackPanel>


    < DataGrid Grid.Row="1" AutoGenerateColumns="False" Name="dg"

    CanUserReoderColumns="False" IsSynchronizedWithCurrentItem="True"
    LayoutUpdated="LayoutUpdated">

    <DataGrid.Columns>

    <DataGridTextColumn Header="column 1" Width="100" x:Name="Column1" /> <DataGridTextColumn Header="column 2" Width="80" x:Name="Column2"/> <DataGridTextColumn Header="column 3" Width="80" x:Name="Column3"/> <DataGridTextColumn Header="column 4" Width="*" x:Name="Column4"/> </DataGrid.Columns> </DataGrid> </Grid>

    and in code behind:

    // the datagrid's scrollviewer
    private ScrollViewer sv;
    
    private void LayoutUpdated(object sender, EventArgs e)
    {
      double offset = 0;
      GetScrollViewer(dg);
      if (sv != null && sv.ComputedHorizontalScrollBarVisibility == Visibility.Visible)
      {
       offset = sv.ContentHorizontalOffset;
      }
    
      double w = Column1.ActualWidth + Column2.ActualWidth - offset;
      Label1.Width = w < 0 ? 0 : w;
    
      double w2 = Column3.ActualWidth + Column4.ActualWidth ;
      Label2.Width = w2; 
    }
    
    private void GetScrollViewer(DependencyObject obj)
    {
      if (sv != null)
      { 
       return;
      }
    
      var tmp = obj as ScrollViewer;
      if (tmp != null)
      {
       if (tmp.Name.Equals("DG_ScrollViewer"))
       { 
         sv = tmp;
       }
       else
       {
         // Recursive call for each visual child
         for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++)
         {
          GetScrollViewer(VisualTreeHelper.GetChild(obj, i));
         }
       }
      }
      else
      {
        // Recursive call for each visual child
       for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++)
       {
         GetScrollViewer(VisualTreeHelper.GetChild(obj, i));
       }
      }
    }
    
    • Proposed as answer by Veena Khanna Thursday, May 05, 2011 8:58 AM
    Tuesday, January 18, 2011 4:12 PM
  • Worked for me. Thanks
    Tuesday, July 09, 2013 8:57 PM
  • Hi, I find this thread very informative. Now I would like to achieve that 1 column header would cover both the super header (grid) and the normal data grid columns so it would look like something like below:


    Should Column 1 and Column 6 be on a different DataGrid and just style it to look like part of the Super Headers?

    Thursday, July 18, 2013 2:21 AM