5

This highlights the entire width of each line by painting a transparent color as the backcolor on the current line. When the line switches, the original background color is restored.

So, what we want to do is:

  1. Verify the previous and current rectangles don't match, so not to paint the same area twice
  2. Replace the last line's highlight using the controls backcolor
  3. Highlight the current line using a transparent color
  4. Set mLastHighlight with the index and rectangle for each applied line

However, when removing the highlight, the text is painted over. This does not occur when applying the highlight.

One solution would be to repaint the text back on the control after resetting the back color. though the text formatting, selection colors, font styles, hyperlinks, etc would be tedious to filter. Not very elegant.

This leads to a simpler solution, refreshing the control. Though that would cause massive flickering. Not acceptable either.

Is there an elegant solution? I'm completely baffled why this occurs.

EDIT: Edited to reflect Code Gray's response.

using System;

public class RTBHL : RichTextBox
{
    private LastHighlight mLastHighlight = new LastHighlight(0, Rectangle.Empty);

    private class LastHighlight
    {
        public int mCharIndex;
        public Rectangle mRectangle;

        public LastHighlight(int index, Rectangle r)
        {
            mCharIndex = index;
            mRectangle = r;
        }
    }

    public void PaintLineHighlight()
    {
        using (Graphics g = this.CreateGraphics)
        {
            // highlight color
            Color c = Color.Beige;
            // current pen color
            Pen cp = new Pen(Color.Beige);
            // color for removing highlight
            Pen lp = new Pen(this.BackColor);
            // brush for removing highlight
            SolidBrush lb = new SolidBrush(this.BackColor);
            // brush for applying highlight
            SolidBrush cb = new SolidBrush(Color.FromArgb(64, c.R, c.G, c.B));
            // index of the current line
            int index = this.GetFirstCharIndexOfCurrentLine;
            // rectangle to specify which region to paint too
            Rectangle r = new Rectangle();

            // specify dimensions
            r.X = 0;
            r.Y = this.GetPositionFromCharIndex(index).Y;
            r.Width = this.HorizontalScrollBarWidth;
            r.Height = Convert.ToInt32(this.Font.Height * this.ZoomFactor);

            // this will always be true unless the current line remains the same
            if (!(mLastHighlight.mCharIndex == index) && !(mLastHighlight.mRectangle == r))
            {
                // remove the last highlight. regardless of the brush specified, white is always applied, and the text is painted over
                g.DrawRectangle(lp, mLastHighlight.mRectangle);
                g.FillRectangle(lb, mLastHighlight.mRectangle);
                // apply highlight to the current line
                g.DrawRectangle(cp, r);
                g.FillRectangle(cb, r);
            }

            mLastHighlight = new LastHighlight(index, r);
        }
    }

#region RichScrollBars
    [DllImport("user32.dll")]
    [return: MarshalAs(UnmanagedType.Bool)]
    private static extern bool GetScrollInfo(IntPtr hWnd, int fnBar, ref SCROLLINFO si);

    [StructLayout(LayoutKind.Sequential)]
    public class SCROLLINFO
    {
        public int cbSize;
        public int fMask;
        public int nMin;
        public int nMax;
        public int nPage;
        public int nPos;
        public int nTrackPos;
        public SCROLLINFO()
        {
            this.cbSize = Marshal.SizeOf(typeof(SCROLLINFO));
        }

        public SCROLLINFO(int mask, int min, int max, int page, int pos)
        {
            this.cbSize = Marshal.SizeOf(typeof(SCROLLINFO));
            this.fMask = mask;
            this.nMin = min;
            this.nMax = max;
            this.nPage = page;
            this.nPos = pos;
        }
    }

    private const int SIF_ALL = 0X17;
    private const int SB_HORZ = 0;
    private const int SB_VERT = 1;

    public int HorizontalScrollBarWidth()
    {
        SCROLLINFO si = new SCROLLINFO() {fMask = SIF_ALL};
        GetScrollInfo(this.Handle, SB_HORZ, si);

        return Math.Max(si.nMax, this.Width);
    }

    public int VerticalScrollBarHeight()
    {
        SCROLLINFO si = new SCROLLINFO() {fMask = SIF_ALL};
        GetScrollInfo(this.Handle, SB_VERT, si);

        return Math.Max(si.nMax, this.Height);
    }
#endregion
}
4

1 回答 1

3

这里的问题是您复制的代码是为 Scintilla 设计的。SCI_*常量由 Scintilla 标头在内部定义,它们引用的消息仅对 Scintilla 控件有意义。

将这些消息发送到本机 Win32 富编辑控件不会做任何事情,因为它不是为处理这些消息而设计的。(或者更糟的是,一个或多个SCI_*常量碰巧与富编辑控件识别的一个或多个消息标识符发生冲突,从而产生一些潜在的有趣行为。)

除非您实际上在项目中使用了 Scintilla 编辑控件(您说过不想这样做),否则该代码不会做任何有趣的事情。它不是为 Win32 丰富的编辑控件编写的,它是为与 Scintilla 控件交互而编写的。

Scintilla 控件不仅仅是 Win32 富编辑控件的包装器。它必须做大量的自定义绘图才能发挥它的魔力,而所有这些代码都很难靠你自己来正确完成。这就是为什么这么多人首先使用 Scintilla 的原因。如果您需要它的功能集,我强烈建议您效仿。

无论如何,我实际上不知道这是否可以使用 Win32 富编辑控件。我不这么认为,但我不能对这个事实发誓。我想你可以通过设置选择颜色来破解它,但这似乎不是一个很好的解决方案。像丹尼尔在这里建议的东西。我不是 Scintilla 专家,但在我未经训练的眼中,这看起来有点像基于 Scintilla 的代码的道德等价物,但为 Win32 丰富的编辑控件编写(通过其 .NET WinForms 包装器)。

于 2012-07-25T08:36:20.763 回答