C# RichTextBox 移除自定义 SelectionBackColor

C# RichTextBox Remove Custom SelectionBackColor

经过一些研究,我相信我问的是与 Remove richtextbox SelectionBackColor 相同的问题。我遇到了同样的问题,但我认为该线程中的答案是不够的,因为问题没有得到清楚的解释。请看下面:

RichTextBox 中,我如何 删除 自定义 BackColor 来自一些但不是全部的文本 (SelectionBackColor ) 以便它假定控件 BackColor,即使 BackColor 将来发生变化?

我有一种方法可以突出显示一些文本并使用 SelectionBackColor 更改其 BackColor。我有另一种方法可以更改整个控件的 BackColor 。这些事件可以独立发生。

如果我想 "remove" 一些 SelectionBackColor,我可以尝试将 SelectionBackColor 设置为 Color.Transparent,但它最终变成了白色。如果我的 RichTextBoxcurrent BackColor 是白色,那暂时没问题。如果我将SelectionBackColor设置为当前的BackColor,暂时没问题,直到BackColor从另一个方法改变。

RichTextBox.BackColor 更改后,任何以前突出显示的地方都使用白色或以前的 BackColor,而不是像以前没有突出显示的文本那样采用新颜色。

我已尝试删除和替换文本,但据我所知,这否定了保留该文本的任何其他自定义格式的能力。将 SelectionBackColor 设置为 null 无效。

使用下面的代码可以很容易地看出我在说什么:

protected override void OnLostFocus(EventArgs e)
{
    base.OnLostFocus(e);
    this.BackColor = Color.Gray;
    if (SelectionLength > 0)
    {
        SelectionBackColor = Color.Yellow;
    }
}

protected override void OnGotFocus(EventArgs e)
{
    base.OnGotFocus(e);
    this.ResetBackColor();
    if (SelectionLength > 0)
    {
        // The goal of this line is to "remove" the yellow.
        // By assigning it any value, it seems to have lost
        // the ability to use the control's BackColor normally.
        SelectionBackColor = this.BackColor;// or Color.Transparent
    }
}

使用上面的代码在自定义 RichTextBox 对象中键入一些文本,突出显示其中的一小部分,然后使框失去焦点。您将看到以黄色突出显示的文本。然后,使框获得焦点。正如预期的那样,黄色背景将消失。但是,如果您将插入符号移到文本的其他位置并使控件再次失去焦点,您将看到先前突出显示的文本不再采用灰色背景颜色。

这很有趣。似乎(在我测试过的 Windows 7/.Net 3.5 上,也许在其他地方)System.Windows.Forms.RichTextBox.SelectionBackColor may have a bug clearing the selection back color. The source code 确实:

    public Color SelectionBackColor {
        set
        {
            //Note: don't compare the value to the old value here: it's possible that 
            //you have a different range selected.
            selectionBackColorToSetOnHandleCreated = value;
            if (IsHandleCreated)
            {
                NativeMethods.CHARFORMAT2A cf2 = new NativeMethods.CHARFORMAT2A();
                if (value == Color.Empty)
                {
                    cf2.dwEffects = RichTextBoxConstants.CFE_AUTOBACKCOLOR;
                }
                else
                {
                    cf2.dwMask = RichTextBoxConstants.CFM_BACKCOLOR;
                    cf2.crBackColor = ColorTranslator.ToWin32(value);
                }

                UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), RichTextBoxConstants.EM_SETCHARFORMAT, RichTextBoxConstants.SCF_SELECTION, cf2);
            }
        }
    }

正如您所注意到的,当设置为 richTextBox.BackColor. Nor does it clear the color when set to RichTextBox.DefaultBackColor 时,这实际上并没有清除背景颜色,它只是将选择背景颜色设置为默认的灰色控制颜色。源代码看起来好像是 试图 在设置为 Color.Empty 时清除选择背景颜色——但至少在我的机器上,它什么都不做。

