TextBlock with some text aligned left, and the remaining text aligned right

I want to have a text block that contains text, as shown below:

My associated textbox is : 

The text is left-aligned, and the colon is right-aligned.

I know how to get the above output using two text blocks. But I want to know that this is the same behavior that applies to a single text block?

+6
source share
3 answers

TextBlock is inherently opposed to the concept of aligned children, but of course there are some possible hacks work-arounds:

  • Fill in the blanks to create alignment.
  • Use InlineUIContainer to add user interface elements (which you can align) inside a TextBlock.

I will give an example of each of them by creating an ExtendedTextBlock control with LeftAlignedText and RightAlignedText . Usage looks like this:

 <my:ExtendedTextBlock RightAlignedText=":" LeftAlignedText="My associated textbox is" /> 

1) Fill in the blanks.

For this approach, I borrowed this answer to get the actual width of the text string. The idea is basically to subtract the full width of the text from the actual width of the control and insert the appropriate number of spaces between them.

 public class ExtendedTextBlock : TextBlock { public string RightAlignedText { get { return (string)GetValue(RightAlignedTextProperty); } set { SetValue(RightAlignedTextProperty, value); } } public static readonly DependencyProperty RightAlignedTextProperty = DependencyProperty.Register("RightAlignedText", typeof(string), typeof(ExtendedTextBlock), new PropertyMetadata(SetText)); public string LeftAlignedText { get { return (string)GetValue(LeftAlignedTextProperty); } set { SetValue(LeftAlignedTextProperty, value); } } public static readonly DependencyProperty LeftAlignedTextProperty = DependencyProperty.Register("LeftAlignedText", typeof(string), typeof(ExtendedTextBlock), new PropertyMetadata(SetText)); public static void SetText(object sender, DependencyPropertyChangedEventArgs args) { SetText((ExtendedTextBlock)sender); } public static void SetText(ExtendedTextBlock owner) { if (owner.ActualWidth == 0) return; // helper function to get the width of a text string Func<string, double> getTextWidth = text => { var formattedText = new FormattedText(text, CultureInfo.CurrentUICulture, FlowDirection.LeftToRight, new Typeface(owner.FontFamily, owner.FontStyle, owner.FontWeight, owner.FontStretch), owner.FontSize, Brushes.Black); return formattedText.Width; }; // calculate the space needed to fill in double spaceNeeded = owner.ActualWidth - getTextWidth(owner.LeftAlignedText ?? "") - getTextWidth(owner.RightAlignedText ?? ""); // get the width of an empty space (have to cheat a bit since the width of an empty space returns zero) double spaceWidth = getTextWidth(" .") - getTextWidth("."); int spaces = (int)Math.Round(spaceNeeded / spaceWidth); owner.Text = owner.LeftAlignedText + new string(Enumerable.Repeat(' ', spaces).ToArray()) + owner.RightAlignedText; } public ExtendedTextBlock() { SizeChanged += (sender, args) => SetText(this); } } 

2) Using InlineUIContainer to add InlineUIContainer text

The idea here is to add a panel inside the TextBlock , which will be responsible for the alignment of each text line. This is the main idea:

 <TextBlock> <InlineUIContainer> <Grid Width="{Binding RelativeSource={RelativeSource AncestorType=TextBlock},Path=ActualWidth}"> <TextBlock Text="Hello" /> <TextBlock Text="World" TextAlignment="Right" /> </Grid> </InlineUIContainer> </TextBlock> 

So, this version of the control recreates higher, but hides the implementation. It adds an InlineUIContainer control to the TextBlock base and maintains a link to the "left" and "right" TextBlock s, setting their text as necessary.

 public class ExtendedTextBlock2 : TextBlock { private TextBlock _left, _right; public string RightAlignedText { get { return (string)GetValue(RightAlignedTextProperty); } set { SetValue(RightAlignedTextProperty, value); } } public static readonly DependencyProperty RightAlignedTextProperty = DependencyProperty.Register("RightAlignedText", typeof(string), typeof(ExtendedTextBlock2), new PropertyMetadata(SetText)); public string LeftAlignedText { get { return (string)GetValue(LeftAlignedTextProperty); } set { SetValue(LeftAlignedTextProperty, value); } } public static readonly DependencyProperty LeftAlignedTextProperty = DependencyProperty.Register("LeftAlignedText", typeof(string), typeof(ExtendedTextBlock2), new PropertyMetadata(SetText)); public static void SetText(object sender, DependencyPropertyChangedEventArgs args) { ((ExtendedTextBlock2)sender).SetText(); } public void SetText() { if (_left == null || _right == null) return; _left.Text = LeftAlignedText; _right.Text = RightAlignedText; } public ExtendedTextBlock2() { Loaded += ExtendedTextBlock2_Loaded; } void ExtendedTextBlock2_Loaded(object sender, RoutedEventArgs e) { Inlines.Clear(); var child = new InlineUIContainer(); var container = new Grid(); var widthBinding = new Binding { Source = this, Path = new PropertyPath(TextBlock.ActualWidthProperty) }; container.SetBinding(Grid.WidthProperty, widthBinding); child.Child = container; container.Children.Add(_left = new TextBlock { HorizontalAlignment = System.Windows.HorizontalAlignment.Left }); container.Children.Add(_right = new TextBlock { HorizontalAlignment = System.Windows.HorizontalAlignment.Right }); Inlines.Add(child); SetText(); } } 
+7
source

This is not true. (I've been looking for quite a while.)

A TextBlock defines a single block of text and applies alignment properties to it. Since TextBlock and other WPF elements are naturally automated, the approach to using two of them, each with different settings to justify them, is the right approach.

They can contain <Span> and <Run> elements, and @JMK points to a code tutorial

To do what you need, consider the FlowDocument element and its contents, allowing you to describe the rationale as hierarchical XAML markup. A FlowDocument can consume very little screen space.

Or consider embedding a converter in which you find the width of your TextBlock and the width of the line you are going to convert by adding spaces and this colon and adjust the spacing in your line accordingly using the FormattedText class.

+3
source

If you use (or want to use) a fixed-width font, you can use String.PadRight . For example, if the maximum text length for a TextBox is 30 characters, call:

 myTextBox.Text = myString.PadRight(29, ' ') + ":"; 

This will cause the colon to align to the right regardless of the length of the line aligned to the left. There is no way to align the TextBox both left and right. I am not a member of WPF, so my next suggestion will also include translating WPF equivalents from Windows Forms instructions. Despite everything, you could do one more thing if you need to use a font of variable width:

  • Create a class that comes from a TextBox.
  • Override the OnPaint function or equivalent WPF.
  • Create code to fill the background and borders as you want.
  • Use Graphics.DrawString (or equiprobably) left justified for the main line and then right justified for the colon. In both cases, the ClientRectangle your base class in the DrawString function .

Besides creating a derived class with the OnPaint custom function, you have to use some kind of trickery.

I wish you the best.

+1
source

All Articles