突出显示RichTextBox的当前行

时间:2012-07-25 04:42:31

标签: c# winforms richtextbox highlight

通过将透明颜色绘制为当前行上的背景颜色,突出显示每行的整个宽度。当线路切换时,原始背景颜色将恢复。

所以,我们想要做的是:

  1. 验证前一个和当前矩形不匹配,以免两次绘制相同区域
  2. 使用控件backcolor
  3. 替换最后一行的高亮显示
  4. 使用透明色突出显示当前行
  5. 使用每个已应用行的索引和矩形设置mLastHighlight
  6. 但是,删除突出显示时,文字会被涂上。应用突出显示时不会发生这种情况。

    一种解决方案是在重置背景颜色后重新重新显示控件上的文本。虽然文本格式,选择颜色,字体样式,超链接等将是繁琐的过滤。不是很优雅。

    这导致更简单的解决方案,刷新控件。虽然那会导致大量的闪烁。也不接受。

    有优雅的解决方案吗?我完全不知道为什么会这样。

    编辑:编辑以反映Code Gray的回复。

    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
    }
    

1 个答案:

答案 0 :(得分:3)

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

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

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

Scintilla控件不仅仅是Win32丰富的编辑控件的包装器。它必须做一个很多的自定义绘图来制作它的魔力,并且所有这些代码很难自己完成。这就是为什么这么多人首先使用Scintilla。如果你需要它的功能集,我强烈建议你效仿。

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