如何自定义光标文件并将其添加到项目中?

How to Customize and add Cursor Files to a project?

快速提问:

我在使用 Wpf 应用程序,我使用此例程在鼠标悬停在图像上时更改光标形状:

private void mainGrid_MouseEnter(object sender, MouseEventArgs e)
{
    mainImage.Cursor = Cursors.Hand;
}

private void mainGrid_MouseLeave(object sender, MouseEventArgs e)
{
    mainImage.Cursor = Cursors.Arrow;
}

输出:

我怎样才能得到下面的形状?

我希望这可以简单地工作:

foreach (var finger in fingers.Skip(2)) { finger.Extend(); }

但是不行..编程不是那样的..

我想要的形状在游标中不可用:Cursors Class

正如其他人所提到的,您需要为此形状自定义光标。

我对这段代码并不特别自豪,但我设法让自己陷入困境,需要在项目中没有任何新资源的情况下这样做,所以这里有一个独立的自定义此特定游标的游标创建者。

您可以将它创建的 HandCursor 分配给您的表单,就像任何其他光标一样,例如:

public Example()
{
    PictureBox box = new PictureBox();
    box.Bounds = new Rectangle(10, 10, 100, 100);
    box.Cursor = HandCursor;
    box.MouseDown += Box_MouseDown;
    box.MouseUp += Box_MouseUp;
    box.BorderStyle = BorderStyle.Fixed3D;
    Controls.Add(box);
}

void Box_MouseUp(object sender, MouseEventArgs e)
{
    ((Control)sender).Cursor = HandCursor;
}

void Box_MouseDown(object sender, MouseEventArgs e)
{
    ((Control)sender).Cursor = HandGrabCursor;
}

这里是代码的核心:

Cursor m_HandCursor = null;
Cursor HandCursor
{
    get
    {
        if (m_HandCursor == null)
        {
            m_HandCursor = CursorFromString(
                "AAACAAEAICAAABAAFACoEAAAFgAAACgAAAAgAAAAQ{A5}EAI{A62}aBgM3GgYD/xoGA/8a" +
                "BgP/GgYD/xoGA/8aBgP/GgYD/xoGA/8aBgO6{A70}GgYDNxoGA73Ny8r/39/f/93d3f/a2" +
                "tr/2NjY/9bW1v/V1dX/09PT/xoGA94{A6a}BoGAzcaBgO909HR/+Xl5f/j4+P/4eHh/97e" +
                "3v/c3Nz/2tra/9jY2P/W1tb/GgYD3hoGAz{A61}aBgM3GgYDvdnX1//s7Oz/6urq/+fn5/" +
                "/l5eX/4+Pj/+Dg4P/e3t7/3Nzc/9ra2v8aBgOHGgYDhw{A5f}BoGA73f3dz/8/Pz//Hx8f" +
                "/u7u7/7Ozs/+np6f/n5+f/5OTk/+Li4v/g4OD/3d3d/7Kurf8aBgPV{A5b}aBgNjGgYDtP" +
                "n5+f/IxMP/9fX1//Ly8v/w8PD/7u7u/+vr6//p6en/5ubm/+Tk5P/i4uL/39/f/xoGA94a" +
                "BgMw{A50}GgYDNhoGA7rm5OT//Pz8/4N5d//4+Pj/9vb2//T09P/y8vL/8PDw/+3t7f/r6" +
                "+v/6Ojo/+bm5v/j4+P/GgYDhxoGA4c{A50}aBgO66Obl{/7}+/v7/GgYD//v7+//6+vr/+" +
                "Pj4//b29v/09PT/8vLy/+/v7//t7e3/6urq/+jo6P+6trX/GgYD1Q{A4f}BoGA//o5uX/6" +
                "Obl/xoGA7oaBgP//v7+//39/f/7+/v/+fn5//j4+P/19fX/8/Pz//Hx8f/v7+//7Ozs/+r" +
                "q6v8aBgP/{A50}GgYDuhoGA/8aBgO6GgYDNhoGA{/d}7+/v/8/Pz/+/v7//n5+f/39/f/9" +
                "fX1//Pz8//x8fH/7u7u/xoGA/8{A65}GgYD{/17}9/f3//Pz8//r6+v/5+fn/GgYD//X19" +
                "f/y8vL/GgYD/w{A65}aBgP{/c}Pysr{/b}8aBgP//f39//z8/P8aBgP/+Pj4//b29v8aBg" +
                "P/{A60}GgYDMDgmJP{/b}4Z7ev{/b}xoGA{/8}v7+/xoGA//7+/v/+vr6/xoGA/8{A60}a" +
                "BgOHhnt6{/c}QC8t{/c}GgYD{/c}GgYD//7+/v/9/f3/GgYD/w{A5f}BoGA9XPysr{/6}8" +
                "/Kyv8aBgP{/b}8aBgP{/b}8aBgP{/6}8/Kyv8aBgO6{A60}GgYD{/c}GgYDhxoGA{/c}xo" +
                "GA{/c}xoGA//Pysr/GgYDuhoGAxo{A60}aBgP{/b}8aBgOHGgYD{/c}GgYD{/7}Pysr/Gg" +
                "YD/xoGA7oaBgMa{A65}BoGA7r{/a}xoGA4caBgP{/b}8aBgP/GgYD/xoGA7oaBgM2{A70}" +
                "GgYDNhoGA7oaBgO6GgYDNhoGA7r{/a}xoGA7o{A95}GgYDNhoGA7oaBgO6GgYDNg{A84a}" +
                "//AD///gA///wAH//4AB//+AAf//AAD//gAA//4AAP/+AAD//gAA///gAP//4AD//8AA//" +
                "/AAP//wAD//8AA///AAf//wAf//8A////8P{/41}8=");
        }

        return m_HandCursor;
    }
}

