获取 FormattedText 的溢出文本

Get overflown text of FormattedText

我想获取设置FormattedText.MaxTextWidthFormattedText.MaxTextHeight后溢出的文本(即省略号后的子串)。有没有一种优雅的方法来实现这一目标?这似乎特别困难,因为 FormattedText 可能包含不同的字体系列、字体大小等。

嗯,这是一个艰难的过程。我可以非常接近它,但它不是 100% 准确。但是,也许您可​​以以此为起点。

此字符串的示例输出"This is some really long text that cannot fit within the width specified!"

方法:

基本上我写了一个 while 循环,当我给它一些文本时,它会检查实际格式化文本的宽度。如果宽度超过了显示省略号的宽度,那么我会去掉最后一个字符并一次又一次地检查直到它适合。

MainWindow.xaml:

<Window x:Class="GetOverflowTextTest.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Grid>
        <StackPanel Orientation="Vertical">
            <StackPanel Orientation="Horizontal">
                <RadioButton x:Name="radioButtonArial" Content="Arial Size 14" GroupName="Fonts" Click="ArialClick" Margin="5" IsChecked="True"/>
                <RadioButton x:Name="radioButtonTimesNewRoman" Content="Times New Roman Size 32" GroupName="Fonts" Click="TimesNewRomanClick" Margin="5"/>
            </StackPanel>
            <Grid>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="Auto"/>
                    <ColumnDefinition/>
                </Grid.ColumnDefinitions>
                <Grid.RowDefinitions>
                    <RowDefinition/>
                    <RowDefinition/>
                </Grid.RowDefinitions>
                <TextBlock Grid.Row="0" Grid.Column="0" Text="Long Text Block With Ellipsis (Width 200): " Margin="5" HorizontalAlignment="Right"/>
                <TextBlock Grid.Row="0" Grid.Column="1" x:Name="myTextBlock" Width="200" Margin="5" TextWrapping="NoWrap" TextTrimming="CharacterEllipsis" Background="DarkGreen" Foreground="White" HorizontalAlignment="Left" />
                <TextBlock Grid.Row="1" Grid.Column="0" Text="Here's your overflow text: " Margin="5" HorizontalAlignment="Right"/>
                <TextBlock Grid.Row="1" Grid.Column="1" x:Name="myOverflowTextBlock" Margin="5" TextWrapping="NoWrap" HorizontalAlignment="Left"/>
            </Grid>
            <StackPanel Orientation="Horizontal">
            </StackPanel>
            <StackPanel Orientation="Horizontal">
            </StackPanel>
        </StackPanel>
    </Grid>
</Window>

MainWindow.xaml.cs:

using System.Globalization;
using System.Windows;
using System.Windows.Media;

namespace GetOverflowTextTest
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        private const string TEXT = "This is some really long text that cannot fit within the width specified!";

        public MainWindow()
        {
            InitializeComponent();
            this.Loaded += OnLoaded;
            this.myTextBlock.Text = TEXT;
        }

        private void OnLoaded(object sender, RoutedEventArgs e)
        {
            UpdateFont();
        }

        private void UpdateFont()
        {
            if (this.radioButtonArial.IsChecked.HasValue && this.radioButtonArial.IsChecked.Value)
            {
                // Change the font to Arial
                this.myTextBlock.FontFamily = new FontFamily("Arial");
                this.myTextBlock.FontSize = 14;
            }
            else
            {
                // Change the font to Times New Roman
                this.myTextBlock.FontFamily = new FontFamily("Times New Roman");
                this.myTextBlock.FontSize = 32;
            }

            // Calculate the overflow text using the font, and then update the result.
            CalculateAndUpdateOverflowText();
        }

        private void CalculateAndUpdateOverflowText()
        {
            // Start with the full text.
            var displayedText = TEXT;

            // Now start trimming until the width shrinks to the width of myTextBlock.
            var fullFormattedText = new FormattedText(displayedText, CultureInfo.InvariantCulture, FlowDirection.LeftToRight, new Typeface(this.myTextBlock.FontFamily, myTextBlock.FontStyle, myTextBlock.FontWeight, myTextBlock.FontStretch), myTextBlock.FontSize, new SolidColorBrush(Colors.Black), 1.0);

            while (fullFormattedText.Width > this.myTextBlock.Width)
            {
                displayedText = displayedText.Remove(displayedText.Length - 1, 1);
                fullFormattedText = new FormattedText(displayedText, CultureInfo.InvariantCulture, FlowDirection.LeftToRight, new Typeface(this.myTextBlock.FontFamily, myTextBlock.FontStyle, myTextBlock.FontWeight, myTextBlock.FontStretch), myTextBlock.FontSize, new SolidColorBrush(Colors.Black), 1.0);
            }

            // What you have left is the displayed text.  Remove it from the overall string to get the remainder overflow text.
            // The reason why I added "- 3" is because there are three ellipsis characters that cover up some of the text that would have otherwise been displayed.
            var overflowText = TEXT.Remove(0, displayedText.Length - 3);

            // Update the text block
            this.myOverflowTextBlock.Text = overflowText;
        }

        private void ArialClick(object sender, RoutedEventArgs e)
        {
            UpdateFont();
        }

        private void TimesNewRomanClick(object sender, RoutedEventArgs e)
        {
            UpdateFont();
        }
    }
}

在进一步思考这个问题之后,我想出了这个解决方案(returns 第一个的索引:

/// <summary>
/// Retrieves the index at which the text flows over (the first index that is trimmed)
/// </summary>
/// <param name="text"></param>
/// <returns></returns>
public static int GetOverflowIndex(FormattedText text)
{
    // Early out: No overflow
    if (text.BuildHighlightGeometry(new Point(0, 0), text.Text.Length - 1, 1) != null)
        return -1;

    int sublen = text.Text.Length;
    int offset = 0;
    int index = 0;

    while (sublen > 1)
    {
        string debugStr = text.Text.Substring(offset, sublen);
        index = offset + sublen / 2;
        Geometry characterGeometry = text.BuildHighlightGeometry(new Point(0, 0), index, 1);

        // Geometry is null, if the character is overflown
        if (characterGeometry != null)
        {
            offset = index;
            sublen = sublen - sublen / 2;
        }
        else
        {
            sublen /= 2;
        }
    }

    return index;
}