0

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.

eriyg
  • 99
  • 1
  • 12
  • What does the FormTextBox style contain? – John V Mar 24 '22 at 05:40
  • @JohnV I edited in the style. I also just tried removing the style from the TextBoxes and got the same result, but with the default TextBox style this time. – eriyg Mar 24 '22 at 06:55
  • @JohnV I want the TextBlock to be where the 2nd one is and the TextBox to begin where the 2nd one begins and end where the 1st one ends. – eriyg Mar 24 '22 at 07:41
  • It's not clear what "2nd one" means. A mock up your desired layout would help. – John V Mar 24 '22 at 08:38
  • @JohnV Look at the 2 TextBlock+TextBox pairs just below and to the right of the "Create" header. The top one is the 1st one. The bottom one is the 2nd one. – eriyg Mar 24 '22 at 13:54
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/243290/discussion-between-eriyg-and-john-v). – eriyg Mar 24 '22 at 14:31
  • I have added your wireframe and a possible solution to my answer. – John V Mar 24 '22 at 20:38

1 Answers1

2

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.

John V
  • 1,286
  • 9
  • 15
  • Regarding the Grids: The control structure here is Grid > DockPanel > Grid. The first Grid uses star sizing and the inner Grids use shared sizing. This is done to get the last character of each TextBlock to line up vertically. What's wrong with nesting grids? – eriyg Mar 24 '22 at 06:47
  • Regarding the multiple DockPanel.Top: See https://wpf-tutorial.com/panels/dockpanel/ you can have multiple DockPanel.X in a given dock panel and it creates a sort of StackPanel effect. I did it here to avoid using a StackPanel and because its a bit more extensible. – eriyg Mar 24 '22 at 06:48
  • Regarding your example: This does not fit the parameters I set in the question. The TextBlock+TextBox is not left aligned. The TextBox does not fill the space. A hard-coded width was used as well (technically minimum width, but this is still unacceptable). – eriyg Mar 24 '22 at 06:49
  • There's nothing wrong with nesting panels, unless you can't get the layout to do what you want, in which case remove components until you figure out which nested control is causing the layout problem. I'm aware that you can put multiple controls in a DockPanel Dock, but starting with simpler controls is usually best. – John V Mar 24 '22 at 07:28
  • I'd already fixed the shared size scope after our previous conversations. I'm still using a DockPanel, it doesn't seem to make a difference visually. The main culprit seems to have been the 2nd column definition using shared size. Changing that to Width="*" got the layout I needed. – eriyg Mar 24 '22 at 20:36