再灵活的布局也不是万能的,极端的情况总会让它的脆弱一览无余,比如今天的主角TextBlock,它在WPF/SL应用程序中担任着显示文本的职责,它即轻巧又强大,大部分情况下都工作的非常好,但是过长的文本就会出现字符串截断了,这显然不是我们期望的,幸运的是,MS已经考虑到了这点,通过设置TextBlock的TextTrimming属性可以改变这种现象,可选值分别为None, CharacterEllipsis和WordEllipsis,字面意思已经很清楚了,就不再翻译了吧,对于非None值,会在截断处自动添加"...", 用户体验上自然更上了一层楼。
但是如果这部分文字比较重要,又比较长,如果仅是达到以上显示效果,是不是会令人抓狂呢(是的,你非常友好的告诉我这段信息太长,被截断了,但是你总得有个途径让我看到完整的信息吧)
为此EllipseBehavior应运而生,能够使得TextBlock在出现字符截断时自动将完整信息以ToolTip的形式展示出来,是不是很酷,但它的代价却是那么的微乎其微,你只需要添加类似如下的代码即可:

<interact:Interaction.Behaviors>
<Behaviors:EllipseBehavior/>
</interact:Interaction.Behaviors>
</TextBlock>
是不是很方便呢?由此我们可以看出Behavior确实给予了我们很大的方便
下面给出EllipseBehavior的代码

using System.Reflection;
using System.Windows.Controls;
using System.Windows.Threading;
namespace System.Windows.Behaviors
{
/// <summary>
/// Enable TextBlock show tooltip with full text when ellipsing
/// </summary>
public class EllipseBehavior : System.Windows.Interactivity.Behavior<TextBlock>
{
#region Field
MethodInfo m_GetLineDetails = typeof(TextBlock).GetMethod("GetLineDetails",
BindingFlags.NonPublic | BindingFlags.Instance);
object[] args = new object[] { 0, 0, 0, 0, 0 };
#endregion
#region DependencyProperty
/// <summary>
/// Gets or sets a value indicating whether [auto show tooltip when ellipsed].
/// </summary>
/// <value>
/// <c>true</c> if [auto show tooltip when ellipsed]; otherwise, <c>false</c>.
/// </value>
public bool AutoShowTooltipWhenEllipsed
{
get { return (bool)GetValue(AutoShowTooltipWhenEllipsedProperty); }
set { SetValue(AutoShowTooltipWhenEllipsedProperty, value); }
}
// Using a DependencyProperty as the backing store for IsShowTooltipWhenEllipsed. This enables animation, styling, binding, etc...
public static readonly DependencyProperty AutoShowTooltipWhenEllipsedProperty =
DependencyProperty.Register("AutoShowTooltipWhenEllipsed", typeof(bool), typeof(EllipseBehavior), new UIPropertyMetadata(true));
#endregion
#region Override
/// <summary>
/// Called after the behavior is attached to an AssociatedObject.
/// </summary>
/// <remarks>Override this to hook up functionality to the AssociatedObject.</remarks>
protected override void OnAttached()
{
base.OnAttached();
if (AutoShowTooltipWhenEllipsed && this.AssociatedObject.TextTrimming == TextTrimming.None)
this.AssociatedObject.TextTrimming = TextTrimming.CharacterEllipsis;
//Register events
var dpDesc = DependencyPropertyDescriptor.FromProperty(TextBlock.TextProperty, typeof(TextBlock));
dpDesc.AddValueChanged(this.AssociatedObject, OnTextChanged);
this.AssociatedObject.Loaded += new RoutedEventHandler(OnLoaded);
}
/// <summary>
/// Called when the behavior is being detached from its AssociatedObject, but before it has actually occurred.
/// </summary>
/// <remarks>Override this to unhook functionality from the AssociatedObject.</remarks>
protected override void OnDetaching()
{
base.OnDetaching();
//unload events
this.AssociatedObject.Loaded -= new RoutedEventHandler(OnLoaded);
this.AssociatedObject.SizeChanged -= new SizeChangedEventHandler(OnSizeChanged);
var dpDesc = DependencyPropertyDescriptor.FromProperty(TextBlock.TextProperty, typeof(TextBlock));
dpDesc.RemoveValueChanged(this.AssociatedObject, OnTextChanged);
}
#endregion
#region EventHandler
/// <summary>
/// Called when [size changed].
/// </summary>
/// <param name="sender">The sender.</param>
/// <param name="e">The <see cref="System.Windows.SizeChangedEventArgs"/> instance containing the event data.</param>
void OnSizeChanged(object sender, SizeChangedEventArgs e)
{
SetToolTip(null);
}
/// <summary>
/// Called when AssociatedObject [loaded].
/// </summary>
/// <param name="sender">The sender.</param>
/// <param name="e">The <see cref="System.Windows.RoutedEventArgs"/> instance containing the event data.</param>
void OnLoaded(object sender, RoutedEventArgs e)
{
SetToolTip(null);
//Register SizeChanged event
this.AssociatedObject.SizeChanged += new SizeChangedEventHandler(OnSizeChanged);
}
/// <summary>
/// Overrides the <see cref="T:System.Windows.DependencyObject"/> implementation of <see cref="M:System.Windows.DependencyObject.OnPropertyChanged(System.Windows.DependencyPropertyChangedEventArgs)"/> to also invoke any <see cref="E:System.Windows.Freezable.Changed"/> handlers in response to a changing dependency property of type <see cref="T:System.Windows.Freezable"/>.
/// </summary>
/// <param name="e">Event data that contains information about which property changed, and its old and new values.</param>
protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e)
{
base.OnPropertyChanged(e);
if (this.AssociatedObject != null && e.Property == AutoShowTooltipWhenEllipsedProperty)
{
if ((bool)e.NewValue && this.AssociatedObject.TextTrimming == TextTrimming.None)
this.AssociatedObject.TextTrimming = TextTrimming.CharacterEllipsis;
// if AssociatedObject is loaded, update tooltip
if (this.AssociatedObject.IsLoaded)
SetToolTip(null);
}
}
/// <summary>
/// Called when AssociatedObject [text changed].
/// </summary>
/// <param name="sender">The sender.</param>
/// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
private void OnTextChanged(object sender, EventArgs e)
{
Dispatcher.Invoke(DispatcherPriority.Background, (Action<object>)SetToolTip, null);
}
#endregion
#region Method
/// <summary>
/// Sets the tool tip.
/// </summary>
/// <param name="obj">The obj.</param>
private void SetToolTip(object obj)
{
if (AutoShowTooltipWhenEllipsed && this.AssociatedObject.TextTrimming != TextTrimming.None)
{
TextBlock tb = this.AssociatedObject;
m_GetLineDetails.Invoke(tb, args);
if ((int)args[4] > 0)
{
tb.ToolTip = tb.Text;
}
else
{
tb.ToolTip = null;
}
}
}
#endregion
}
}
GetLineDetails方法的访问限定是internal,此处只能通过反射调用
out int cchContent, out int cchEllipses)
{
...
}
调用后,其最后一个参数cchEllipses的值0与非0正好可以标识此时TextBlock是否发生ellipsing
需要注意的是 GetLineDetails方法必须在控件Load之后才能使用,否则会抛异常
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· 周边上新:园子的第一款马克杯温暖上架
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· DeepSeek如何颠覆传统软件测试?测试工程师会被淘汰吗?
· 使用C#创建一个MCP客户端