Using the WPF WrapPanel to Wrap and Right Align Text - WPF Bug? - wpf

I am attempting to use the WrapPanel and two TextBlocks to append an asterisk (*) to the left side of some text, allow the text to wrap, and force the text to be right aligned. I have successfully done so by creating a WrapPanel with the FlowDirection set to RightToLeft and adding my text, followed by the asterisk. However, if the text I use happens to have any non-alphanumeric characters at the end of the line it is inexplicably forced to the front of the line. I find this behavior to be very strange. I think it must be a bug in WPF and not intended behavior.
Example with Text = Normal Text (Other Text) :
Expected:
* Normal Text (Other
Text)
Actual:
* Normal Text (Other
(Text
Feel free to use the following sample code to recreate the issue for yourself. Simply put this in a window with Height and Width = 100, then type "Normal Text (Other Text)" in the TextBox. Or, set the Height and Width to anything you like and write enough text that it is forced to wrap the text, then add punctuation to the end.
Sample Code:
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<TextBox Name="input" />
<WrapPanel Grid.Row="2" FlowDirection="RightToLeft">
<TextBlock Text="{Binding ElementName=input, Path=Text}" TextWrapping="Wrap"/>
<TextBlock Text="*" Margin="0,0,3,0"/>
</WrapPanel>
</Grid>
So, my question(s).
Is this a bug, or is this intended?
If this is a bug, should I submit it to Microsoft in some way? How?
Since starting this post, I have decided to put the two TextBlocks in a two column grid instead. With the non-asterisk containing TextBlock configured to use a Right TextAlignment I meet all my requirements anyway. Still, I found this to be an interesting issue.

Try this instead:
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<TextBox Name="input" />
<WrapPanel Grid.Row="2" HorizontalAlignment="Right" >
<TextBlock Text="*" Margin="0,0,3,0"/>
<TextBlock Text="{Binding ElementName=input, Path=Text}" TextWrapping="Wrap"/>
</WrapPanel>
</Grid>
FlowDirection is for use to support languages that are read that from right to left. Since I don't know the rules for languages like that I'm not going to pretend to understand why you are seeing what you are, or if it is a bug. That said I know that changing the FlowDirection isn't the correct way to handle right aligning left to right languages and you should use HorizontalAlignment instead.
(For future reference you submit bugs to Microsoft through the Connect site)

In .Net 4 (WPF4) runs are bindable, so you can try something like so:
<TextBlock TextAlignment="Right" TextWrapping="Wrap">
<Run Text="*" /><Run Text="{Binding ElementName=Input, Path=Text}" />
</TextBlock>
The two Run elements are on the same line because any type of whitespace between the tag boundaries will cause a space to appear between the two runs. (As HTML does.)

Related

WPF UserControl - Margins Misbehaving

I'm fairly new to WPF custom controls and have started by playing with a basic "LabelEdit" - basically a Label control and a TextBox. I have binding for 4 properties - Text, Label, TextWidth and LabelWidth (perhaps not what you would name them in a production environment, but this is just so that I can educate myself!).
All seems to work well. I also have an event which fires when the size of the label changes and causes an "ActualLabelWidth" DependencyProperty to change, so that a series of LabelEdit controls can all have the same Label Width. Here's the XAML for the LabelEdit:
<Grid DataContext="{Binding RelativeSource={RelativeSource AncestorType=UserControl}}">
<Grid.RowDefinitions >
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="{Binding LabelWidth}" />
<ColumnDefinition Width="{Binding TextWidth}" />
</Grid.ColumnDefinitions>
<Label Grid.Column="0" Grid.Row="0" Content="{Binding Label, FallbackValue=LabelEdit}" SizeChanged="Label_SizeChanged" />
<TextBox Grid.Column="1" Grid.Row="0" Text="{Binding Text}" />
</Grid>
...and for the MainWindow using it:
<ajdata:LabelEdit Text="{Binding Title}" Label="Title:" LabelWidth="{Binding ElementName=lblForename, Path=ActualLabelWidth}" TextWidth="100" />
<ajdata:LabelEdit Text="{Binding Surname}" Label="Surname:" LabelWidth="{Binding ElementName=lblForename, Path=ActualLabelWidth}" TextWidth="300" />
<ajdata:LabelEdit Text="{Binding Forename}" Label="Forename(s):" LabelWidth="Auto" TextWidth="300" Name="lblForename" />
So the label with the largest piece of text sets the width for the others.
The problem occurs when I give the Label a margin ("0,0,5,0") to space it from the TextBox element. In this situation, the LabelEdit with the "Auto" width seems to work fine. The bound versions, however, appear not to honour the margins. This means the TextBox part of the element appears positioned left of where it should be.
Does anyone know what I need to do so that all the Labels end up the same width with margins taken in to account? I realise I could probably insert an extra piece of code in my event handler, but would rather make the XAML do its job, if possible. Many thanks.
I have now solved this (but am still perhaps unsure why). Instead of setting margins of "0,0,5,0" on the label, I have set margins of "5,0,0,0" on the TextBox. This way, everything remains nicely lined up, whatever the label width.

Setting width in a data template based on the size of a parent control

I am using a telerik TreeView in WPF, and I'm using a HierarchicalDataTemplate to show the nodes. Nodes represent matched items - which can be left only, right only, equal or inequal (a tree based comparison).
I am currently using a DataTemplateSelector to select from one of four templates, which all look similar to the following:
<HierarchicalDataTemplate x:Key="EqualTreeItemTemplate" ItemsSource="{Binding}">
<Grid Name="rowGrid" HorizontalAlignment="Stretch" Height="Auto" d:DataContext="{d:DesignInstance Carbon:ICarbonComparisonPair }">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="16" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Path Data="F1M574.042,314.611L533.8,344.398 522.251,328.798 515.235,333.988 526.786,349.593 526.782,349.596 531.978,356.603 579.235,321.622 574.042,314.611z" Stretch="Uniform" Fill="#FF000000" Width="16" Height="16" VerticalAlignment="Center" Margin="0,0,0,0" />
<TextBlock Grid.Column="1" Text="{Binding ObjectName}" Style="{StaticResource ObjectNameStyle}" Margin="4,0,0,0" />
<TextBlock Grid.Column="2" Text="{Binding ObjectName}" Style="{StaticResource ObjectNameStyle}" Margin="4,0,0,0" />
</Grid>
</HierarchicalDataTemplate>
The problem that I have is that the item content area is a different width based on the level of the tree that the item appears in. This means that the columns that I have don't line up - the text in the right hand column shifts to the right a bit for each level of the tree that you expand.
What I would like to do is specify the right hand grid column's width to be 50% of the size of the tree control as a whole, rather than 50% of the grid's natural area. I thought maybe I could do this with a binding with a RelativeSource, but I just can't seem to make it work. Is there a way to achieve this in XAML, or do I need to resort to code-behind?
If I'm understanding it correctly you want column index 2 to align across all items?
Check out the TreeListView control and see if that gives you what you need.
Silverlight demo here (just so you can see what it looks like - the WPF version is pretty much the same)
[Edit - More info]
The SharedGroupName property on ColumnDefinition is tempting but, thanks to the indent, it won't quite work - you'll end up with all of the content in column 1 or 2 being sized the same, but the pesky indent still throws it off. Check out ListView's View Property. I'm believe it's at least in the same spirit as what Telerik TreeListView is, if not darned similar in implementation.
Here's a decent writeup on how to use it. (Ironically I have that page bookmarked in a folder called "TreeGridList" so apparently at some point I had the idea to do that to accomplish something similar :) )

WPF/XAML Layout of two Labels containing different length text whilst maintaining equal size Font Scaling

I'm trying position two labels, one right next to the other (with a small amount of spacing).
e.g.
Label 1 Label 2
the content of each label will always be different, most often Label 2 will have more characters but I want the content to scale as the window is resized and I want the font sizes in Label 1 and Label 2 to remain consistent.
I don't care if the contents of Label 2 are cropped if too long.
I've tried using a grid with two columns and placed the Label inside a ViewBox:
<Viewbox Margin="0,0,0,0" VerticalAlignment="Top" HorizontalAlignment="Left">
<Label Margin="0,0,0,0" Content="{Binding Path=Suburb}"/>
</Viewbox>
Problem with this is, the positioning is never right and the font sizes don't remain consistent.
What is the best layout method to achieve this? Can I synchronise the font sizes through binding? I've tried binding Label 2's FontSize property to Label 1's FontSize but that doesn't work as it just returns 12 every time no matter how big/small the font really is (I'm presuming the actual FontSize isn't being calculated because the Label is inside a ViewBox).
Any suggestions?
Thanks
You can use the following approach:
<Viewbox VerticalAlignment="Top">
<DockPanel>
<TextBlock Text="Second" DockPanel.Dock="Right" />
<TextBlock Text="First" />
</DockPanel>
</Viewbox>
Note that the second label would never get cropped since the Viewbox would allow the content to render to any size and then scale it. You can specify MaxWidth on the second TextBlock which would limit its size in pixels.
Instead of a DockPanel you can use a Grid with different ColumnDefinitions to achieve the same effect.
The important thing is that they're all in the same Viewbox (which, consequently, doesn't synchronize the FontSize but performs a visual scaling of everything it contains).
The following markup seems to be ok:
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Viewbox>
<Label Content="Label 1" Width="{Binding ActualWidth, ElementName=label2}"/>
</Viewbox>
<Viewbox Grid.Column="1">
<Label Name="label2" Content="Label 2 Label 2 Label 2"/>
</Viewbox>
</Grid>
Im not 100% sure of what 'content scale as the window resizes' and 'font sizes remain consistent' actuall means, but I guess you want each label to trim depending on available space.
So why not use TextBlock as below
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding Path=Suburb}" TextWrapping="NoWrap" TextTrimming="CharacterEllipsis" Margin="0,0,5,0" />
<TextBlock Text="{Binding Path=Area}" TextWrapping="NoWrap" TextTrimming="CharacterEllipsis" Grid.Column="1"/>
</Grid>
I realize this question is old, but one of the more correct ways to do this is to use Runs.
Runs are sections of text within a TextBlock.
<TextBlock Margin="0,0,0,0" VerticalAlignment="Top" HorizontalAlignment="Left">
<Run Text="{Binding Suburb}" />
<Run Text="{Binding Area}" />
</TextBlock>
Keep in mind that everything within a TextBlock is whitespace-sensitive, so if you wanted to have two Runs back-to-back without any space in between, you would do something like this:
<TextBlock Margin="0,0,0,0" VerticalAlignment="Top" HorizontalAlignment="Left">
<Run Text="Foo" /><Run Text="bar" />
<!-- Prints 'Foobar' -->
</TextBlock>
It's similar to how HTML is rendered, if there is whitespace between the tags, a single space is rendered on the page between those elements. This rule only applies to items within a TextBlock, not the entire XAML page.

