Search code examples
wpfxamlgriditemscontrolsharedsizegroup

Sharing column widths between multiple grids


I have the following XAML:

<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto" />
        <RowDefinition />
    </Grid.RowDefinitions>
    <Grid Grid.Row="0">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*" />
            <ColumnDefinition Width="*" />
            <ColumnDefinition Width="Auto" />
        </Grid.ColumnDefinitions>
        <TextBlock Grid.Column="0" Text="Header 1" />
        <TextBlock Grid.Column="1" Text="Header 2" />
    </Grid>
    <ScrollViewer Grid.Row="1" VerticalScrollBarVisibility="Auto">
        <ItemsControl ItemsSource="{Binding SomeList}">
            <ItemsControl.ItemTemplate>
                <DataTemplate>
                    <Grid>
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="*" />
                            <ColumnDefinition Width="*" />
                            <ColumnDefinition Width="Auto" />
                        </Grid.ColumnDefinitions>
                        <TextBox Grid.Column="0" Text="{Binding Value1}" />
                        <TextBox Grid.Column="1" Text="{Binding Value2}" />
                        <Button Grid.Column="2" Content="Button" />
                    </Grid>
                </DataTemplate>
            </ItemsControl.ItemTemplate>
        </ItemsControl>    
    </ScrollViewer>
</Grid>

and I want to achieve something like that:

Header 1              |Header 2              |             <--   this is the top grid
------------------------------------------------------
Value 1.1             |Value 1.2             |[Button]         |
Value 2.1             |Value 2.2             |[Button]     <-- | this is the scroll viewer
Value 3.1             |Value 3.2             |[Button]         |

which looks similar to a data grid with the fixed header row. I can't use an actual DataGrid because in the real app "Header 1" and "Header 2" are actually not simple grid text headers but rather interactable elements with quite complex markup. The main question is how to achieve the width synchronization for all three columns? The last column should size itself to the widest button in that column while the first two columns should share the remaining space in 50/50 proportion. It feels like this can be achieved using the SharedSizeGroup property but I cannot figure out how exactly.


Solution

  • I think I figured out how to do it:

    <Grid Grid.IsSharedSizeScope="True">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition />
        </Grid.RowDefinitions>
        <Grid Grid.Row="0" Width="{Binding ActualWidth, ElementName=itemsControl}"
              HorizontalAlignment="Left">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="*" />
                <ColumnDefinition Width="*" />
                <ColumnDefinition Width="Auto" SharedSizeGroup="ButtonColumn" />
            </Grid.ColumnDefinitions>
            <TextBlock Grid.Column="0" Text="Header 1" />
            <TextBlock Grid.Column="1" Text="Header 2" />
        </Grid>
        <ScrollViewer Grid.Row="1" VerticalScrollBarVisibility="Auto">
            <ItemsControl Name="itemsControl" ItemsSource="{Binding SomeList}">
                <ItemsControl.ItemTemplate>
                    <DataTemplate>
                        <Grid>
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition Width="*" />
                                <ColumnDefinition Width="*" />
                                <ColumnDefinition Width="Auto" SharedSizeGroup="ButtonColumn" />
                            </Grid.ColumnDefinitions>
                            <TextBox Grid.Column="0" Text="{Binding Value1}" />
                            <TextBox Grid.Column="1" Text="{Binding Value2}" />
                            <Button Grid.Column="2" Content="Button" />
                        </Grid>
                    </DataTemplate>
                </ItemsControl.ItemTemplate>
            </ItemsControl>    
        </ScrollViewer>
    </Grid>
    
    1. Grid.IsSharedSizeScope="True" and SharedSizeGroup="ButtonColumn" synchronize the last columns.
    2. Width="*" splits the remaining space between first two columns in 50/50 proportion.
    3. Width="{Binding ActualWidth, ElementName=itemsControl}" synchronizes the width of the top header grid with the width of the ItemsControl below which can change when the scroll bar is shown or hidden.
    4. HorizontalAlignment="Left" pushes the header grid to the left to align it with the ItemsControl below.