wpfwpf-gridhorizontal-alignment

WPF - center TextBlock horizontally on Grid column edges


I have the following Grid:

<Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="2*" />
        <ColumnDefinition Width="1*" />
        <ColumnDefinition Width="3*" />
    </Grid.ColumnDefinitions>
</Grid>

I want to place two TextBlocks into this Grid so that the horizontal center of the two TextBlocks to be exactly on the column edges (vertical alignment does not matter):

enter image description here

The closest I got to it is the following:

<Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="2*" />
        <ColumnDefinition Width="auto" />
        <ColumnDefinition Width="1*" />
        <ColumnDefinition Width="auto" />
        <ColumnDefinition Width="3*" />
    </Grid.ColumnDefinitions>
    <TextBlock Grid.Column="1"
               Text="textOfTextBlock1"/>
    <TextBlock Grid.Column="3"
               Text="textOfTextBlock2"/>
</Grid>

which results in

enter image description here

but if we look closely, the position of the TextBlocks in the two images are not the same, because the actual column widths of the columns having the widths 2*, 1* and 3* are calculated based on the total space that is left after the TextBlocks take their widths.

I do not want to specify exact coordinates in the solution, the positions of the TextBlocks should change when the Grid gets resized, of course.

I was also thinking about the following:

<Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="2*" />
        <ColumnDefinition Width="0" />
        <ColumnDefinition Width="1*" />
        <ColumnDefinition Width="0" />
        <ColumnDefinition Width="3*" />
    </Grid.ColumnDefinitions>
    <TextBlock Grid.Column="1"
               Text="textOfTextBlock1"/>
    <TextBlock Grid.Column="3"
               Text="textOfTextBlock2"/>
</Grid>

but the columns are cutting/clipping the edges of the TextBlocks, so having 0 as the column width for the second and fourth columns the TextBlocks are not visible.


Solution

  • The TextBlock's Margin can be bound to the ActualWidth of the TextBlock, then setting the Left Margin equal to the ActualWidth / -2 in a converter results in no need to hardcode the Width and Margin of the TextBlocks:

    <Grid>
        <Grid.Resources>
            <converters:ActualWidthToNegativeHalfLeftMarginConverter x:Key="ActualWidthToNegativeHalfLeftMarginConverter" />
        </Grid.Resources>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="2*" />
            <ColumnDefinition Width="1*" />
            <ColumnDefinition Width="3*" />
        </Grid.ColumnDefinitions>
        <TextBlock Grid.Column="1"
                   Text="textOfTextBlock1"
                   HorizontalAlignment="Left">
            <TextBlock.Margin>
                <Binding Path="ActualWidth" RelativeSource="{RelativeSource Self}" Converter="{StaticResource ActualWidthToNegativeHalfLeftMarginConverter}" />
            </TextBlock.Margin>
        </TextBlock>
        <TextBlock Grid.Column="2"
                   Text="textOfTextBlock2"
                   HorizontalAlignment="Left">
            <TextBlock.Margin>
                <Binding Path="ActualWidth" RelativeSource="{RelativeSource Self}" Converter="{StaticResource ActualWidthToNegativeHalfLeftMarginConverter}" />
            </TextBlock.Margin>
        </TextBlock>
    </Grid>
    
    public class ActualWidthToNegativeHalfLeftMarginConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            return new Thickness((double)value / -2, 0, 0, 0);
        }
    
        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            // ...
        }
    }