WPF: Aligning the base line of a Label and its TextBox

Let's say I have a simple TextBox next to a Label:
<StackPanel>
<StackPanel Orientation="Horizontal">
<Label Margin="3">MyLabel</Label>
<TextBox Margin="3" Width="100">MyText</TextBox>
</StackPanel>
...
</StackPanel>
This yields the following result:
As you can see, the base lines of MyLabel and MyText are not aligned, which looks ugly. Of course, I could start playing around with the margins until they match up, but since this is such a common requirement I'm sure that WPF provides a much easier and more elegant solution, which I just haven't found yet...
This behaviour is, I think, caused by the fact that the TextBox defaults to a vertical alignment of Stretch, which causes it to fill the available space and have the extra couple of pixels under the text. If you use this instead:
<StackPanel>
<StackPanel Orientation="Horizontal">
<Label >MyLabel</Label>
<TextBox VerticalAlignment="Center" Width="100">MyText</TextBox>
</StackPanel>
</StackPanel>
... you should see a cleaner result.
What do you think?
<StackPanel Orientation="Horizontal">
<Label Margin="3" VerticalContentAlignment="Center">MyLabel</Label>
<TextBox Margin="3" VerticalContentAlignment="Center" Width="100">MyText</TextBox>
</StackPanel>
I achieved that look in Kaxaml with:
<StackPanel Orientation="Horizontal">
<Label Margin="3" VerticalAlignment="Center">MyLabel</Label>
<TextBox Margin="3" Width="100" VerticalAlignment="Center">MyText</TextBox>
</StackPanel>
I know that this is an old answer, but for here's an example for those who seek another way, where you don't need to rely on a fixed textbox width:
Instead of StackPanel, use a DockPanel and .Dock.
This proves to be very handy when used inside a Grid.
<DockPanel Grid.Column="2" Grid.Row="2">
<Label Content="SomeTitle:" DockPanel.Dock="Left"></Label>
<TextBox x:Name="SomeValueTextBox" VerticalAlignment="Center" DockPanel.Dock="Right"></TextBox>
</DockPanel>
This question is not as trivial as it looks and the accepted answers lacks details. If you try custom heights with the controls, you will see issues.
First, this is the correct implementation as answered by User7116.
<StackPanel Orientation="Horizontal">
<Label Margin="3" VerticalAlignment="Center">MyLabel</Label>
<TextBox Margin="3" Width="100" VerticalAlignment="Center">MyText</TextBox>
</StackPanel>
The tricky part is that there two level of vertical alignments here so understand how the alignments works.
When we specify alignment for a control, we are telling it how to position itself in the parent container (See documentation). So when we specify VerticalAlignment="Center"> to the TextBox we are telling it that this TextBox should appear vertically centered in parent stackpanel.
Now the actual text inside that TextBox could also use vertical alignment within that TextBox! This is the 2nd level and actually quite tricky and is answered here.
If you experiment with setting the Label's height above to say 50 as well, you will see they will not align again. This is because Label is now taking larger area and its text inside that area is not vertical aligned so it doesn't look aligned again.
The code for above is:
<StackPanel Orientation="Horizontal" VerticalAlignment="Center">
<Label Margin="3" VerticalAlignment="Center" Height="50">MyLabel</Label>
<TextBox Margin="3" VerticalAlignment="Center" Width="50" Height="50">MyText</TextBox>
</StackPanel>
Luckily when control height is default (like label control), it's just tall enough to contain the text so the inside alignment doesn't matter. But it comes into play if someone is setting custom heights for these controls and its better to understand how this works.