Cursor m_HandGrabCursor = null;
Cursor HandGrabCursor
{
    get
    {
        if (m_HandGrabCursor == null)
        {
            m_HandGrabCursor = CursorFromString(
                "AAACAAEAICAAABAAFACoEAAAFgAAACgAAAAgAAAAQ{A5}EAI{A62}aBgM3GgYD/xoGA/8a" +
                "BgP/GgYD/xoGA/8aBgP/GgYD/xoGA/8aBgO6{A70}GgYDNxoGA73Pzc3/4eHh/97e3v/b2" +
                "9v/2dnZ/9bW1v/U1NT/0tLS/xoGA94{A6a}BoGAzcaBgO919XV/+rq6v/n5+f/4+Pj/+Dg" +
                "4P/d3d3/29vb/9jY2P/W1tb/GgYD3hoGAz{A61}aBgM3GgYDvd/d3P/y8vL/7+/v/+zs7P" +
                "/p6en/5ubm/+Pj4//g4OD/3d3d/9ra2v8aBgOHGgYDhw{A5f}BoGA73l4+P/+vr6//f39/" +
                "/19fX/8vLy/+/v7//r6+v/6Ojo/+Xl5f/i4uL/39/f/7Ovrv8aBgPV{A5b}aBgNjGgYDtP" +
                "{/5}Nycj/+/v7//n5+f/39/f/9PT0//Hx8f/u7u7/6+vr/+fn5//k5OT/4eHh/xoGA94aB" +
                "gMw{A50}GgYDNhoGA7ro5uX{/6}4Z7ev/+/v7//f39//v7+//5+fn/9vb2//Pz8//w8PD/" +
                "7e3t/+rq6v/n5+f/GgYDhxoGA4c{A50}aBgO66Obl{/c}GgYD{/d}v7+//z8/P/6+vr/+P" +
                "j4//X19f/y8vL/7+/v/+zs7P+9ubj/GgYD1Q{A4f}BoGA//o5uX/6Obl/xoGA7oaBgP{/1" +
                "7}7+/v/8/Pz/+vr6//f39//19fX/8vLy/+/v7/8aBgP/{A50}GgYDuhoGA/8aBgO6GgYDN" +
                "hoGA{/22}9/f3/+/v7//n5+f/39/f/9PT0/xoGA/8{A65}GgYD{/27}+/v7/GgYD//v7+/" +
                "/5+fn/GgYD/w{A65}aBgP{/b}8aBgP{/b}8aBgP{/b}8aBgP//v7+/8zIx/8aBgO6{A65}" +
                "BoGA{/c}xoGA{/c}xoGA{/c}xoGA//Pysr/GgYDuhoGAxo{A65}GgYD/8/Kyv{/6}GgYD{" +
                "/c}GgYD{/7}Pysr/GgYD/xoGA7oaBgMa{A6b}aBgM2GgYDuhoGA/8aBgO6{/a}8aBgO6Gg" +
                "YD/xoGA7oaBgM2{A85}BoGAzYaBgO6GgYDuhoGAzY{Aaf5}//AD///gA///wAH//4AB//+" +
                "AAf//AAD//gAA//4AAP/+AAD//gAA///gAP//4AD//+AA///gAf//4Af///w{/57}8=");
        }

        return m_HandGrabCursor;
    }
}

Cursor CursorFromString(string data)
{
    byte[] bits = Convert.FromBase64String(
                Regex.Replace(data,
                    "\{(.)([0-9a-f]+)\}",
                    delegate(Match m)
                    {
                        return new string(
                            m.Groups[1].Value[0],
                            int.Parse(m.Groups[2].Value,
                                System.Globalization.NumberStyles.HexNumber));
                    }
                )
            );

    bits[2] = 1;

    using (MemoryStream stream = new MemoryStream(bits))
    {
        return new Cursor(stream);
#if false
        // Version for Windows Forms
        using (Icon icon = new Icon(stream))
        {
            WinAPI.ICONINFO info = new WinAPI.ICONINFO();
            WinAPI.GetIconInfo(icon.Handle, out info);

            info.fIcon = false;
            info.xHotspot = bits[10];
            info.yHotspot = bits[12];

            IntPtr hCursor = WinAPI.CreateIconIndirect(ref info);

            Cursor ret = new Cursor(hCursor);

            return ret;
        }
#endif
    }
}

