How can I keep dynamic sized controls aligned? - c#

I have a WPF application and I am looking for a way to align some Label and TextBox controls so that the TextBox controls are always inline, but also allow for the Label content to be dynamic (which should move the TextBoxes as required).
It is a little hard to explain so here are a couple of screenshots that should show my requirements...
Before:
After:
Notice how the first TextBox shifts to the right in order to make room for the longer text, while the second TextBox also shifts to keep inline with the first. (The behavior I want is similar to that of an HTML table with two rows of two cells each)
Keeping in mind I am fairly new to WPF (so I may be going down the wrong road completely), I have used a couple of StackPanels in order to cater for the dynamic sized labels. However, the problem of course is that both StackPanels do not know about each other.
Here is my current code:
<StackPanel Orientation="Horizontal">
<Label Content="Label 1" HorizontalAlignment="Left" VerticalAlignment="Top"/>
<TextBox HorizontalAlignment="Left" Height="23" TextWrapping="Wrap" VerticalAlignment="Top" Width="200"/>
</StackPanel>
<StackPanel Orientation="Horizontal">
<Label Content="Label 2" HorizontalAlignment="Left" VerticalAlignment="Top"/>
<TextBox HorizontalAlignment="Left" Height="23" TextWrapping="Wrap" VerticalAlignment="Top" Width="200"/>
</StackPanel>
Is there anyway to get what I want by using StackPanels? If not, what other controls or methods can I use to meet my requirements?

If you want the columns to be sized the same for all controls, simply use a Grid.
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="23"/>
<RowDefinition Height="23"/>
</Grid.RowDefinitions>
<Label Grid.Row="0" Grid.Column="0" Content="Longer Label 1"/>
<TextBox Grid.Row="0" Grid.Column="1" TextWrapping="Wrap"/>
<Label Grid.Row="1" Grid.Column="0" Content="Label 2" />
<TextBox Grid.Row="1" Grid.Column="1" TextWrapping="Wrap"/>
</Grid>
With this setup, the first column will size to fit the widest label, and the second will take up the rest.

Just use a Grid:
<Grid Grid.Row="2" VerticalAlignment="Top">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
...
</Grid.RowDefinitions>
<TextBox Grid.Row="0" Grid.Column="0" Text="Name" Style="{StaticResource
LabelStyle}" />
<TextBox Grid.Row="0" Grid.Column="1" Text="{Binding Name,
UpdateSourceTrigger=PropertyChanged}" Style="{StaticResource TextBoxStyle}" />
<TextBox Grid.Row="1" Grid.Column="0" Text="Age" Style="{StaticResource
LabelStyle}" />
<TextBox Grid.Row="1" Grid.Column="1" Text="{Binding Age,
UpdateSourceTrigger=PropertyChanged}" Style="{StaticResource TextBoxStyle}" />
...
</Grid>

Related

C# WPF - Creating Custom Control. Not sure how to align Text Correctly