WPF Grid Column MaxWidth not enforced

This problem stems from not being able to get my TextBlock to wrap. Basically as a last-ditch attempt I am setting MaxWidth on my container grid's columns. I was surprised to find that my child label and textbox still do whatever they want (bad children, BAD) and are not limited by my grid column's MaxWidth="200".
What I'm really trying to do is let my TextBlock fill available width and wrap if necessary. So far after trying many variations of HorizontalAlignment="Stretch" on every known parent in the universe, nothing works, except setting an explicit MaxWidth="400" or whatever number on the TextBlock. This is not good because I need the TextBlock to fill available width, not be limited by some fixed number. Thanks!
<ItemsControl>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition MaxWidth="200" SharedSizeGroup="A" />
<ColumnDefinition MaxWidth="200" SharedSizeGroup="B" />
</Grid.ColumnDefinitions>
<Label VerticalAlignment="Top" Margin="0 5 0 0" Grid.Column="0" Style="{StaticResource LabelStyle}" Width="Auto" Content="{Binding Value.Summary}" />
<TextBlock Grid.Column="1" Margin="5,8,5,8" FontWeight="Normal"
Background="AliceBlue"
Foreground="Black" Text="{Binding Value.Description}"
HorizontalAlignment="Stretch"
TextWrapping="Wrap" Height="Auto" />
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
I've tried to replicate your problem by pasting everything between your Grid elements in to Kaxaml but everything wraps as you would expect it to. (I inserted regular strings where you were doing bindings and removed the Label style).
It could be that the problem is higher up the tree.
I'd suggest pasting chunks in to Kaxaml or similar to test and see which parent breaks your UI.
I provided an answer to this question, only it was using an ListView instead of an ItemsControl but the issue is likely the same. There is probably a ScrollViewer surrounding your ItemPresenter and you need to edit a copy of the ItemsControl template.
WPF ListView TextBlock TextWrapping

Resources