Search code examples
c#wpfbuttondatatemplatewpf-grid

Why doesn't my WPF Grid inside a Button's DataTemplate stretch correctly?


I have a WPF ItemsControl that generates buttons. These buttons have two sides one for the name of the item, the other for the prices thereof. My issue is the fact that the Grid columns (see code bellow) don't seem to expand correctly to the entire width and height of the button they are in, they just stay in the center even though the button intself expands to the full width and height of the it's UniformGrid cell. Take this item with name 'Item 1' and with a price of '56.00 Kč' for example:

malfunctioning button example

The only thing that seems to work is to add a Margin or Padding between the TextBlocks, but then they are still not aligned correctly as I would like them to be. Anyone got any ideas?


                <ItemsControl.ItemTemplate>
                    <DataTemplate>
                        <Button Margin="{Binding DataContext.MarginBetweenButtons, RelativeSource={RelativeSource AncestorType=ItemsControl}}" IsEnabled="{Binding Enabled}"
                                Command="{Binding Path=DataContext.ButtonClickedCommand, RelativeSource={RelativeSource AncestorType=ItemsControl}}"
                                CommandParameter="{Binding}"
                                Background="{Binding Color}" 
                                HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch">
                            <Button.ContentTemplate>
                                <DataTemplate>
                                    <Grid VerticalAlignment="Stretch" HorizontalAlignment="Stretch">
                                        <Grid.ColumnDefinitions>
                                            <ColumnDefinition Width="3*"/>
                                            <ColumnDefinition Width="2*"/>
                                        </Grid.ColumnDefinitions>
                                        
                                        <TextBlock Grid.Column="0"
                                                   Text="{Binding DataContext.Name, RelativeSource={RelativeSource AncestorType=Button}}"
                                                   FontSize="{Binding DataContext.FontSize, RelativeSource={RelativeSource AncestorType=Button}}"
                                                   TextWrapping="Wrap" TextAlignment="Center" Margin="0" Padding="0" VerticalAlignment="Center" HorizontalAlignment="Center"
                                                   Foreground="{Binding DataContext.TextColor, RelativeSource={RelativeSource AncestorType=Button}}"/>

                                        <TextBlock Grid.Column="1" Visibility="{Binding DataContext.PriceText, Converter={StaticResource textToVisibility}, RelativeSource={RelativeSource AncestorType=Button}}"
                                                   Text="{Binding DataContext.PriceText, RelativeSource={RelativeSource AncestorType=Button}}"
                                                   FontSize="{Binding DataContext.FontSize, RelativeSource={RelativeSource AncestorType=Button}}"
                                                   TextWrapping="NoWrap" TextAlignment="Right" Margin="0,0,0,0" Padding="0,0,0,0" VerticalAlignment="Center" HorizontalAlignment="Right"
                                                   Foreground="{Binding DataContext.TextColor, RelativeSource={RelativeSource AncestorType=Button}}" />
                                    </Grid>
                                </DataTemplate>
                            </Button.ContentTemplate>
                        </Button>
                    </DataTemplate>
                </ItemsControl.ItemTemplate>

I've tried changing the Vertical and Horizontal alignments to stretch, creating text boxes with just dummy text and no additional values (then slowly adding values back), changing the Button's properties to leave it barebones, and configuring a UniformGrid to act like a Grid with nested UniformGrids to create a column span effect. All of this was done to no avail.


Solution

  • The problem is that Button by default centers the internal ContentPresenter (which displays your ContentTemplate). Thus while your root Grid has its alignments set correctly, its parent ContentPresenter will still be centered and not stretched.

    See the WPF default templates here. Specifically:

          <ContentPresenter Margin="2"
                            HorizontalAlignment="Center"
                            VerticalAlignment="Center"
                            RecognizesAccessKey="True" />
    

    The solution then is to re-template the button entirely. A good way to do this is to just copy the template from the default at the link above, and modify it to suit your needs. This way you won't lose all the various visual effects like hover, etc. But rather than make the template too bespoke, you might change the above to be more general:

          <ContentPresenter Margin="2"
                            HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
                            VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
                            RecognizesAccessKey="True" />
    

    Then set HorizontalContentAlignment and VerticalContentAlignment properties on each specific Button (or its style), and keep your ContentTemplate the same. Frankly the default Button template should have done this in the first place, as this is the exact purpose of those Control properties.