TextBlock 的可见行数 [英] Visible line count of a TextBlock

查看:27
本文介绍了TextBlock 的可见行数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

如果将 TextWrapping 设置为Wrap",则 WPF TextBlock 可以包含多行文本.有没有一种干净"的方式来获取文本的行数?我考虑过查看所需的高度并将其除以每条线的估计高度.然而,这似乎很脏.有没有更好的办法?

If you set TextWrapping to "Wrap", a WPF TextBlock can have several lines of text. Is there a "clean" way to get the number of lines of text? I considered looking at the desired height and dividing it by an estimated height of each line. However, that seems quite dirty. Is there a better way?

推荐答案

WPF 的一个非常好的地方是所有控件都非常简洁.因此,我们可以使用 TextBox,它有一个 LineCount属性(为什么它不是 DependencyProperty 或者为什么 TextBlock 没有它我不知道).使用 TextBox,我们可以简单地重新模板化它,使其行为和看起来更像一个 TextBlock.在我们的自定义样式/模板中,我们将把 IsEnabled 设置为 False,并且只创建控件的基本重新模板化,以便不再存在禁用的外观.我们还可以通过使用 TemplateBindings 绑定我们想要维护的任何属性,例如 Background.

One thing about WPF that's very nice is that all of the controls are very lookless. Because of this, we can make use of TextBox, which has a LineCount property (Why it's not a DependencyProperty or why TextBlock doesn't also have it I do not know). With the TextBox, we can simply re-template it so it behaves and looks more like a TextBlock. In our custom Style/Template we're going to set IsEnabled to False, and just create a basic re-templating of the control so that the disabled look is no longer present. We can also bind any properties we want to maintain, like Background, through the use of TemplateBindings.

<Style x:Key="Local_TextBox"
    TargetType="{x:Type TextBoxBase}">
    <Setter Property="IsEnabled"
            Value="False" />
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type TextBoxBase}">
                <Border Name="Border"
                    Background="{TemplateBinding Background}">
                    <ScrollViewer x:Name="PART_ContentHost" />
                </Border>
            </ControlTemplate>
        </Setter.Value>
</Setter>
</Style>

现在,这将使我们的 TextBox 看起来和行为像一个 TextBlock,但我们如何获得行数?

Now, that will take care of making our TextBox look and behave like a TextBlock, but how do we get the line count?

好吧,如果我们想在后面的代码中直接访问它,那么我们可以注册到 TextBox 的 SizeChanged 事件.

Well, if we want to access it directly in the code behind then we can register to the TextBox's SizeChanged Event.

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();

        LongText = "This is a long line that has lots of text in it.  Because it is a long line, if a TextBlock's TextWrapping property is set to wrap then the text will wrap onto new lines. However, we can also use wrapping on a TextBox, that has some diffrent properties availible and then re-template it to look just like a TextBlock!";

        uiTextBox.SizeChanged += new SizeChangedEventHandler(uiTextBox_SizeChanged);

        this.DataContext = this;
    }

    void uiTextBox_SizeChanged(object sender, SizeChangedEventArgs e)
    {
        Lines = uiTextBox.LineCount;
    }

    public string LongText { get; set; }

    public int Lines
    {
        get { return (int)GetValue(LinesProperty); }
        set { SetValue(LinesProperty, value); }
    }

    // Using a DependencyProperty as the backing store for Lines.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty LinesProperty =
        DependencyProperty.Register("Lines", typeof(int), typeof(MainWindow), new UIPropertyMetadata(-1));
}

但是,由于我倾向于需要在当前窗口以外的地方使用类似的属性,和/或正在使用 MVVM 并且不想采用这种方法,那么我们可以创建一些 AttachedProperties 来处理检索和LineCount 的设置.我们将使用 AttachedProperties 来做同样的事情,但现在我们可以将它与任何地方的任何 TextBox 一起使用,并通过该 TextBox 而不是 Window 的 DataContext 绑定到它.

However, since I tend to need to use properties like that in places other then the current window, and/or am using MVVM and don't want to take that approach, then we can create some AttachedProperties to handle the retrieval and setting of the LineCount. We're going to use the AttachedProperties to do the same thing, but now we'll be able to use it with any TextBox anywhere, and bind to it through that TextBox instead of the Window's DataContext.

public class AttachedProperties
{
    #region BindableLineCount AttachedProperty
    public static int GetBindableLineCount(DependencyObject obj)
    {
        return (int)obj.GetValue(BindableLineCountProperty);
    }

    public static void SetBindableLineCount(DependencyObject obj, int value)
    {
        obj.SetValue(BindableLineCountProperty, value);
    }

    // Using a DependencyProperty as the backing store for BindableLineCount.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty BindableLineCountProperty =
        DependencyProperty.RegisterAttached(
        "BindableLineCount",
        typeof(int),
        typeof(MainWindow),
        new UIPropertyMetadata(-1));

    #endregion // BindableLineCount AttachedProperty

    #region HasBindableLineCount AttachedProperty
    public static bool GetHasBindableLineCount(DependencyObject obj)
    {
        return (bool)obj.GetValue(HasBindableLineCountProperty);
    }

    public static void SetHasBindableLineCount(DependencyObject obj, bool value)
    {
        obj.SetValue(HasBindableLineCountProperty, value);
    }

    // Using a DependencyProperty as the backing store for HasBindableLineCount.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty HasBindableLineCountProperty =
        DependencyProperty.RegisterAttached(
        "HasBindableLineCount",
        typeof(bool),
        typeof(MainWindow),
        new UIPropertyMetadata(
            false,
            new PropertyChangedCallback(OnHasBindableLineCountChanged)));

    private static void OnHasBindableLineCountChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
    {
        var textBox = (TextBox)o;
        if ((e.NewValue as bool?) == true)
        {
            textBox.SetValue(BindableLineCountProperty, textBox.LineCount);
            textBox.SizeChanged += new SizeChangedEventHandler(box_SizeChanged);
        }
        else
        {
            textBox.SizeChanged -= new SizeChangedEventHandler(box_SizeChanged);
        }
    }

    static void box_SizeChanged(object sender, SizeChangedEventArgs e)
    {
        var textBox = (TextBox)sender;
        (textBox).SetValue(BindableLineCountProperty, (textBox).LineCount);
    }
    #endregion // HasBindableLineCount AttachedProperty
}

现在,找到 LineCount 很简单:

Now, it's simple to find the LineCount:

<StackPanel>
    <TextBox x:Name="uiTextBox"
             TextWrapping="Wrap"
             local:AttachedProperties.HasBindableLineCount="True"
             Text="{Binding LongText}"
             Style="{StaticResource Local_TextBox}" />

    <TextBlock Text="{Binding Lines, StringFormat=Binding through the code behind: {0}}" />
    <TextBlock Text="{Binding ElementName=uiTextBox, Path=(local:AttachedProperties.BindableLineCount), StringFormat=Binding through AttachedProperties: {0}}" />
</StackPanel>

这篇关于TextBlock 的可见行数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