I'm trying to create a custom control; similar, say to a Compass, so it is circular. I have a compass face image, and an arrow image that I can rotate to point the right direction. The problem is that I have N,S,E,W markers that I want to update at times and thus made them labels. I divided the field up into percentages and put the labels in the right grid locations.
This all looks great.
BUT when I use this custom control on various pages, some work and some don't. What it comes down to is the size of the cell of the grid on the new page. In one case the grid width is 4x the grid height. The background image grows to fill the height; this is right. The West and East TextBlocks are way off of the Compass_Face image because the 1/4th the width of the cell is way to the left. I thought that they would be constrained to the size of the custom control but that appears to not be the problem.
I'm sure that there has to be a way to keep the text constrained to the compass_face image. Thoughts?
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="25*"/>
<ColumnDefinition Width="50*"/>
<ColumnDefinition Width="25*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="25*"/>
<RowDefinition Height="50*"/>
<RowDefinition Height="25*"/>
</Grid.RowDefinitions>
<Image x:Name="imageBackground" Grid.Column="0" Grid.Row="0" Grid.ColumnSpan="3" Grid.RowSpan="3" Source="Resources/compass_face.png"/>
<Image x:Name="imageArrow" Grid.Column="1" Grid.Row="1" Source="Resources/arrow.png"/>
<TextBlock x:Name="labelNorth" Grid.Column="1" Grid.Row="0" HorizontalAlignment="Center" VerticalAlignment="Center" Foreground="White" FontWeight="Bold" Text="N"/>
<TextBlock x:Name="labelSouth" Grid.Column="1" Grid.Row="2" HorizontalAlignment="Center" VerticalAlignment="Center" Foreground="White" FontWeight="Bold" Text="S"/>
<TextBlock x:Name="labelEast" Grid.Column="2" Grid.Row="1" HorizontalAlignment="Center" VerticalAlignment="Center" Foreground="White" FontWeight="Bold" Text="E"/>
<TextBlock x:Name="labelWest" Grid.Column="0" Grid.Row="1" HorizontalAlignment="Center" VerticalAlignment="Center" Foreground="White" FontWeight="Bold" Text="W"/>
</Grid>
Grid isn't really the best layout to use in this case, but you can force it to be square by putting a Viewbox around it and assigning your parent grid to a specific size. That will keep your text aligned properly:
<Viewbox>
<Grid Width="200" Height="200">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="25*"/>
<ColumnDefinition Width="50*"/>
<ColumnDefinition Width="25*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="25*"/>
<RowDefinition Height="50*"/>
<RowDefinition Height="25*"/>
</Grid.RowDefinitions>
<!--<Image x:Name="imageBackground" Grid.Column="0" Grid.Row="0" Grid.ColumnSpan="3" Grid.RowSpan="3" Source="Resources/compass_face.png"/>
<Image x:Name="imageArrow" Grid.Column="1" Grid.Row="1" Source="Resources/arrow.png"/>-->
<Ellipse Fill="CornflowerBlue" Grid.Column="0" Grid.Row="0" Grid.ColumnSpan="3" Grid.RowSpan="3" />
<TextBlock Grid.Column="1" Grid.Row="1" Text="↑" FontSize="200" Foreground="White" HorizontalAlignment="Center" VerticalAlignment="Center" />
<TextBlock x:Name="labelNorth" Grid.Column="1" Grid.Row="0" HorizontalAlignment="Center" VerticalAlignment="Center" Foreground="White" FontWeight="Bold" Text="N"/>
<TextBlock x:Name="labelSouth" Grid.Column="1" Grid.Row="2" HorizontalAlignment="Center" VerticalAlignment="Center" Foreground="White" FontWeight="Bold" Text="S"/>
<TextBlock x:Name="labelEast" Grid.Column="2" Grid.Row="1" HorizontalAlignment="Center" VerticalAlignment="Center" Foreground="White" FontWeight="Bold" Text="E"/>
<TextBlock x:Name="labelWest" Grid.Column="0" Grid.Row="1" HorizontalAlignment="Center" VerticalAlignment="Center" Foreground="White" FontWeight="Bold" Text="W"/>
</Grid>
</Viewbox>

TextBox content vertical stretch to available size

