在C# Windows应用程序中使用自定义颜色光标

7

我正在开发一款SDG(单显示组件)应用程序,为此我需要在单个窗口中使用多个光标(至少需要不同颜色的光标)。我了解到,使用C#只能使用黑色和白色光标,这并不能解决我的问题。


彩色光标工作正常。你是怎么发现只能使用黑白光标的? - Tim Robinson
Windows会允许你拥有超过1个光标吗? - H H
我曾经需要动态地创建光标。这个过程中出现了一些奇怪的问题,特别是半透明度会与黑色混合,使得光标太暗。最终,在SO社区的帮助下,我解决了这个问题,整个解决方案在这里展示:https://dev59.com/z0jSa4cB1Zd3GeqPIMNs - Pedery
@Tim - 我在一个 C# 论坛上读到,使用默认的 Cursor 类,你只能使用黑白光标(甚至不能使用灰度光标)。 - Himanshu
@Henk - 我正在使用一个名为SDGToolkit(来自卡尔加里大学)的C# API,它可以处理从键盘、鼠标甚至是平板电脑获取多个输入的所有底层操作。 - Himanshu
5个回答

15

Cursor类设计得相当糟糕。由于某种神秘的原因,它使用了一个遗留的COM接口(IPicture),该接口不支持彩色和动画光标。但是可以通过一些相当丑陋的操作来解决这个问题:

using System;
using System.ComponentModel;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.Reflection;

static class NativeMethods {
    public static Cursor LoadCustomCursor(string path) {
        IntPtr hCurs = LoadCursorFromFile(path);
        if (hCurs == IntPtr.Zero) throw new Win32Exception();
        var curs = new Cursor(hCurs);
        // Note: force the cursor to own the handle so it gets released properly
        var fi = typeof(Cursor).GetField("ownHandle", BindingFlags.NonPublic | BindingFlags.Instance);
        fi.SetValue(curs, true);
        return curs;
    }
    [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
    private static extern IntPtr LoadCursorFromFile(string path);
}

使用示例:

this.Cursor = NativeMethods.LoadCustomCursor(@"c:\windows\cursors\aero_busy.ani");

谢谢Hans,最近你一直是我的英雄。我只想补充一下,如果有人从Control构造函数内部加载具有路径目录的文件,就像"aero_busy.ani"(假设.ani文件存在于根应用程序目录中),那么这将使Designer停止工作。因此,在我的情况下,我返回了nullptr而不是抛出Win32Exception,然后仅在Cursornullptr不同的情况下更改它。 - Simple

4

这个帖子相当老了,但是它在谷歌上排名靠前,所以这里提供VS 2019的答案:

someControl.Cursor = new Cursor(Properties.Resources.somePNG.GetHicon());

你应该将带有透明度的'somePNG.png'添加为项目资源。

希望这能帮助到2020年的某些人。


3
我尝试了一些不同的方法,似乎可以使用不同颜色的光标,但是这段代码的唯一问题在于鼠标光标的热点坐标不准确,即它们会向右移动一点。但是这可以通过在代码中考虑偏移来修复。
代码如下:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.IO;
using System.Runtime.InteropServices;

namespace MID
{    
    public partial class CustomCursor : Form
    {
        [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
        public static extern IntPtr LoadCursorFromFile(string filename);

        public CustomCursor()
        {
            InitializeComponent();

            Bitmap bmp = (Bitmap)Bitmap.FromFile("Path of the cursor file saved as .bmp");
            bmp.MakeTransparent(Color.Black);
            IntPtr ptr1 = blue.GetHicon();

            Cursor cur = new Cursor(ptr1);
            this.Cursor = cur;

        }
    }
}

1
您可以像这样动态地从文件加载光标:

var myCursor = new Cursor("myCursor.cur");

在加载后,您可以像这样设置任何控件的光标:

myControl.Cursor = myCursor;

光标还可以接受流作为构造函数参数。这意味着您可以从嵌入在应用程序中的资源加载,而不是从文件系统中加载。

Windows 不允许您拥有多个光标,但您可以在控件上绘制多个光标。您可以使用光标对象的 Draw 方法,如下所示:

myCursor.Draw(g, new Rectangle(...));

如果您正在使用TCP/IP在客户端之间发送光标数据,则这应该足以工作。

然而,有一些应用程序支持在单个PC上进行多个输入。 (例如,Rag Doll Kung Fu)对于此,您需要查看.NET框架不支持的内容。

您可能需要研究一些USB调用的PInvoke。(我在这方面没有太多经验,所以无法详细说明。)


谢谢John,但我实际上已经尝试过这个方法,但它对我没有用。对于我的Windows应用程序中的多个鼠标控件,我基本上使用了一个名为SDGToolkit的C# API来处理所有底层操作。 - Himanshu

0
唯一的问题是热点将位于文件的中心。所以要么:
将文件扩大两倍宽度和高度,并将图标放置在右下象限。
或者
使用这个复杂的代码来调整热点: 在WinForms / .NET中更改光标热点

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接