C# 在Windows窗体应用程序中取消抗锯齿手动光标!

C# 在Windows窗体应用程序中取消抗锯齿手动光标!,c#,.net,winforms,cursor,C#,.net,Winforms,Cursor,好的,那么您知道在Windows Vista和Windows 7 MS中如何更改手动光标(当您将鼠标悬停在超链接上时显示的光标),并为其添加更多细节,使其抗锯齿、边缘平滑美观吗 那么,为什么在Windows窗体应用程序中不是这样呢 我讨厌看到一个看起来像是穴居人画的糟糕的指针,有没有办法通过编程告诉它显示系统中实际安装的指针?我在我的Windows目录中的游标文件夹中查找,而旧的指针甚至不在那里!那么为什么WinForms仍然使用旧版本呢?如何“升级”它?是的,WinForms控件仍然使用Win

好的,那么您知道在Windows Vista和Windows 7 MS中如何更改手动光标(当您将鼠标悬停在超链接上时显示的光标),并为其添加更多细节,使其抗锯齿、边缘平滑美观吗

那么,为什么在Windows窗体应用程序中不是这样呢


我讨厌看到一个看起来像是穴居人画的糟糕的指针,有没有办法通过编程告诉它显示系统中实际安装的指针?我在我的Windows目录中的游标文件夹中查找,而旧的指针甚至不在那里!那么为什么WinForms仍然使用旧版本呢?如何“升级”它?

是的,WinForms控件仍然使用Windows 98/2000附带的老式手动光标。它缺少Aero游标附带的抗锯齿效果。这是因为.NET Framework包含自己的硬编码游标,它使用该游标而不是系统默认游标。我认为这是因为早期版本的.NET是针对Windows 95这样的操作系统的,这些操作系统并没有与游标捆绑在一起,但还没有进行考古来证明这一点

幸运的是,强迫它使用正确的方法很容易。您只需告诉操作系统您希望它使用默认的手动光标,然后无论用户在哪个版本的Windows上运行您的程序,即使他们已从默认主题更改了鼠标光标,它都是正确的

最简单的方法是对现有控件进行子类化,覆盖以拦截,并告诉它使用system
IDC\u HAND
光标。你只需要一点P/Invoke魔法

下面的代码是使用
LinkLabel
控件显示的示例:

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

请原谅我复活了一条有一年历史的线

在与原始解决方案混为一谈并查看解决方案后,我“终于”找到了一种快速而干净的方法:

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;
            }
        }
    }
}
这个类实际上做了我们想要做的事情:它显示正确的系统指针而不闪烁,并且只在控件的LinkArea上这样做

这篇文章解决了其他文章的问题:

  • 它尊重链接位置,并在光标位于链接上时显示手
  • 它不会在鼠标移动时闪烁
您需要将光标更改为系统手动光标。为此,您需要处理
WM_SETCURSOR
并检查
OverrideCursor
是否为
Cursors。Hand
然后通过调用
SETCURSOR
将其更改为系统光标:

using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;
public class MyLinkLabel : LinkLabel
{
    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)]
    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);
        }
    }
}

很抱歉收回这篇旧文章,但我也有一些解决办法。 如果需要在不接触旧控件的情况下在应用程序范围内应用systemcursor,请在applicationstart中使用此选项:

    private static void TrySetCursorsDotHandToSystemHandCursor()
    {
        try
        {
            typeof(Cursors).GetField("hand", BindingFlags.Static | BindingFlags.NonPublic)
                           .SetValue(null, SystemHandCursor);
        }
        catch { }
    }

    [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, 32649 /*IDC_HAND*/));

在不创建新控件的情况下,我们需要更改控件的光标并创建自定义链接标签,否则它将无法工作 我们通过添加一个标签来创建自定义链接标签,更改字体下划线和前颜色,并添加一个单击事件

Private Const IDC_HAND As Integer = 32649

<DllImport("user32.dll", CharSet:=CharSet.Auto, SetLastError:=True)>
Private Shared Function LoadCursor(ByVal hInstance As IntPtr, ByVal lpCursorName As Integer) As IntPtr
End Function

Private Shared ReadOnly SystemHandCursor As Cursor = New Cursor(LoadCursor(IntPtr.Zero, IDC_HAND))
    
'add the cursor to custom linklabel
CustomLinkLabel1.Cursor = SystemHandCursor
Private Const IDC\u HAND为整数=32649
私有共享函数LoadCursor(ByVal hInstance作为IntPtr,ByVal lpCursorName作为Integer)作为IntPtr
端函数
私有共享只读SystemHandCursor作为游标=新游标(LoadCursor(IntPtr.Zero,IDC_HAND))
'将光标添加到自定义链接标签
CustomLinkLabel1.Cursor=SystemHandCursor
抱歉,只有vb.net代码,您可以使用联机转换器
编辑:缺少一些代码

Um。我是震惊的惊骇的困惑的释然的所以,我应该对我的应用程序中的每个控件进行子分类,我想为它们设置一个抗锯齿光标?这不是有点过分了吗顺便说一句,谢谢你的代码,它修复了它!:)@βӔḺṪẶⱫŌŔ:不。每隔一个光标就可以完美地工作。Windows应用程序很少需要显示手动光标。大约唯一使用的时间是
LinkLabel
。因此,您只需创建一次自定义替换
LinkLabel
控件,然后在应用程序中的任何地方使用它。是的,这就是我的意思。但我也有一个PictureBox(客户的徽标),点击后会将他们带到他们的网站,所以我也会显示手动光标@βӔḺṪẶⱫŌŔ:好的,您有两个自定义控件。
CustomLinkLabel
LinkPictureBox
。对我来说并不难。我已经为几乎所有的标准控件编写了增强功能,并在我的所有项目中反复使用它们。我建议不要每次鼠标移动时都创建新的光标。这似乎是不必要的内存泄漏。相反,我会创建一个静态字段,设置一个静态只读游标变量,然后在需要时引用它。真不敢相信我没看到。。。编辑代码以修复它。一个类似但更好的解决方案是设置游标以响应WM_SETCURSOR消息。这就是建议。当系统仅根据需要发送一条非常好的消息时,没有理由响应WM_MOUSEMOVE(OnMouseMove)。正如Hamido san所说,他的答案在控件的链接区域内正确工作,如果链接标签没有自动调整大小,也可以正确工作。WndProc解决方案无法通过这两个测试。非常好的最小代码解决方案。适用于Visual Studio中
Program.cs
文件中的新手。