My goal is a TextBox that accepts return but only shows 4 lines of text that is alligned to other lines of text, but I'm having some problems that basically seem to boil down to the question: What is the correct way to get the content of a TextBox to vertically stretch to the available space?
A minimal example looks like this:
<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="150" d:DesignWidth="150">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="auto" />
<RowDefinition Height="auto" />
<RowDefinition Height="auto" />
<RowDefinition Height="auto" />
</Grid.RowDefinitions>
<Label Grid.Column="0" Grid.Row="0" Content="1" />
<Label Grid.Column="0" Grid.Row="1" Content="2" />
<Label Grid.Column="0" Grid.Row="2" Content="3" />
<Label Grid.Column="0" Grid.Row="3" Content="4" />
<TextBox Grid.Column="1" Grid.Row="0" Grid.RowSpan="4" VerticalContentAlignment="Stretch" VerticalAlignment="Stretch" AcceptsReturn="True" MaxLines="4" Text="1
2
3
4" />
</Grid>
</Window>
But the output is not what I expected:
I tried setting the LineHeigt, but it only "cuts off" the text:
<TextBox Grid.Column="1" Grid.Row="0" Grid.RowSpan="4" VerticalContentAlignment="Stretch" VerticalAlignment="Stretch" AcceptsReturn="True" MaxLines="4" Text="1
2
3
4" TextBlock.LineStackingStrategy="MaxHeight" TextBlock.LineHeight="18" VerticalScrollBarVisibility="Visible" />
edit
Setting a fixed height for the TextBox corrects the behaviour, but that's not really a good way to solve the problem.
The immediate reason for your current issue is because you're using Label instead of TextBlock
The issue this causes is that TextBox renders lines as TextBlock which is a framework element. Whereas Label is a templated control inheriting from ContentControl and has a Padding set by default within it.
So if you you want them to align in your scenario you've got some options such as;
Curb the padding on your Label's;
<Label Padding="0"/>
Or swap them for good old TextBlock (which by the way is a "lighter" control and suggested instead unless using Label is actually necessary).
Or you could adjust your TextBox to reflect the padding of your Label's by targeting TextBlock with attached properties like TextBlock.LineHeight and TextBlock.LineStackingStrategy="BlockLineHeight" which would take some tinkering to get the exact output you want.
Also keep in mind TextBox is also a templated control with an embedded ScrollViewer so there will be a 1px offset for the default BorderThickness
So keeping your original control pairs if we do something like this instead, you'll see the culprit as example;
<Grid ShowGridLines="True"
HorizontalAlignment="Center" VerticalAlignment="Center">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="auto" />
<RowDefinition Height="auto" />
<RowDefinition Height="auto" />
<RowDefinition Height="auto" />
</Grid.RowDefinitions>
<Label Content="1" Padding="0"/>
<Label Grid.Row="1" Content="2" Padding="0"/>
<Label Grid.Row="2" Content="3" Padding="0"/>
<Label Grid.Row="3" Content="4" Padding="0"/>
<TextBox BorderThickness="0"
Grid.Column="1" Grid.RowSpan="4"
VerticalAlignment="Top"
VerticalContentAlignment="Stretch"
AcceptsReturn="True" MaxLines="4"
Text="1
2
3
4" />
</Grid>
Giving the result of;
Hope this helps, cheers.
A textbox could be stretched if it is inside a viebox, like this:
<Viewbox Stretch="Uniform" Grid.Column="1" Grid.Row="0" Grid.RowSpan="4" >
<TextBox Name="textBox" VerticalAlignment="Stretch" VerticalContentAlignment="Stretch" Text="1
2
3
4"/>
</Viewbox>
But in similar condition, I'd like to use a DataGrid
Just wrap the Textbox into Grid and set his MaxHeight to Infinity and his Height Value from that Grid:
<Grid x:Name="ValueTextBoxGrid" Grid.Row="1">
<TextBox
MaxHeight="Infinity"
Text="Your Text"
TextWrapping="Wrap"
Height="{Binding ElementName=ValueTextBoxGrid,
Path=ActualHeight}" />
</Grid>

UserControl examples