static class WinAPI
{
    [DllImport("user32.dll")]
    public static extern bool GetIconInfo(IntPtr hIcon, out ICONINFO piconinfo);
    [DllImport("user32.dll")]
    public static extern IntPtr CreateIconIndirect(ref ICONINFO piconinfo);

    [StructLayout(LayoutKind.Sequential)]
    public struct ICONINFO
    {
        public bool fIcon;
        public Int32 xHotspot;
        public Int32 yHotspot;
        public IntPtr hbmMask;
        public IntPtr hbmColor;
    }
}

为了获得这样的光标,您需要获得(或创建)自定义光标。 Windows 中没有这样的游标。可以使用 Visual Studio 的内置 Image Editor for Icons.

创建您自己的光标

创建您自己的游标

  1. 首先打开 Add New Item 对话框。


  1. 然后向下滚动并 select Cursor File,给它一个你选择的名字。


  1. 它现在将在所谓的 图标图像编辑器 中打开您的新光标文件。现在您可以使用铅笔或任何其他不同的绘图工具开始绘图(也可以粘贴图像,但正如您所见,光标最初具有 1 位颜色格式 - 这意味着只有两种颜色:黑色和白色。这可以通过添加新的图像类型进行更改)。


  1. 现在我们必须为游标指定一个hot spot。热点是图标中的位置,Windows 用来跟踪指针的实际位置(为简单起见,您可以将其称为光标的点击点)。正常的 Windows 7 Aero Arrow 在 (0, 0) - 左上角有热点。

    要指定光标的热点,我们必须使用 Set Hot Spot Tool。然后单击要用作光标图标中的热点的特定像素。对于这个游标,我选择了 (9, 8).

    设置热点在(9, 8):


  1. 保存所有内容,然后转到 Solution Explorer,右键单击您的项目并按 Properties。然后转到 Resources 选项卡并单击 Add ResourceAdd Existing File...

    现在找到您的项目文件夹 select 并打开光标文件。


  1. 最后的非代码步骤是 select 您的光标在 Solution Explorer 中,转到 Properties 窗格并将 Build Action 设置为None。这是为了防止它被添加到编译的可执行文件中两次,因为它已经作为资源添加了。

    重要提示: 不要 对位于 Resources 文件夹中的光标文件执行此操作!


在您的应用程序中使用自定义光标

现在上代码,其实很简单。由于您的光标现在已作为字节数组资源添加,您只需将其加载到 MemoryStream 中,然后将该内存流传递到 Cursor class 的构造函数中。为了简单和可读性,我把这段代码放在另一个 class.

public static class CursorHelper
{
    public static Cursor FromByteArray(byte[] array)
    {
        using (MemoryStream memoryStream = new MemoryStream(array))
        {
            return new Cursor(memoryStream);
        }
    }
}

现在您可以继续在 form/control/etc 中声明光标在 class 级。然后您就可以使用它了!

private Cursor OpenHand = CursorHelper.FromByteArray(Properties.Resources.CursorOpenHand);

public MainWindow()
{
    InitializeComponent();
    this.Cursor = OpenHand;
}

用法示例

我根据您的规格做了一个例子;当您按住鼠标时,一只正常张开的手会变成一只抓握的手:

private Cursor OpenHand = CursorHelper.FromByteArray(Properties.Resources.CursorOpenHand);
private Cursor GrabbingHand = CursorHelper.FromByteArray(Properties.Resources.CursorGrabbingHand);

public MainWindow()
{
    InitializeComponent();
    this.Cursor = OpenHand;
    this.MouseDown += this.MainWindow_MouseDown;
    this.MouseUp += this.MainWindow_MouseUp;
}

private void MainWindow_MouseDown(object sender, MouseEventArgs e)
{
    ((Control)sender).Cursor = GrabbingHand;
}

private void MainWindow_MouseUp(object sender, MouseEventArgs e)
{
    ((Control)sender).Cursor = OpenHand;
}

盈利!


以下是我使用的游标:

您可以将游标编码为 base-64 字符串,然后使用 Convert.FromBase64String:

将该 base-64 字符串转回游标
byte[] HandGrabCursor = Convert.FromBase64String("AAACAAEAICAAAAkACAAwAQAAFgAAACgAAAAgAAAAQAAAAAEAAQAAAAAAgAAAAAAAAAAAAAAAAgAAAAIAAAAAAAAA////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD8AAAA/AAAAfwAAAP+AAAH/gAAB/8AAAH/AAAB/wAAA/0AAANsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//////////////////////////////////////////////////////////////////////////////////////gH///4B///8Af//+AD///AA///wAH//+AB///wAf//4AH//+AD///yT/////////////////////////////8=");

using MemoryStream stream = new MemoryStream(HandGrabCursor);
Cursor = new Cursor(stream);