为什么要自定义?
「添雨跟打器」中的核心功能之一——「词库管理」。这是一个大类。包含有词库添加、删除、自动提示,智能学习,理论码长等功能。用户在跟打过程中,如何以方便,快捷的方式提示给用户?这是一个产品体验上的问题。
早在「老版添雨跟打器」之中,@hwj 帮忙制作了「提词器」。使用的技术很简单,就是在 RichTextBox 上显示 Label ,用来划出不同的线条,颜色。但是缺点很明显。
- 渲染效率问题
- 显示样式单一
这两个问题都很好解决。但是「效率」和显示的「感观」上的问题仍然有一种,无法让人忍受的问题。例如,在拖动滚动条时,整个显示错位。如果让它们随之滚动,则会存在效率上的问题。两者,似乎存在一种不可兼得的情况。
寻找
为了解决问题。利用搜索引擎,去寻找解决方案。MSDN 是首先的选择。正好,在其上便找到如下一篇 《how to change the underline style》 。里面含有大量的 VB 代码。
手动翻译成 C# 代码如下文。
代码
新建类:
1 2 3 4 5 6 7 8 9 |
public class Underline { public RichTextBox R { get; private set; } public Underline(RichTextBox richText) { this.R = richText; } } |
创建结构体:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
[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:
1 2 |
[DllImport("user32", CharSet = CharSet.Auto)] private static extern int SendMessage(IntPtr hWnd, int msg, int wParam, ref CHARFORMAT lp); |
创建属性:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 |
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); } } |
使用:
1 2 3 4 5 |
var line = new Underline(this.richTextBoxEx1); this.richTextBoxEx1.SelectionStart = 0; this.richTextBoxEx1.SelectionLength = 1; line.SelectionUnderlineStyle = UnderlineStyle.Dash; line.SelectionUnderlineColor = UnderlineColor.Blue; |
其它问题
- 下划线样式好像并不是每个都有效,像 wave 样式
- 颜色方面还没有找到是否可以完全自定义颜色的方式
如果有读者读至此处,了解相关内容,欢迎留言。