为什么要自定义?

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

早在「老版添雨跟打器」之中,@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. 颜色方面还没有找到是否可以完全自定义颜色的方式

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

标签: 技术, C#

添加新评论