左对齐的TextBlock和右对齐的剩余文本

7

我想要一个包含以下文本的文本块:

My associated textbox is                :

文本左对齐,冒号右对齐。

我知道如何使用两个文本块来获得上面的输出。但是我想知道单个文本块是否适用于相同的行为?


2
这是一篇关于在WPF TextBlock中应用HTML标记的好文章。可能会有帮助。 - JMK
1
根据您的示例,看起来您想要在冒号之前放置一个文本框。这将需要您计算字符的位置,以确保它正确地布局,这也可以根据操作系统配置动态更改。如果您分离TextBlocks,则WPF会自动管理此过程。使用单个元素有什么好处? - nmclean
@nmclean 我想只使用一个文本块,因为我必须定义两个文本块,仅仅是因为每个文本框前面有一个冒号。 - Khushi
@Khushi,我认为使用两个文本块是实现它的最佳方式,但为了简化你的代码,也许你可以创建一个简单的解析器来从字符串生成元素,例如:“value is {Value}:" 会产生 <TextBlock Text="value is "/><TextBox Width="50" Text="{Binding Path=Value}"/><TextBlock Text=":"/> - nmclean
3个回答

8

TextBlock 本质上不支持对齐子元素的概念,但当然有一些可能的解决方法:

  1. 填充空格以呈现对齐的外观。
  2. 使用 InlineUIContainer 在 TextBlock 内添加实际 UI 元素(您可以对齐这些元素)。

我将通过创建一个带有 LeftAlignedTextRightAlignedText 属性的 ExtendedTextBlock 控件来为每个示例提供示例。使用方式如下:

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

1) 使用空格填充。

这种方法,我借鉴了这个答案,以获取文本字符串的实际宽度。基本思想是从控件的实际宽度中减去文本的总宽度,并在它们之间插入适当数量的空格。

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) 使用InlineUIContainer添加对齐文本

这里的想法是在TextBlock中添加一个面板,负责对齐每个文本字符串。基本思路如下:

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

因此,此版本的控件重新创建了上述内容,但隐藏了实现细节。它向基本的TextBlock添加了InlineUIContainer控件,并保留了对“左”和“右”TextBlock的引用,根据需要设置其文本。
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();
    }
}

1
哦...如果你愿意编写控件,这很好。但是,InlineUIContainer仅指定了两个TextBlock。在这种情况下,我个人更倾向于编写两个TextBlock。(如果可以的话,我建议将其拆分为两个答案。) - Rob Perkins
我已经尝试了你的两种方法。但是当 TextBlock 的宽度设置为自动时,你的两种方法似乎都不起作用。如果你有时间,请通过解决这些问题来更新你的代码。 - Vishal

3
这不是一个翻译内容。(我已经找了很长时间。)
一个定义了一块文本,并应用对齐属性。由于和其他WPF元素自然地自适应大小,所以使用两个的方法,每个均具有其对齐属性上不同的设置,是正确的方法。
它们可以包含和元素,@JMK指向了一个代码教程
为了达到你的需求,请考虑使用元素及其内容,这将允许您将对齐描述为分层XAML标记。 可以占用非常小的屏幕空间。
或者,考虑实现一个转换器,在其中发现您的宽度和您打算通过添加空格和冒号来转换的字符串的宽度,并相应调整您的字符串中的间距,使用类。

1
如果您正在使用(或愿意使用)固定宽度字体,则可以使用String.PadRight。例如,如果TextBox的最大文本长度为30个字符,请调用:
myTextBox.Text = myString.PadRight(29, ' ') + ":";

那样可以使冒号对齐右侧,无论左对齐字符串的长度如何。没有办法同时左右对齐文本框。我不是WPF专家,所以我的下一个建议也涉及将Windows Forms指令转换为WPF等效指令。不管怎样,如果您必须使用可变宽度字体,另一件事情是您可以做这个:

  • 创建一个从TextBox派生的类。
  • 覆盖OnPaint函数或WPF等效函数。
  • 创建代码以任何您想要的方式填充背景和边框。
  • 对于主字符串,使用Graphics.DrawString(或其等效项)向左对齐,然后对于冒号向右对齐。在两种情况下,都使用基类中的ClientRectangleDrawString函数中。

除了创建具有自定义OnPaint函数的派生类之外,您还需要使用某种诡计。

祝您好运。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接