C# WinForm RichTextBox 如何自定义下划线样式颜色?

为什么要自定义?

「添雨跟打器」中的核心功能之一——「词库管理」。这是一个大类。包含有词库添加、删除、自动提示,智能学习,理论码长等功能。用户在跟打过程中,如何以方便,快捷的方式提示给用户?这是一个产品体验上的问题。

早在「老版添雨跟打器」之中,@hwj 帮忙制作了「提词器」。使用的技术很简单,就是在 RichTextBox 上显示 Label ,用来划出不同的线条,颜色。但是缺点很明显。

  1. 渲染效率问题
  2. 显示样式单一

这两个问题都很好解决。但是「效率」和显示的「感观」上的问题仍然有一种,无法让人忍受的问题。例如,在拖动滚动条时,整个显示错位。如果让它们随之滚动,则会存在效率上的问题。两者,似乎存在一种不可兼得的情况。

寻找

为了解决问题。利用搜索引擎,去寻找解决方案。MSDN 是首先的选择。正好,在其上便找到如下一篇 《how to change the underline style》 。里面含有大量的 VB 代码。

手动翻译成 C# 代码如下文。

代码

新建类:

public class Underline
{
	public RichTextBox R { get; private set; }
	
	public Underline(RichTextBox richText)
	{
		this.R = richText;
	}
}

创建结构体:

[StructLayout(LayoutKind.Sequential)]
private struct CHARFORMAT
{
	public int cbSize;
	public uint dwMask;
	public uint dwEffects;
	public int yHeight;
	public int yOffset;
	public int crTextColor;
	public byte bCharSet;
	public byte bPitchAndFamily;

	[MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)]
	public char[] szFaceName;

	public short wWeight;
	public short sSpacing;
	public int crBackColor;
	public int LCID;
	public uint dwReserved;
	public short sStyle;
	public short wKerning;
	public byte bUnderlineType;
	public byte bAnimation;
	public byte bRevAuthor;
}

 引入 Win32 API:

[DllImport("user32", CharSet = CharSet.Auto)]
private static extern int SendMessage(IntPtr hWnd, int msg, int wParam, ref CHARFORMAT lp);

 创建属性:

public UnderlineStyle SelectionUnderlineStyle
{
	set
	{
		//Ensure we don't alter the color by accident.
		var color = SelectionUnderlineColor;
		//Ensure we don't show it if it shouldn't be shown.
		if (value == UnderlineStyle.None)
		{
			color = UnderlineColor.Black;
		}
		var fmt = new CHARFORMAT
		{
			yOffset = 100
		};
		fmt.cbSize = Marshal.SizeOf(fmt);
		fmt.dwMask = CFM_UNDERLINETYPE;
		fmt.bUnderlineType = (byte)((byte)value | (byte)color);
		fmt.bAnimation = 1;
		//Set the underline type.
		SendMessage(this.R.Handle, EM_SETCHARFORMAT, SCF_SELECTION, ref fmt);
	}
	get
	{
		var fmt = new CHARFORMAT
		{
			yOffset = 4
		};
		fmt.cbSize = Marshal.SizeOf(fmt);
		//Get the underline style.
		SendMessage(this.R.Handle, EM_GETCHARFORMAT, SCF_SELECTION, ref fmt);
		// Default to no underline.
		if ((fmt.dwMask & CFM_UNDERLINETYPE) == 0)
		{
			return UnderlineStyle.None;
		}
		var style = (byte)fmt.bUnderlineType & 0xF;
		return (UnderlineStyle)style;
	}
}

public UnderlineColor SelectionUnderlineColor
{
	get
	{
		var fmt = new CHARFORMAT();
		fmt.cbSize = Marshal.SizeOf(fmt);
		//Get the underline color.
		SendMessage(this.R.Handle, EM_GETCHARFORMAT, SCF_SELECTION, ref fmt);
		// Default to black.
		if ((fmt.dwMask & CFM_UNDERLINETYPE) == 0)
		{
			return UnderlineColor.Black;
		}
		var style = fmt.bUnderlineType & 0xF;
		return (UnderlineColor)style;
	}
	set
	{
		//Ensure we don't alter the style.
		var style = SelectionUnderlineStyle;
		//Ensure we don't show it if it shouldn't be shown.
		if (style == UnderlineStyle.None)
		{
			value = UnderlineColor.Black;
		}
		var fmt = new CHARFORMAT();
		fmt.cbSize = Marshal.SizeOf(fmt);
		fmt.dwMask = CFM_UNDERLINETYPE;
		fmt.bUnderlineType = (byte)((byte)style | (byte)value);
		//Set the underline color.
		SendMessage(this.R.Handle, EM_SETCHARFORMAT, SCF_SELECTION, ref fmt);
	}
}

 使用:

var line = new Underline(this.richTextBoxEx1);
this.richTextBoxEx1.SelectionStart = 0;
this.richTextBoxEx1.SelectionLength = 1;
line.SelectionUnderlineStyle = UnderlineStyle.Dash;
line.SelectionUnderlineColor = UnderlineColor.Blue;

 其它问题

  1. 下划线样式好像并不是每个都有效,像 wave 样式
  2. 颜色方面还没有找到是否可以完全自定义颜色的方式

如果有读者读至此处,了解相关内容,欢迎留言。

发表评论

电子邮件地址不会被公开。 必填项已用*标注