改进 Link 标签 - 使用系统手形光标并更改 Link 颜色

Improve LinkLabel - Use system hand Cursor and Change Link Color

LinkLabel 控件有一些烦人的问题:

我找到了以下声称可以解决光标问题的答案 here

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

但是,这个解决方案并不完美。例如,旧的、丑陋的光标在悬停在其上时会在显示正确的光标之前闪烁一帧。

我也在ComCtl32.dll中看到原生的SysLink控件没有问题,但是我找不到好的在 C#/WinForms 中使用它的解决方案。但是无论如何我更喜欢纯 .NET 解决方案。

如何通过解决上述问题使LinkLabel控制更好?

关于颜色,该控件具有允许您更改 link 颜色的属性:LinkColorActiveLinkColorVisitedLinkColorDisabledLinkColor .

这些属性的默认值来自存储在 HKEY_CURRENT_USER\Software\Microsoft\Internet Explorer\Settings 注册表项中的 Internet Explorer 设置。

要使用不同的颜色,您可以根据自己的喜好设置这些属性。例如,您可以将 LinkColor 设置为 SystemColors.HotTrack 或遵循 w3org 的颜色建议,并使用 #0000EE 作为默认 link 颜色和 #551A8B对于已访问的 link 和 #FF0000 对于活跃的 links.

关于 blinking,这是因为您共享的代码是在基础 class 更改了光标后在鼠标移动时设置光标。因此,在设置新游标之前有可能 blinking 基础 class 游标。解决问题需要处理WM_SETCURSOR,必要时将光标设置为系统手形光标。

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