Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Un-Antialiased Hand Cursor in Windows Forms Apps!

Okay, so you know how in Windows Vista and Windows 7 MS changed the Hand Cursor (the one that shows up when you hover over a hyperlink), and added more detail to it so it's antialiased and nice and smooth around the edges?

Well, why isn't it like that in Windows Forms apps?

I'm sick off looking at a crappy hand cursor that looks like it was drawn by a caveman.

Is there a way to programmatically tell it to display the one that's actually installed in the system? I looked in the Cursors folder in my Windows directory, and the old hand cursor isn't even there! So why is WinForms still using the old one? How can I 'upgrade' it?

like image 892
βӔḺṪẶⱫŌŔ Avatar asked May 16 '11 11:05

βӔḺṪẶⱫŌŔ


2 Answers

Yes, the WinForms controls still use the old-school hand cursor, as shipped with Windows 98/2000. It lacks the anti-aliasing effects that the one included with the Aero cursors does. This is because the .NET Framework includes its own hard-coded cursor, which it uses instead of the system default. I presume this is because early versions of .NET were targeting operating systems like Windows 95 that didn't come bundled with this cursor, but haven't done the archaeology to prove it.

Fortunately, it's easy enough to force it to use the right one. You just have to tell the operating system you want it to use the default hand cursor, and then it will be correct no matter what version of Windows the user runs your program on, and even if they've changed their mouse cursors from the default theme.

The simplest way of doing that is to subclass the existing control, override the WndProc function to intercept the WM_SETCURSOR message, and tell it to use the system IDC_HAND cursor. You just need a little bit of P/Invoke magic.

The following code is an example of how that might look using the LinkLabel control:

public class LinkLabelEx : LinkLabel
{
    private const int WM_SETCURSOR = 0x0020;
    private const int IDC_HAND = 32649;

    [DllImport("user32.dll", CharSet=CharSet.Auto, SetLastError=true)]
    private static extern IntPtr LoadCursor(IntPtr hInstance, int lpCursorName);

    [DllImport("user32.dll", CharSet=CharSet.Auto)]
    private static extern IntPtr SetCursor(IntPtr hCursor);

    protected override void WndProc(ref Message m)
    {
        if (m.Msg == WM_SETCURSOR)
        {
            // Set the cursor to use the system hand cursor
            SetCursor(LoadCursor(IntPtr.Zero, IDC_HAND));

            // Indicate that the message has been handled
            m.Result = IntPtr.Zero;
            return;
        }

        base.WndProc(ref m);
    }
}
like image 190
Cody Gray Avatar answered Sep 18 '22 02:09

Cody Gray


Excuse me for resurrecting a year-old thread!!!

After messing around with the original solution and taking a look at the reflected LinkLabel source code, I "finally" found a quick yet clean way of doing it :

using System.Runtime.InteropServices;

namespace System.Windows.Forms {
    public class LinkLabelEx : LinkLabel {
        private const int IDC_HAND = 32649;

        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern IntPtr LoadCursor(IntPtr hInstance, int lpCursorName);

        private static readonly Cursor SystemHandCursor = new Cursor(LoadCursor(IntPtr.Zero, IDC_HAND));

        protected override void OnMouseMove(MouseEventArgs e) {
            base.OnMouseMove(e);

            // If the base class decided to show the ugly hand cursor
            if(OverrideCursor == Cursors.Hand) {
                // Show the system hand cursor instead
                OverrideCursor = SystemHandCursor;
            }
        }
    }
}

This class actually does what we want: It shows the proper system hand cursor without flickering and does this only on the LinkArea of the control.

like image 33
Hamid Sadeghian Avatar answered Sep 18 '22 02:09

Hamid Sadeghian