【问题标题】:Mouse cursor flickers over selected text - how to prevent this?鼠标光标在选定的文本上闪烁 - 如何防止这种情况?
【发布时间】:2014-04-09 15:20:18
【问题描述】:

将鼠标移到 RichTextBox(C#、.NET 4.0、WinForms)中的选定文本上时遇到奇怪的行为:当我移动鼠标光标时,它会在 Cursors.Arrow 和 Cursors.IBeam 之间闪烁。

我找到了禁用闪烁的代码:

protected override void WndProc(ref System.Windows.Forms.Message m)  
{  
     if (m.Msg == WM_SETCURSOR) //WM_SETCURSOR is set to 0x20
          return;
}

但随后鼠标光标卡在 Cursors.Arrow 中,即使我手动将其设置为其他内容,例如:

void RTFBox_MouseMove(object sender, MouseEventArgs e)
{
    Cursor = Cursors.IBeam;
}

(我在 MouseMove 函数中也有逻辑将Cursor 设置为其他类型的非Arrow 光标,具体取决于鼠标所在的位置。)

我也试过了:

public override Cursor Cursor
{
    get
    {
        //(I have other logic here to determine the desired cursor type I want; in all cases it was a non-Arrow cursor)
        return Cursors.Cross; //'Cross' instead of 'IBeam' just to prove whether this works
    }
    set
    {
        return;
    }
}

这成功地使光标成为十字(但仅当我注释掉 WndProc 代码时),但是当我将鼠标悬停在选定的文本上时闪烁仍然存在(鼠标光标在箭头和十字之间切换)。

在尝试寻找解决方案时,我遇到了this post,但致电
SendMessage(Handle, LVM_SETHOTCURSOR, IntPtr.Zero, Cursors.IBeam.Handle);
从继承自 RichTextBox 的类中没有解决闪烁问题。

我的问题似乎与this post 中描述的问题相同,但该问题被描述为存在于 .NET 3.0 中并已在 .NET 3.5 中修复。

当我创建一个新项目并在表单中插入一个 RichTextBox 时,闪烁仍然存在。

因此,我的问题是:如何防止这种闪烁?或者有谁知道这个问题是否在更高版本的 .NET/visual studio 中得到解决?

[更新:我下载了 Visual Studio 2013,但“闪烁”效果仍然存在。我下载了 .Net 4.5.1 安装程序并告诉它修复,但“闪烁”仍然存在。在“属性”>“参考”下,它说“System.Windows.Forms”是版本 4.0.0.0;我想这意味着没有必要更新 4.0 以上的版本?]

【问题讨论】:

    标签: c# .net winforms richtextbox


    【解决方案1】:

    我无法真正谈论闪烁的问题...这听起来像是您需要与供应商讨论的问题。

    至于为什么您的代码“修复”了问题,但不允许您更改光标,这有助于更多地了解 Windows 消息泵的工作原理。

    基本上,在低级别,您正在拦截更改光标的请求,然后阻止它们。如果您查看 the documentation 的此功能,您会看到以下评论:

    继承人须知
    继承控件应调用基类的 WndProc 方法来处理它们不处理的任何消息。

    您正在重写此函数,实质上成为“继承者”,然后通过忽略光标来“处理”请求更改光标的消息。

    【讨论】:

      【解决方案2】:

      我添加到 chess123mate 的答案中,让它在垂直滚动条上显示箭头光标:

      [DllImport("user32.dll")]
      public static extern int SetCursor(IntPtr cursor);
      
      private const int WM_SETCURSOR = 0x20;
      protected override void WndProc(ref System.Windows.Forms.Message m)
      {
          if (m.Msg == WM_SETCURSOR)
          {
              var scrollbarWidth = System.Windows.Forms.SystemInformation.VerticalScrollBarWidth;
              var x = PointToClient(Control.MousePosition).X;
              var inScrollbar = x > this.Width - scrollbarWidth;
      
              var cursor = inScrollbar ? Cursors.Arrow : Cursors.IBeam;
              SetCursor(cursor.Handle);
              m.Result = new IntPtr(1); //Signify that we dealt with the message. We should be returning "true", but I can't figure out how to do that.
              return;
          }
          base.WndProc(ref m);
      }
      

      【讨论】:

        【解决方案3】:

        WndProc 的 WM_SETCURSOR 的文档可在此处找到:
        http://msdn.microsoft.com/en-us/library/windows/desktop/ms648382(v=vs.85).aspx
        您可以使用此代码手动将光标设置为特定类型:

        [DllImport("user32.dll")]
        public static extern int SetCursor(IntPtr cursor);
        
        private const int WM_SETCURSOR = 0x20;
        protected override void WndProc(ref System.Windows.Forms.Message m)
        {
            if (m.Msg == WM_SETCURSOR)
            {
                SetCursor(Cursors.IBeam.Handle);
                m.Result = new IntPtr(1); //Signify that we dealt with the message. We should be returning "true", but I can't figure out how to do that.
                return;
            }
            base.WndProc(ref m);
        }
        

        但是,每次调用 SetCursor 时,此代码都会导致文本插入符号闪烁(每次在控件中移动鼠标时都会发生这种情况)。

        【讨论】:

        • 这很好用。我建议的唯一更改是使用 this.Cursor 而不是硬编码游标枚举值 - 这样控件仍然可以管理要显示的游标的状态。解决了我的问题,谢谢!
        【解决方案4】:

        闪烁主要是由于图形对象或表面重绘造成的。如果您将代码放在不断更新自身的控件事件(例如 MouseMove)中,并且代码以某种方式修改了它的表面或内容,则会发生闪烁。 我曾经使用 DoubleBuffering 来解决闪烁问题。只需将此代码添加到表单的构造函数中(在 InitializeComponent() 方法的上方或下方):

        DoubleBuffered = true;
        

        如果这不能解决问题,则问题可能不是因为图形问题。

        当两个事件代码要同时更改光标时,光标也会闪烁。 它可能是您的代码或默认代码(例如,当您将光标移动到控件上方时,IBeam 会自动出现)。 检查您的代码,它是否包含在您使用期间修改光标或选定文本的代码。然后每次将光标从另一个事件更改为您想要的类型。我的意思是:

        //MouseMove is the best choice in this case
        private void RichTextBox1_MouseMove(object sender, MouseEventArgs e)
        {
            Cursor = Cursors.Arrow;
        }
        

        但我使用的是 .Net 4.0,我没有任何问题。我认为它已在最新版本中修复。

        希望对您有所帮助。 :)

        【讨论】:

        • RichTextBox 控件自己绘制,因此 TextBoxBase 控件忽略了 DoubleBuffered 属性。
        • 我已经在表单属性中有 DoubleBuffered = true 。我的帖子已经提到我正在使用 MouseMove,但该代码要么不起作用(因为 WndProc 代码),要么不能防止闪烁。如前所述,我在一个空白项目中也遇到了这个问题(只是没有代码的表单上的 RichTextBox)。我也在使用 .NET 4.0。
        猜你喜欢
        • 2010-10-10
        • 2012-08-02
        • 1970-01-01
        • 1970-01-01
        • 2020-12-14
        • 1970-01-01
        • 2011-12-22
        • 2022-08-03
        • 1970-01-01
        相关资源
        最近更新 更多