副标题:给WPF文本框加上省略号
介绍
WPF TextBox类没有内置选项,当文本被截断以适合可见区域时,自动显示省略号。这是一个非常常见的需求,在TextBlock控件中倒是有,所以我创建了自己的TextBox的子类TextBoxWithEllipsis,它在适当的时候会显示一个省略号。并且还提供了将省略号放置在可见文本的左侧、右侧或中心的功能。
带省略号属性的文本框
除了从TextBox继承的属性外,TextBoxWithEllipsis还具有以下属性。没有必要添加多余的依赖属性。
LongText
最原始的内容,当文本框的内容过长时,将会被替换为带省略号的短内容。你可以切换短内容和长内容。但是,当你获取Text的内容时,可能获取到的是被截取后的文本。所以可以获取LongText,以得到最原始的内容。
IsEllipsisEnabled
是否启用省略号。如果是true,那么将会截断过长的文本,替换为省略号(\u2026),否则就是一个普通的文本框。
UseLongTextForToolTip
是否启用提示。如果是true。那么当文本被截断后,在控件的ToolTip属性内添加整个文本的内容。如果是false,那么控件的提示保持不变。
EllipsisPlacement
指定省略号应出现在何处的枚举属性(左边,中间,右边,看你喜欢了。反正我是喜欢放在后面)。
FudgePix
偏移量,用于计算省略号的位置,默认为3.
具体代码
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
///
/// 如果不想要引用的话,你可以修改这个命名空间
///
namespace 带省略号的WPF文本框
{
///
/// 继承至TextBox控件,这个控件的目的是文本过长,显示省略号
///
public class TextBoxWithEllipsis : TextBox
{
///
/// 构造
///
public TextBoxWithEllipsis()
{
// 初始化继承的属性
IsReadOnlyCaretVisible = true;
// 初始化自定义的属性
IsEllipsisEnabled = true;
UseLongTextForToolTip = true;
FudgePix = 3.0;
_placement = EllipsisPlacement.Right;//默认右边
_internalEnabled = true;
LayoutUpdated += new EventHandler(TextBoxWithEllipsis_LayoutUpdated);//布局改变
SizeChanged += new SizeChangedEventHandler(TextBoxWithEllipsis_SizeChanged);//大小改变
}
///
/// 最原始的内容。设置此项和设置Text属性具有相同的效果,但获取Text属性可能会得到LongText的截断版本。
///
public string LongText
{
get { return _longText; }
set
{
_longText = value ?? "";
PrepareForLayout();
}
}
///
/// 省略号位置
///
public EllipsisPlacement EllipsisPlacement
{
get { return _placement; }
set
{
if (_placement != value)
{
_placement = value;
if (_DoEllipsis)
{
PrepareForLayout();
}
}
}
}
///
/// 是否启用省略号,如果启用了,就会截断文字,显示省略号。当文本获取焦点时,暂时失效
///
public bool IsEllipsisEnabled
{
get { return _externalEnabled; }
set
{
_externalEnabled = value;
PrepareForLayout();
if (_DoEllipsis)
{
TextBoxWithEllipsis_LayoutUpdated(this, EventArgs.Empty);
}
}
}
///
/// 修改控件提示, 如果为true,则当LongText过长时,工具提示将设置为LongText。如果为false,工具提示将设置为null,除非用户将其设置为LongText以外的其他值。
///
public bool UseLongTextForToolTip
{
get { return _useLongTextForToolTip; }
set
{
if (_useLongTextForToolTip != value)
{
_useLongTextForToolTip = value;
if (value)
{
//文本太长,修改提示为原始文本
if (ExtentWidth > ViewportWidth ||
Text != _longText)
{
ToolTip = _longText;
}
}
else
{
//不处理提示,置为默认值
if (_longText.Equals(ToolTip))
{
ToolTip = null;
}
}
}
}
}
public double FudgePix
{
get;
set;
}
///
/// 原始文本的适合的长度
///
private int _lastFitLen = 0;
///
/// 原始文本的最后长度
///
private int _lastLongLen;
///
/// 当前分配给原始文本的长度
///
private int _curLen;
///
/// 记录是否变化
///
private bool _externalChange = true;
///
/// 获取焦点时禁用省略号
///
private bool _internalEnabled = true;
private string _longText = "";
private bool _externalEnabled = true;
private bool _useLongTextForToolTip;
private EllipsisPlacement _placement;
///
/// 重写了OnTextChanged事件,以便在搜索适合的最长子字符串时在内部更改Text属性时避免引发TextChanged事件。
/// 如果在外部更改了文本,则在使用截断的版本(IsEllipsisEnabled)重写文本之前,将新文本复制到LongText中。
///
///
protected override void OnTextChanged(TextChangedEventArgs e)
{
if (_externalChange)
{
_longText = Text ?? "";
if (UseLongTextForToolTip) ToolTip = _longText;
PrepareForLayout();
base.OnTextChanged(e);
}
}
///
/// 获取焦点时隐藏省略号
///
///
protected override void OnGotKeyboardFocus(KeyboardFocusChangedEventArgs e)
{
_internalEnabled = false;
SetText(_longText);
base.OnGotKeyboardFocus(e);
}
///
/// 失去焦点时显示省略号
///
///
protected override void OnLostKeyboardFocus(KeyboardFocusChangedEventArgs e)
{
_internalEnabled = true;
PrepareForLayout();
base.OnLostKeyboardFocus(e);
}
///
/// 跳过TextChanged来修改文字
///
///
private void SetText(string text)
{
if (Text != text)
{
_externalChange = false;
Text = text; // 触发布局事件
_externalChange = true;
}
}
///
/// 通过布局事件来修改长文本,添加省略号
///
private void PrepareForLayout()
{
_lastFitLen = 0;
_lastLongLen = _longText.Length;
_curLen = _longText.Length;
//触发布局事件,处理省略号
SetText(_longText);
}
private void TextBoxWithEllipsis_SizeChanged(object sender, SizeChangedEventArgs e)
{
if (_DoEllipsis && e.NewSize.Width != e.PreviousSize.Width)
{
//重新计算文字长度,添加省略号
PrepareForLayout();
}
}
private bool _DoEllipsis { get { return IsEllipsisEnabled && _internalEnabled; } }
///
/// 文本或者大小改变时触发
///
///
///
private void TextBoxWithEllipsis_LayoutUpdated(object sender, EventArgs e)
{
if (_DoEllipsis)
{
//这将执行二分查找以确定适合可见区域的最大longText子串。使用递归的方式,因为如果在此处设置文本属性,则会再次引发此事件。
if (ViewportWidth + FudgePix
{
//长度太长
_lastLongLen = _curLen;
}
else
{
//长度适合
_lastFitLen = _curLen;
}
//测试一个新的子字符串,其长度介于最后一个已知适合的长度和最后一个已知太长的长度之间。
int newLen = (_lastFitLen + _lastLongLen) / 2;
if (_curLen == newLen)
{
if (UseLongTextForToolTip)
{
if (Text == _longText)
{
ToolTip = null;
}
else
{
ToolTip = _longText;
}
}
}
else
{
_curLen = newLen;
//在不引发TextChanged事件的情况下设置文本属性。但是会触发LayoutUpdated事件。
CalcText();
}
}
else if (UseLongTextForToolTip)
{
if (ViewportWidth
{
ToolTip = _longText;
}
else
{
ToolTip = null;
}
}
}
///
/// 计算省略号的位置
///
private void CalcText()
{
switch (_placement)
{
case EllipsisPlacement.Right:
SetText(_longText.Substring(0, _curLen) + "\u2026");
break;
case EllipsisPlacement.Center:
int firstLen = _curLen / 2;
int secondLen = _curLen - firstLen;
SetText(_longText.Substring(0, firstLen) + "\u2026" + _longText.Substring(_longText.Length - secondLen));
break;
case EllipsisPlacement.Left:
int start = _longText.Length - _curLen;
SetText("\u2026" + _longText.Substring(start));
break;
default:
throw new Exception("Unexpected switch value: " + _placement.ToString());
}
}
}
}
枚举
namespace 带省略号的WPF文本框
{
///
/// 用于指定省略号所在位置的枚举,你可以换成中文,不影响使用。
///
public enum EllipsisPlacement
{
Left,
Center,
Right
}
}
示例程序
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:带省略号的WPF文本框"
mc:Ignorable="d"
Title="带省略号的WPF文本框" Height="300" Width="450">
x:Name="tbEllipsis" IsReadOnly=""
VerticalContentAlignment="Center" FontSize="15"/>
Grid.Row="5" Width="120" Height="30" Margin="5" HorizontalAlignment="Left"/>
后台代码
using System.Windows;
namespace 带省略号的WPF文本框
{
///
/// MainWindow.xaml 的交互逻辑
///
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void RadioButton_Checked(object sender, RoutedEventArgs e)
{
tbEllipsis.EllipsisPlacement = EllipsisPlacement.Left;
}
private void RadioButton_Checked_1(object sender, RoutedEventArgs e)
{
tbEllipsis.EllipsisPlacement = EllipsisPlacement.Center;
}
private void RadioButton_Checked_2(object sender, RoutedEventArgs e)
{
tbEllipsis.EllipsisPlacement = EllipsisPlacement.Right;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
tbEllipsis.Text = tbSource.Text;
}
private void cbShowEllipsis_Checked(object sender, RoutedEventArgs e)
{
tbEllipsis.IsEllipsisEnabled = true;
}
private void cbShowToolTip_Checked(object sender, RoutedEventArgs e)
{
tbEllipsis.UseLongTextForToolTip = true;
}
private void cbShowEllipsis_Unchecked(object sender, RoutedEventArgs e)
{
tbEllipsis.IsEllipsisEnabled = false;
}
private void cbShowToolTip_Unchecked(object sender, RoutedEventArgs e)
{
tbEllipsis.UseLongTextForToolTip = false;
}
}
}
TextBoxWithEllipsis.cs-TextBoxWithEllipsis类的实现。您只需将此文件复制到您自己的项目中,你需要更改命名空间。
MainWindow.xaml-上面显示的可调整大小的WPF窗口,它测试并演示带有省略号控件的Textbox。
您可以直接在“tbEllipsis”控件中键入内容,或在“tbSource”字段中输入内容,然后单击按钮以编程方式测试是否设置带省略号的文本框的文本属性。复选框和单选按钮连接到tbEllipsis控件的文本框的相应属性。
控件在调整大小时自动调整显示的文本。如果控件的长度足够容纳所有文本,则省略号将消失(在示例中,控件将随窗口调整大小)。
每当tbEllipsis具有焦点时,省略号将被暂时禁用,以便用户可以编辑、选择或滚动全文。此行为内置于控件中。
重点
代码假设如果(TextBox.ViewportWidth+FudgePix)
重写OnTextChanged()方法,以防止在内部更改文本属性(例如,在LayoutUpdated事件)时引发TextChanged事件。在外部设置文本并在控件中输入或者粘贴文本就会触发事件。
摘要
在这篇短文中,我们已经了解了在WPF的文本框中,如果文字过长时,显示省略号的方法。祝各位好运,编码愉快,摆脱996,回归955!
领取专属 10元无门槛券
私享最新 技术干货