LinkLabel
control has some annoying problems:
Color.Blue
instead of SystemColors.HotTrack
for the LinkColor
property)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?
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);
}
}
}