I am new to the WPF.
I wanted to create a UserControl which contains two fields for example
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="189*"/>
<ColumnDefinition Width="328*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="53*"/>
<RowDefinition Height="64*"/>
<RowDefinition Height="62*"/>
<RowDefinition Height="141*"/>
</Grid.RowDefinitions>
<TextBlock HorizontalAlignment="Right" TextWrapping="Wrap" Text="Name:" VerticalAlignment="Center"/>
<TextBox HorizontalAlignment="Left" Height="23" TextWrapping="Wrap" VerticalAlignment="Center" Width="120" Grid.Column="1" Margin="3,0,0,0"/>
<TextBlock HorizontalAlignment="Right" TextWrapping="Wrap" Text="Age:" VerticalAlignment="Center" Grid.Row="1"/>
<TextBox HorizontalAlignment="Left" Height="23" TextWrapping="Wrap" VerticalAlignment="Center" Width="120" Grid.Row="1" Grid.Column="1" Margin="3,0,0,0"/>
<Button Content="Add Person" HorizontalAlignment="Left" VerticalAlignment="Center" Width="75" Grid.Row="2" Grid.Column="1"/>
</Grid>
I wanted to create my user control in such a way that other developers(Who uses my usercontrol) can be able to add extra fields to it.
Imagine a developer want to add "Address filed to the above control".
If any one have sample articles on this please provide me.
I think you are after ContentControl. See below article for an example.
The ContentControl can contain any type of common language runtime
object (such as a string or a DateTime object) or a UIElement object
(such as a Rectangle or a Panel). This enables you to add rich content
to controls such as Button and CheckBox.
How to Embed Arbitrary Content in a WPF Control

Vertically align tops of blocks of text in WPF

I'm trying to visually align the top of the content of 2 (or more) blocks of Text.
The content and the Font (Size, Family, Weight) of each block can be modified by user.
I tried to play with the GetCellAscent, GetCellDescent and GetLineSpacing functions but the results depends only on the font and not the real content. And anyway I can't find how is distributed the difference between LineSpacing and Ascent+ Descent at the top and bottom of the block.
For example I want to produce this kind of output:
Any help?
Try This.....
<Grid Width="171" Height="100" Background="Black" Margin="257,78,75,133">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<TextBlock Name="aaa" Text="12" FontSize="40" HorizontalAlignment="Center" VerticalAlignment="Stretch" Grid.Row="1" FontWeight="Bold" Foreground="White" />
<TextBlock Text="$" HorizontalAlignment="Left" FontSize="20" Grid.Column="1" FontFamily="Euphemia" FontWeight="Bold" Foreground="White" Margin="8,0,72,0" />
<TextBlock Text="00 le Kg" FontSize="15" Grid.Column="1" Grid.Row="3" HorizontalAlignment="Left" Grid.IsSharedSizeScope="True" FontFamily="Euphemia" FontWeight="Bold" Foreground="White"/>
</Grid>

Column marging on a WPF Grid

I created a grid. On this grid, i have two colums with two TextBlock
I would like to insert a space between my columns, in order to having space between my textBlocks.
How doing this ?
Here is my code :
<ListBox x:Name="ListBoxTiers" HorizontalContentAlignment="Stretch" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="0">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Stretch" VerticalAlignment="Top">
<Grid Margin="10" VerticalAlignment="Top" HorizontalAlignment="Stretch">
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<TextBlock Grid.Row="0" Grid.Column="0" x:Name="TxtBox_CodeTiers" TextWrapping="Wrap" Text="{Binding m_strCode}" HorizontalAlignment="Stretch" VerticalAlignment="Top" />
<TextBlock Grid.Row="0" Grid.Column="1" x:Name="TxtBox_NomTiers" TextWrapping="Wrap" Text="{Binding m_strNom}" HorizontalAlignment="Stretch" VerticalAlignment="Top" />
</Grid>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Thanks a lot :)
Instead of playing with the columnns, set a margin around the textbox.
<TextBox Margin="10">
You can set each side independently or set left/right and up/down:
<TextBox Margin="10, 3, 7, 0">
<TextBox Margin="10, 5">
Or wrap your TextBoxes inside another panel and set the margin there:
<Grid Margin="10">
<TextBox />
<TextBox />
</Grid>

Categories

Resources