Search code examples
c#wpfxamlalignment

WPF: How do I get a text box to fill all available horizontal space?


I'm creating a form in WPF. The form has TextBoxes with TextBlocks to their left. A screenshot with GridLines is below:

My UI

I want it to look like this (forgive the MS Paint style): Desired UI

The problem is: the text boxes are really small unless I set the HorizontalAlignment to Stretch, but if I do that, I can't align them to the left. How do I get the Grid containing TextBlock/TextBox to align to the left and make the TextBox fill all available space?

It's trivially easy if I hardcode widths, but that obviously won't work with resizing. I've tried playing with Stack and Dock Panels to hold the Grids with no luck.

I can't use HorizontalAlignment=Stretch, MaxWidth, and Left aligned at the same time? because there is no other control that is the width of the space I am trying to fill.

I also can't use How to get controls in WPF to fill available space? because nothing in my xaml has HorizontalContentAllignment. I tried wrapping stuff in ContentControls and using an ItemsControl to force it with no luck.

The xaml for the form is below. Note, this xaml is for the control that is under the 'Create' header and to the right of the 3 buttons.

<Grid ShowGridLines="True">
    <Grid.RowDefinitions>
        <RowDefinition Height="*"/>
        <RowDefinition Height="*"/>
    </Grid.RowDefinitions>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="*"/>
        <ColumnDefinition Width="*"/>
    </Grid.ColumnDefinitions>
    <!--Left hand side content goes here-->
    <DockPanel Grid.Row="0" Grid.Column="1" Grid.RowSpan="2"
                Margin="20,0,0,0">
        <Grid ShowGridLines="True" DockPanel.Dock="Top" HorizontalAlignment="Stretch" VerticalAlignment="Top">
            <Grid.ColumnDefinitions>
                <ColumnDefinition SharedSizeGroup="TextBlockColumn"/>
                <ColumnDefinition SharedSizeGroup="TextBoxColumn"/>
            </Grid.ColumnDefinitions>
            <TextBlock Grid.Column="0"
                       Text="Input Name:"
                       HorizontalAlignment="Right" VerticalAlignment="Center"/>
            <TextBox Grid.Column="1"
                     HorizontalAlignment="Stretch" VerticalAlignment="Center"
                     Style="{StaticResource FormTextBox}"
                     Margin="5,0,5,0"/>
        </Grid>
        <Grid ShowGridLines="True" DockPanel.Dock="Top" HorizontalAlignment="Left" VerticalAlignment="Top">
            <Grid.ColumnDefinitions>
                <ColumnDefinition SharedSizeGroup="TextBlockColumn"/>
                <ColumnDefinition SharedSizeGroup="TextBoxColumn"/>
            </Grid.ColumnDefinitions>
            <TextBlock Grid.Column="0"
                       Text="Input Name:"
                       HorizontalAlignment="Right" VerticalAlignment="Center"/>
            <TextBox Grid.Column="1"
                     HorizontalAlignment="Stretch" VerticalAlignment="Center"
                     Style="{StaticResource FormTextBox}"
                     Margin="5,0,5,0"/>
        </Grid>
    </DockPanel>
</Grid>

The FormTextBox style is:

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Style TargetType="TextBox"
       x:Key="FormTextBox">
    <Setter Property="FocusVisualStyle" Value="{x:Null}"/>
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="TextBox">
                <Border CornerRadius="10"
                        Background="{StaticResource InputBrush}">
                    <Grid>
                        <Rectangle StrokeThickness="1"/>
                        <TextBox Margin="1"
                                 Text="{Binding RelativeSource={RelativeSource TemplatedParent},
                                                Path=Text,
                                                Mode=TwoWay,
                                                UpdateSourceTrigger=PropertyChanged}"
                                 BorderThickness="0"
                                 Background="Transparent"
                                 VerticalContentAlignment="Center"
                                 Padding="5"
                                 Foreground="{StaticResource TextBrush}"/>
                    </Grid>
                </Border>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

It rounds the corners, changes the background color, and removes the dotted line that shows up if it was selected before you alt-tabbed out and back into the application.


Solution

  • You indicate that this is the desired layout:

    enter image description here

    There are a few problems with your current code. First, using a DockPanel to host the TextBlock/TextBox pairs, and second, no control has Grid.IsSharedSizeScope=true set on it. You also have defined a shared size for the text box column when in reality you just want it to take up all the available space.

    Here is some code that achieves the desired layout:

    <Grid ShowGridLines="True">
        <Grid.RowDefinitions>
            <RowDefinition Height="*"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*"/>
            <ColumnDefinition Width="*"/>
        </Grid.ColumnDefinitions>
        <!--Left hand side content goes here-->
        <StackPanel Orientation="Vertical" Grid.Column="1"
                    Grid.RowSpan="2"
                    Margin="20,0,0,0"
                    Grid.IsSharedSizeScope="True" >
            <StackPanel.Resources>
                <Style TargetType="TextBlock">
                    <Setter Property="Grid.Column" Value="0"/>
                    <Setter Property="Text" Value="Input Name:"/>
                    <Setter Property="VerticalAlignment" Value="Center"/>
                </Style>
            </StackPanel.Resources>
    
    
            <Border BorderBrush="Blue" BorderThickness="5">
                <Grid ShowGridLines="True" HorizontalAlignment="Stretch">
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition SharedSizeGroup="TextBlockColumn"/>
                        <ColumnDefinition Width="*"/>
                    </Grid.ColumnDefinitions>
                    <TextBlock/>
                    <TextBox Grid.Column="1"
                             VerticalAlignment="Center" 
                             Style="{StaticResource FormTextBox}"
                             Margin="5,0,5,0"/>
                </Grid>
            </Border>
            <Border BorderBrush="Red" BorderThickness="5">
                <Grid ShowGridLines="True" HorizontalAlignment="Stretch">
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition SharedSizeGroup="TextBlockColumn"/>
                        <ColumnDefinition Width="*"/>
                    </Grid.ColumnDefinitions>
                    <TextBlock/>
                    <TextBox Grid.Column="1"
                             VerticalAlignment="Center" 
                             Style="{StaticResource FormTextBox}"
                             Margin="5,0,5,0"/>
                </Grid>
            </Border>
        </StackPanel>
    </Grid>
    

    This code produces:

    enter image description here

    In reality though, if you're going for maximum usability, you'll want to create your own control for the TextBlock/TextBox, and then you could just put it in an ItemsControl of some kind for dynamic content.