2

LinkLabel control has some annoying problems:

  • By default, it doesn't use any system colors (namely Color.Blue instead of SystemColors.HotTrack for the LinkColor property)
  • It uses the old, ugly, aliased version of the hand cursor

I have found the following answer here which claims to fix the cursor issue:

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;
            }
        }
    }
}

However, this solution is not perfect. For example, the old, ugly cursor flashes for one frame before the correct cursor is displayed when hovered over it.

I have also read about the native SysLink control in ComCtl32.dll which doesn't have there problems, but I can't find a good solution to use it in C#/WinForms. However I would prefer a pure .NET solution anyway.

How can I make the LinkLabel control better by solving above mentioned problems?

4

1 回答 1

3

About the color, the control has come properties which allows you to change the link color: LinkColor, ActiveLinkColor, VisitedLinkColor and DisabledLinkColor.

The default values for those properties are coming from Internet Explorer settings which are stored in HKEY_CURRENT_USER\Software\Microsoft\Internet Explorer\Settings registry key.

To use different colors, you can set those properties based on your preference. For example you can set LinkColor to SystemColors.HotTrack or follow the recommendations by w3org for the colors and use #0000EE as default link color and #551A8B for visited link and #FF0000 for active links.

About the blinking, it's because the code which you shared is setting the cursor on mouse move after the base class has changed the cursor. As a result, there is a chance for blinking the base class cursor before setting the new cursor. To solve the problem, you need to handle WM_SETCURSOR and set the cursor to system hand cursor when it's necessary.

using System;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Windows.Forms;
public class MyLinkLabel : LinkLabel
{
    public MyLinkLabel()
    {
        this.LinkColor = Color.FromArgb(0x00, 0x66, 0xCC);
        this.VisitedLinkColor = Color.FromArgb(0x80, 0x00, 0x80);
        this.ActiveLinkColor = Color.FromArgb(0xFF, 0x00, 0x00);
    }
    const int IDC_HAND = 32649;
    const int WM_SETCURSOR = 0x0020;
    const int HTCLIENT = 1;
    [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    static extern IntPtr LoadCursor(IntPtr hInstance, int lpCursorName);
    [DllImport("user32.dll", ExactSpelling = true, CharSet = CharSet.Auto)]
    public static extern IntPtr SetCursor(HandleRef hcursor);

    static readonly Cursor SystemHandCursor = 
        new Cursor(LoadCursor(IntPtr.Zero, IDC_HAND));
    protected override void WndProc(ref Message msg)
    {
        if (msg.Msg == WM_SETCURSOR)
            WmSetCursor(ref msg);
        else
            base.WndProc(ref msg);
    }
    void WmSetCursor(ref Message m)
    {
        if (m.WParam == (IsHandleCreated ? Handle : IntPtr.Zero) &&
           (unchecked((int)(long)m.LParam) & 0xffff) == HTCLIENT)
        {
            if (OverrideCursor != null)
            {
                if (OverrideCursor == Cursors.Hand)
                    SetCursor(new HandleRef(SystemHandCursor, SystemHandCursor.Handle));
                else
                    SetCursor(new HandleRef(OverrideCursor, OverrideCursor.Handle));
            }
            else
            {
                SetCursor(new HandleRef(Cursor, Cursor.Handle));
            }
        }
        else
        {
            DefWndProc(ref m);
        }
    }
}
于 2019-01-13T03:01:23.753 回答