【问题标题】:Un-Antialiased Hand Cursor in Windows Forms AppsWindows 窗体应用程序中的非抗锯齿手形光标
【发布时间】:2011-05-16 11:58:43
【问题描述】:

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

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

看到一个看起来像是穴居人画的蹩脚手形光标,我感到恶心。

有没有办法以编程方式告诉它显示系统中实际安装的那个?我查看了 Windows 目录中的 Cursors 文件夹,旧的手形光标甚至都不存在!那么为什么 WinForms 还在使用旧的呢?如何“升级”它?

【问题讨论】:

    标签: c# .net winforms mouse-cursor


    【解决方案1】:

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

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

    最简单的方法是对现有控件进行子类化,覆盖WndProc function 以拦截WM_SETCURSOR message,并告诉它使用系统IDC_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);
        }
    }
    

    【讨论】:

    • 嗯。我感到震惊。吓坏了。使困惑。松了一口气。那么,我应该对我的应用程序中想要为其设置抗锯齿光标的每个控件进行子类化?是不是有点矫枉过正了? - 顺便说一句,感谢代码,它修复了它! :)
    • @βӔḺṪẶⱫŌŔ:不。其他所有光标都可以正常工作。 Windows 应用程序需要显示手形光标的情况非常。唯一一次使用的是LinkLabel。所以你只需要创建一个自定义的替换LinkLabel控件一次,然后在你的应用中任何你想要的地方使用它。
    • 是的,这就是我的意思。但我也有一个 PictureBox(客户的徽标),点击后会将他们带到他们的网站,所以我也显示了手形光标
    • @βӔḺṪẶⱫŌŔ:好的,所以你有两个自定义控件。一个CustomLinkLabel 和一个LinkPictureBox。对我来说似乎并不难。我已经为几乎所有标准控件编写了增强功能,并在我的所有项目中一遍又一遍地重复使用它们。
    【解决方案2】:

    对不起,我复活了一个岁的线程!!!

    在弄乱了原始解决方案并查看了reflected LinkLabel source code 之后,我“终于”找到了一种快速而干净的方法:

    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 消息。这就是my answer 的建议。当有一个非常好的消息专门用于此目的并且系统只会在需要时发送时,没有理由这样做来响应 WM_MOUSEMOVE (OnMouseMove)。
    • 正如 Hamido-san 所说,他的答案在控件的 LinkArea 中可以正常工作,如果 LinkLabel 不是 AutoSized,它也可以正常工作。 WndProc 解决方案未能通过这两项测试。
    【解决方案3】:

    这个帖子解决了其他帖子的问题:

    • 它尊重链接位置并在光标位于链接上时显示手
    • 鼠标移动时不闪烁

    您需要将光标更改为系统手形光标。为此,您需要处理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);
            }
        }
    }
    

    【讨论】:

      【解决方案4】:

      很抱歉让这个旧帖子回来,但我也有一些解决方案。 如果您需要在不触及旧控件的情况下应用系统游标应用程序,请在 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*/));
      

      【讨论】:

      • 伟大的最小代码解决方案。对于 Visual Studio 中 Program.cs 文件中的新手。
      【解决方案5】:

      在不创建新控件的情况下这样做,我们需要更改控件的光标并创建自定义链接标签,否则它将不起作用 我们通过添加标签来更改字体下划线并更改其前景色并添加点击事件来创建自定义链接标签

      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
      

      抱歉只有 vb .net 代码,您可能会使用在线转换器 编辑:缺少一些代码

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-05-16
        • 1970-01-01
        • 1970-01-01
        • 2014-07-14
        相关资源
        最近更新 更多