Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to align TextBlock and TextBox text on one baseline

I am trying to figure out a nice layout for a group of controls with labels. If i have a label on the left of a textbox for example, the base line of the label and the textbox are not on the same vertical position. An often workaround ist to just vertically center both controls but that doesn't look nice if you have multi line textbox controls.

I got a few combinations.

I hoped a relative panel would provide the option to align the baseline like it does with the android relative layout panel. Unfortunately it doesn't.

<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
  <RelativePanel>
    <TextBlock Text="Name:" Name="t1"></TextBlock>
    <TextBox Text="Content" RelativePanel.RightOf="t1" ></TextBox>
  </RelativePanel>
</Grid>

A grid does not help as well.

<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
  <Grid>
    <Grid.ColumnDefinitions>
      <ColumnDefinition Width="Auto"/>
      <ColumnDefinition Width="Auto"/>
    </Grid.ColumnDefinitions>

    <TextBlock Grid.Column="0" Text="{x:Bind Label}" Style="{ThemeResource BaseTextBlockStyle}"/>
    <TextBox Grid.Column="1" Text="Content" HorizontalAlignment="Stretch"></TextBox>
  </Grid>
</Grid>

Obviously a horizontal stackpanel has no magic as well.

<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
  <StackPanel Orientation="Horizontal">
    <TextBlock Grid.Column="0" Text="{x:Bind Label}" Style="{ThemeResource BaseTextBlockStyle}"/>
    <TextBox Grid.Column="1" Text="Content" VerticalAlignment="Top"></TextBox>
  </StackPanel>
</Grid>

A vertical stackpanel would avoid the problem, but i'd prefer the label on the left of the control.

  <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
    <StackPanel Orientation="Vertical">
      <TextBlock Grid.Column="0" Text="{x:Bind Label}" Style="{ThemeResource BaseTextBlockStyle}"/>
      <TextBox Grid.Column="1" Text="Content" ></TextBox>
    </StackPanel>
  </Grid>

Do you have any ideas how to align baselines which is / or maybe a nice layout idea for a lot of input controls and labels?

like image 896
Diego Frehner Avatar asked Dec 02 '16 23:12

Diego Frehner


People also ask

How do you line up text boxes in Word?

Hold down Shift , click the objects that you want to align, and then click the Shape Format tab. Click Arrange > Align, and select an alignment option. If you displayed the gridlines, you will see that the objects are aligned on the grid.

What is the difference between TextBlock and label in WPF?

Labels usually support single line text output while the TextBlock is intended for multiline text display. For example in wpf TextBlock has a property TextWrapping which enables multiline input; Label does not have this.


2 Answers

I had a quick look through the docs and I don't think there is any equivalent to Android's baseline alignment in UWP. Text controls don't expose the position of their baseline for panels to make use of, as it is highly dependent on the template of the control (which can be completely changed, unlike in Android where the visual appearance of views are sort of hard-coded).

Anyway, by inspecting the visual tree of a textbox, I found that the innermost TextBoxView (the control which actually displays and handles text input for the TextBox control) renders text in the same way that a TextBlock does (meaning the padding and such is the same). So as long as we can align the top edges of the TextBlock to the TextBoxView, then the text for both controls will be at the same vertical position. Of course, the font and font size for both controls needs to be the same (which it is by default).

The TextBoxView is pushed down by its parent ScrollContentPresenter by 3px (effective pixels), and the text box border by 2px. So you just need to add 5px of top margin to the TextBlock and the text for both controls will align.

Screenshot

FYI this is based on the default TextBox template provided by SDK version 14393 which I am using. Microsoft can change these styles with new SDK versions.


Looking at the Android docs, the View class has a method getBaseline; in UWP, UIElement (or FrameworkElement) has no such equivalent. Searching the visual tree and trying to determine the baseline of the first text element you see mightn't work in every case (some elements have more than one text element).

If you're going to have lots of these left-labelled-elements, some suggestions:

  • Create a UserControl with the label and textbox in the correct position. That way you only have the margin hardcoded in one place in your code and if you need to change it you only need to change one piece of code.
  • The TextBox control (like many others) has a Header property where you can specify text which will be displayed immediately above the textbox. You can change the template so that the header will be to the left of the textbox instead.

It seems that the recommended way is to use the Header property and have the label on top. Most UWP apps do this (in Groove when you edit song info, Edge's settings pane, etc). This is probably because when displayed on a mobile device, there isn't much horizontal space.

like image 74
Decade Moon Avatar answered Sep 30 '22 16:09

Decade Moon


The easiest way to achieve what you want is to not create a TextBlock and a TextBox, but simply two TextBox elements with any of the layouts you have suggested at the top. Then apply the following Style to the TextBox element which should only show the label to make it look like a TextBlock:

<Style x:Key="TransparentTextBoxStyle" TargetType="TextBox">
    <Setter Property="Background" Value="Transparent"/>
    <Setter Property="BorderBrush" Value="Transparent"/>
    <Setter Property="IsReadOnly" Value="True" />
    <Setter Property="IsTapEnabled" Value="False" />
    <Setter Property="IsTabStop" Value="False" />
</Style>

I think setting IsReadOnly, IsTapEnabled and IsTabStop should be enough to disable all potential interactions (just made a quick test), but to be sure you could also simply set IsEnabled to false and modify the template to look the same like if it would be enabled (you can get the default template in VS2015 by opening the Document Outline window (if you don't have it visible, you can find it in View > Other windows) open the visual tree to get to your item, then right-click, Edit Template and Edit a Copy...).

like image 40
DevAttendant Avatar answered Sep 30 '22 16:09

DevAttendant