但是如果我创建一个扩展方法,在发送消息之前也将 cf2.dwMask = RichTextBoxConstants.CFM_BACKCOLOR; 设置为空颜色,那么 SetSelectionBackColor(Color.Empty) 现在可以工作了!

    public static void SetSelectionBackColor(this RichTextBox richTextBox, Color value)
    {
        if (richTextBox.IsHandleCreated && value == Color.Empty)
        {
            var cf2 = new CHARFORMAT2();

            cf2.dwEffects = RichTextBoxConstants.CFE_AUTOBACKCOLOR;
            cf2.dwMask = RichTextBoxConstants.CFM_BACKCOLOR;
            cf2.crBackColor = ColorTranslator.ToWin32(value);

            UnsafeNativeMethods.SendMessage(new HandleRef(richTextBox, richTextBox.Handle), RichTextBoxConstants.EM_SETCHARFORMAT, RichTextBoxConstants.SCF_SELECTION, cf2);
        }
        else
        {
            richTextBox.SelectionBackColor = value;
        }
    }

完整方法,包含常量和 类 改编自 here and here and here and here and here:

public static class RichTextBoxConstants
{
    // http://referencesource.microsoft.com/#System.Windows.Forms/winforms/Managed/System/WinForms/RichTextBoxConstants.cs,31b52ac41e96a888
    /* EM_SETCHARFORMAT wparam masks */
    internal const int SCF_SELECTION = 0x0001;

    internal const int EM_SETCHARFORMAT = (NativeMethods.WM_USER + 68);

    internal const int CFM_BACKCOLOR = 0x04000000;

    /* NOTE: CFE_AUTOCOLOR and CFE_AUTOBACKCOLOR correspond to CFM_COLOR and
       CFM_BACKCOLOR, respectively, which control them */
    internal const int CFE_AUTOBACKCOLOR = CFM_BACKCOLOR;
}

[StructLayout(LayoutKind.Sequential, Pack = 4)]
public class CHARFORMAT2
{
    // http://referencesource.microsoft.com/#System.Windows.Forms/winforms/Managed/System/WinForms/NativeMethods.cs,acde044a28b57a48
    // http://pinvoke.net/default.aspx/Structures/CHARFORMAT2.html

    public int cbSize = Marshal.SizeOf(typeof(CHARFORMAT2));
    public int dwMask;
    public int dwEffects;
    public int yHeight;
    public int yOffset;
    public int crTextColor;
    public byte bCharSet;
    public byte bPitchAndFamily;
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
    public string szFaceName;
    public short wWeight;
    public short sSpacing;
    public int crBackColor;
    public int lcid;
    public int dwReserved;
    public short sStyle;
    public short wKerning;
    public byte bUnderlineType;
    public byte bAnimation;
    public byte bRevAuthor;
    public byte bReserved1;
}

public static class NativeMethods
{
    // http://referencesource.microsoft.com/#System.Windows.Forms/winforms/Managed/System/WinForms/NativeMethods.cs,e75041b5218ff60b

    public const int WM_USER = 0x0400;

    public static void SetSelectionBackColor(this RichTextBox richTextBox, Color value)
    {
        if (richTextBox.IsHandleCreated && value == Color.Empty)
        {
            var cf2 = new CHARFORMAT2();

            cf2.dwEffects = RichTextBoxConstants.CFE_AUTOBACKCOLOR;
            cf2.dwMask = RichTextBoxConstants.CFM_BACKCOLOR;
            cf2.crBackColor = ColorTranslator.ToWin32(value);

            UnsafeNativeMethods.SendMessage(new HandleRef(richTextBox, richTextBox.Handle), RichTextBoxConstants.EM_SETCHARFORMAT, RichTextBoxConstants.SCF_SELECTION, cf2);
        }
        else
        {
            richTextBox.SelectionBackColor = value;
        }
    }
}

public static class UnsafeNativeMethods
{
    // http://referencesource.microsoft.com/#System.Windows.Forms/winforms/Managed/System/WinForms/UnsafeNativeMethods.cs,0d546f58103867e3
    // For RichTextBox
    //
    [DllImport("user32.dll", CharSet = CharSet.Auto)]
    public static extern IntPtr SendMessage(HandleRef hWnd, int msg, int wParam, [In, Out, MarshalAs(UnmanagedType.LPStruct)] CHARFORMAT2 lParam);
}

我做到了:

SelectionBackColor = default(Color);

而且效果很好。