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;
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(); } }