5

I have a readonly RichTextBox, with its cursor set to Arrow. Even so, when I hover it, the cursor flickers, and switches very quickly between Arrow and IBeam. How can I make it stay on Arrow and not flicker?

General Grievance
  • 4,555
  • 31
  • 31
  • 45
Michael Haddad
  • 4,085
  • 7
  • 42
  • 82

2 Answers2

4

Jimi's answer works well to stop flickering, but I don't have a good feeling about capturing mouse on mouse move. For example one issue that I see in that solution, is if you set the capture on mouse move, then keyboard shortcuts like Alt+F4 or Alt+Space will stop working.

I'd prefer to handle WndProc and set the cursor when received WM_SETCURSOR:

using System.Windows.Forms;
public class ExRichTextBox : RichTextBox
{
    const int WM_SETCURSOR = 0x0020;
    protected override void WndProc(ref Message m)
    {
        if (m.Msg == WM_SETCURSOR)
            Cursor.Current = this.Cursor;
        else
            base.WndProc(ref m);
    }
}

It stops flickering. Not a perfect solution, but at least those important shortcuts will continue working.

Reza Aghaei
  • 120,393
  • 18
  • 203
  • 398
  • Why is it not perfect? – Michael Haddad Feb 08 '19 at 10:57
  • 1
    For example when you hover on links, it doesn't show hand. – Reza Aghaei Feb 08 '19 at 10:58
  • 1
    Well, from my experience in StackOverflow, you are one of the best WinForms experts on this site (which probably means that you are one of the bests in the world). So I believe in your ability to help the WinForms community with this REALLY important question. :D – Michael Haddad Feb 08 '19 at 11:02
  • Yes, this is why I do not change the accepted answer. Jimi's answer works just fine for me. – Michael Haddad Feb 08 '19 at 11:05
  • 1
    No need to change at all, unless a perfect answer is shared. I also didn't meant to affect the other answer and the other answer has my vote as well. Just shared another alternative. Specially in case some one wants to have Alt+F4 working even when the mouse is over richtextbox. – Reza Aghaei Feb 08 '19 at 11:08
  • Maybe playing with what is passed (back and forth) when [`DefWndProc` is called](https://referencesource.microsoft.com/#System.Windows.Forms/winforms/Managed/System/WinForms/RichTextBox.cs,3669). It takes *some* testing, though. – Jimi Feb 08 '19 at 11:12
3

I'm assuming this is the WinForms' RichTextBox, because the WPF one doesn't have this problem.

The RichTextBox handles WM_SETCURSOR messages, to change the Cursor to Cursors.Hand if the Mouse Pointer ends up on a Link. A note from the developers:

RichTextBox uses the WM_SETCURSOR message over links to allow us to change the cursor to a hand. It does this through a synchronous notification message. So we have to pass the message to the DefWndProc first, and then, if we receive a notification message in the meantime (indicated by changing "LinkCursor", we set it to a hand. Otherwise, we call the WM_SETCURSOR implementation on Control to set it to the user's selection for the RichTextBox's cursor.

You could set the Capture when the Mouse enters the Control's bounds and then release it when the Mouse Pointer leaves the area. The capture needs to be released otherwise, when you first click on another control, the cursor will be set to RichTextBox instead:

private void richTextBox1_MouseMove(object sender, MouseEventArgs e)
{
    if (!richTextBox1.ClientRectangle.Contains(e.Location)) {
        richTextBox1.Capture = false;
    }
    else if (!richTextBox1.Capture) {
        richTextBox1.Capture = true;
    }
}
Jimi
  • 29,621
  • 8
  • 43
  • 61
  • Thanks! It works, until the user clicks on it, then it starts to flicker again. – Michael Haddad Feb 08 '19 at 09:39
  • Updated, using just the `MouseMove` event. – Jimi Feb 08 '19 at 09:49
  • 1
    I have noticed something. When I hover the mouse over the edges of the `RichTextBox`, the IBeam sometimes appear again for a fraction of a second. Perhaps it's negligible for some people, but it drives me crazy. :D I have tried to capture my screen to show it, but it is so fast that even with 60fps I don't manage to capture it... – Michael Haddad Feb 08 '19 at 10:28
  • I can't reproduce it, but it may depend on the `ClientRectangle` size. Try to enlarge it: `Rectangle rect = richTextBox1.ClientRectangle; rect.Inflate(4, 4); if (!rect.Contains(e.Location)) (...)` – Jimi Feb 08 '19 at 10:45
  • Didn't work... However, when I remove the condition and simply write `richTextBox1.Capture = true;` it works. Why do we actually need the condition? – Michael Haddad Feb 08 '19 at 10:53
  • 1
    The reason why you need to release the Capture is explained in the answer. See also what Reza Aghaei wrote. The Capture needs to be released when the Mouse pointer leaves the Control's area, otherwise it generates some mis-behaviours. This is also explained. If you decide to use a Custom Control, see also this: [How to justify text in a label](https://stackoverflow.com/a/47470191/7444103). Among the other things, there's a custom RichTextBox that supports Block Align (also known as Full justification). Maybe you have some use for it. – Jimi Feb 08 '19 at 11:01
  • Your comment helped me understand better what you meant in your answer. Thank you! – Michael Haddad Feb 08 '19 at 11:03
  • 1
    @Jimi My comment about capturing is, while you are release the capture when the mouse is outside the control, but when the mouse is over richtextbox, Alt+F4 will not work, because richtextbox has the capture. It's not about releasing capture when mouse leaves. – Reza Aghaei Feb 08 '19 at 11:15
  • 1
    @Reza Aghaei Yes, I know what you meant. My comment was about enforcing the need to release the capture, because the OP was apparently deciding to use just `richTextBox1.Capture = true;` in the event handler. – Jimi Feb 08 '19 at 11:18