获取 FormattedText 的溢出文本
Get overflown text of FormattedText
我想获取设置FormattedText.MaxTextWidth
和FormattedText.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;
}
我想获取设置FormattedText.MaxTextWidth
和FormattedText.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;
}