草庐IT

关于 .net:WPF 中的自定义光标?

codeneng 2023-03-28 原文

Custom cursor in WPF?

我想在 WPF 应用程序中使用图像或图标作为自定义光标。我该怎么做?


你有两个基本的选择:

  • 当鼠标光标在您的控件上时,通过设置 this.Cursor = Cursors.None; 隐藏系统光标并使用您喜欢的任何技术绘制您自己的光标。然后,通过响应鼠标事件来更新光标的位置和外观。这里有两个例子:

    • http://www.xamlog.com/2006/07/17/creating-a-custom-cursor/
    • http://www.hanselman.com/blog/DeveloperDesigner.aspx
      可以在此处找到其他示例:

    • WPF 教程 - 如何使用自定义光标

    • 将光标设置为在拖动时呈现一些文本
    • 变得花哨并使用我们正在拖动的视觉反馈[而不是光标]
    • 如何在数据绑定的 ItemsControls 之间拖放项目?
  • 通过从 .cur 或 .ani 文件加载图像来创建新的 Cursor 对象。您可以在 Visual Studio 中创建和编辑这些类型的文件。还有一些免费的实用程序可以处理它们。基本上,它们是指定"热点"的图像(或动画图像),指示光标位于图像中的哪个点。

  • 如果您选择从文件加载,请注意您需要一个绝对文件系统路径才能使用 Cursor(string fileName) 构造函数。 Lamely,相对路径或 Pack URI 将不起作用。如果您需要从相对路径或与程序集一起打包的资源加载光标,则需要从文件中获取流并将其传递给 Cursor(Stream cursorStream) 构造函数。烦人但真实。

    另一方面,在使用 XAML 属性加载光标时将光标指定为相对路径确实有效,您可以使用这一事实将光标加载到隐藏控件上,然后复制引用以在另一个控件上使用。我没试过,但应该可以。

    • 另请注意,您可以从任何 WPF 内容动态构建光标。有关如何完成此操作的示例,请参阅 stackoverflow.com/questions/2835502/…。
    • 我在上一条评论中发布的链接涉及旋转现有光标。我刚刚发布了这个问题的新答案(见下文),它讲述了如何将任意 Visual 转换为 Cursor。


    就像 Peter 提到的,如果您已经有一个 .cur 文件,您可以通过在资源部分创建一个虚拟元素,然后在需要时引用虚拟的光标,将其用作嵌入式资源。

    例如,假设您想根据所选工具显示非标准光标。

    添加到资源:

    1
    2
    3
    4
    5
    6
    <Window.Resources>
        <ResourceDictionary>
            <TextBlock x:Key="CursorGrab" Cursor="Resources/Cursors/grab.cur"/>
            <TextBlock x:Key="CursorMagnify" Cursor="Resources/Cursors/magnify.cur"/>
        </ResourceDictionary>
    </Window.Resources>

    代码中引用的嵌入式游标示例:

    1
    2
    3
    4
    5
    6
    if (selectedTool =="Hand")
        myCanvas.Cursor = ((TextBlock)this.Resources["CursorGrab"]).Cursor;
    else if (selectedTool =="Magnify")
        myCanvas.Cursor = ((TextBlock)this.Resources["CursorMagnify"]).Cursor;
    else
        myCanvas.Cursor = Cursor.Arrow;

    • 您是否有任何理由使用 TextBlock 来缓存首先定义 Cursor 属性的 FrameworkElement 上的 Cursor 引用?
    • 没有理由; FrameworkElement 将是一个更好的选择。谢谢!


    有一种比自己管理光标显示或使用 Visual Studio 构建大量自定义光标更简单的方法。

    如果您有一个 FrameworkElement,您可以使用以下代码从中构造一个 Cursor:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    public Cursor ConvertToCursor(FrameworkElement visual, Point hotSpot)
    {
      int width = (int)visual.Width;
      int height = (int)visual.Height;

      // Render to a bitmap
      var bitmapSource = new RenderTargetBitmap(width, height, 96, 96, PixelFormats.Pbgra32);
      bitmapSource.Render(visual);

      // Convert to System.Drawing.Bitmap
      var pixels = new int[width*height];
      bitmapSource.CopyPixels(pixels, width, 0);
      var bitmap = new System.Drawing.Bitmap(width, height, System.Drawing.Imaging.PixelFormat.Format32bppPArgb);
      for(int y=0; y<height; y++)
        for(int x=0; x<width; x++)
          bitmap.SetPixel(x, y, Color.FromArgb(pixels[y*width+x]));

      // Save to .ico format
      var stream = new MemoryStream();
      System.Drawing.Icon.FromHandle(resultBitmap.GetHicon()).Save(stream);

      // Convert saved file into .cur format
      stream.Seek(2, SeekOrigin.Begin);
      stream.WriteByte(2);
      stream.Seek(10, SeekOrigin.Begin);
      stream.WriteByte((byte)(int)(hotSpot.X * width));
      stream.WriteByte((byte)(int)(hotSpot.Y * height));
      stream.Seek(0, SeekOrigin.Begin);

      // Construct Cursor
      return new Cursor(stream);
    }

    请注意,您的 FrameworkElement 的大小必须是标准光标大小(例如 16x16 或 32x32),例如:

    1
    2
    3
    <Grid x:Name="customCursor" Width="32" Height="32">
      ...
    </Grid>

    它会这样使用:

    1
    someControl.Cursor = ConvertToCursor(customCursor, new Point(0.5, 0.5));

    如果你有一个现有的图像,你的 FrameworkElement 显然可以是一个 <Image> 控件,或者你可以使用 WPF 的内置绘图工具绘制任何你喜欢的东西。

    请注意,有关 .cur 文件格式的详细信息可以在 ICO(文件格式)中找到。

    • 嘿,我尝试使用此代码片段来定义带有 xaml 的自定义光标。不幸的是,它什么都不显示,而不是我定义的 <Image /> 元素。调试代码我意识到 var pixels 数组在 CopyPixels() 方法运行后每个像素只包含 0。 CopyPixels() 方法的 stride 参数出现错误,因此我根据我发现的其他一些片段稍微更改了代码: int stride = width * ((bitmapSource.Format.BitsPerPixel + 7) / 8); 除了代码看起来与上面相同。 visual 是:<Image Height="32" Width="32"/>


    为了在 XAML 中使用自定义光标,我稍微修改了 Ben McIntosh 提供的代码:

    1
    2
    3
    <Window.Resources>    
     <Cursor x:Key="OpenHandCursor">Resources/openhand.cur</Cursor>
    </Window.Resources>

    要使用光标,只需引用资源:

    1
    <StackPanel Cursor="{StaticResource OpenHandCursor}" />

    • 使用游标资源而不是框架元素"dummy"更有意义


    如果有人正在寻找 UIElement 本身作为光标,我结合了 Ray 和 Arcturus 的解决方案:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
        public Cursor ConvertToCursor(UIElement control, Point hotSpot)
        {
            // convert FrameworkElement to PNG stream
            var pngStream = new MemoryStream();
            control.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
            Rect rect = new Rect(0, 0, control.DesiredSize.Width, control.DesiredSize.Height);
            RenderTargetBitmap rtb = new RenderTargetBitmap((int)control.DesiredSize.Width, (int)control.DesiredSize.Height, 96, 96, PixelFormats.Pbgra32);

            control.Arrange(rect);
            rtb.Render(control);

            PngBitmapEncoder png = new PngBitmapEncoder();
            png.Frames.Add(BitmapFrame.Create(rtb));
            png.Save(pngStream);

            // write cursor header info
            var cursorStream = new MemoryStream();
            cursorStream.Write(new byte[2] { 0x00, 0x00 }, 0, 2);                               // ICONDIR: Reserved. Must always be 0.
            cursorStream.Write(new byte[2] { 0x02, 0x00 }, 0, 2);                               // ICONDIR: Specifies image type: 1 for icon (.ICO) image, 2 for cursor (.CUR) image. Other values are invalid
            cursorStream.Write(new byte[2] { 0x01, 0x00 }, 0, 2);                               // ICONDIR: Specifies number of images in the file.
            cursorStream.Write(new byte[1] { (byte)control.DesiredSize.Width }, 0, 1);          // ICONDIRENTRY: Specifies image width in pixels. Can be any number between 0 and 255. Value 0 means image width is 256 pixels.
            cursorStream.Write(new byte[1] { (byte)control.DesiredSize.Height }, 0, 1);         // ICONDIRENTRY: Specifies image height in pixels. Can be any number between 0 and 255. Value 0 means image height is 256 pixels.
            cursorStream.Write(new byte[1] { 0x00 }, 0, 1);                                     // ICONDIRENTRY: Specifies number of colors in the color palette. Should be 0 if the image does not use a color palette.
            cursorStream.Write(new byte[1] { 0x00 }, 0, 1);                                     // ICONDIRENTRY: Reserved. Should be 0.
            cursorStream.Write(new byte[2] { (byte)hotSpot.X, 0x00 }, 0, 2);                    // ICONDIRENTRY: Specifies the horizontal coordinates of the hotspot in number of pixels from the left.
            cursorStream.Write(new byte[2] { (byte)hotSpot.Y, 0x00 }, 0, 2);                    // ICONDIRENTRY: Specifies the vertical coordinates of the hotspot in number of pixels from the top.
            cursorStream.Write(new byte[4] {                                                    // ICONDIRENTRY: Specifies the size of the image's data in bytes
                                              (byte)((pngStream.Length & 0x000000FF)),
                                              (byte)((pngStream.Length & 0x0000FF00) >> 0x08),
                                              (byte)((pngStream.Length & 0x00FF0000) >> 0x10),
                                              (byte)((pngStream.Length & 0xFF000000) >> 0x18)
                                           }, 0, 4);
            cursorStream.Write(new byte[4] {                                                    // ICONDIRENTRY: Specifies the offset of BMP or PNG data from the beginning of the ICO/CUR file
                                              (byte)0x16,
                                              (byte)0x00,
                                              (byte)0x00,
                                              (byte)0x00,
                                           }, 0, 4);

            // copy PNG stream to cursor stream
            pngStream.Seek(0, SeekOrigin.Begin);
            pngStream.CopyTo(cursorStream);

            // return cursor stream
            cursorStream.Seek(0, SeekOrigin.Begin);
            return new Cursor(cursorStream);
        }

    • 我会使用围绕您的流的语句来清理它,但除此之外,我对这种方法没有任何问题(与其他实现不同)。
    • 我注意到在控件上调用 Arrange 会导致 ListBoxItems 和 TreeViewItems 暂时消失,只是在导致其父级的布局发生更改(例如,扩展 TreeViewItem)后才重新出现。知道这是为什么吗?


    一种非常简单的方法是在 Visual Studio 中将光标创建为 .cur 文件,然后将其添加到项目资源中。

    然后在要分配光标时添加以下代码:

    1
    myCanvas.Cursor = new Cursor(new System.IO.MemoryStream(myNamespace.Properties.Resources.Cursor1));

    另一种解决方案有点类似于 Ray 的解决方案,但不是缓慢而繁琐的像素复制,它使用了一些 Windows 内部结构:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    private struct IconInfo {
      public bool fIcon;
      public int xHotspot;
      public int yHotspot;
      public IntPtr hbmMask;
      public IntPtr hbmColor;
    }

    [DllImport("user32.dll")]
    private static extern IntPtr CreateIconIndirect(ref IconInfo icon);

    [DllImport("user32.dll")]
    [return: MarshalAs(UnmanagedType.Bool)]
    private static extern bool GetIconInfo(IntPtr hIcon, ref IconInfo pIconInfo);

    public Cursor ConvertToCursor(FrameworkElement cursor, Point HotSpot) {
      cursor.Arrange(new Rect(new Size(cursor.Width, cursor.Height)));
      var bitmap = new RenderTargetBitmap((int)cursor.Width, (int)cursor.Height, 96, 96, PixelFormats.Pbgra32);
      bitmap.Render(cursor);

      var info = new IconInfo();
      GetIconInfo(bitmap.ToBitmap().GetHicon(), ref info);
      info.fIcon = false;
      info.xHotspot = (byte)(HotSpot.X * cursor.Width);
      info.yHotspot = (byte)(HotSpot.Y * cursor.Height);

      return CursorInteropHelper.Create(new SafeFileHandle(CreateIconIndirect(ref info), true));
    }

    中间有一个扩展方法,我更喜欢在扩展类中使用这种情况:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    using DW = System.Drawing;

    public static DW.Bitmap ToBitmap(this BitmapSource bitmapSource) {
      var bitmap = new DW.Bitmap(bitmapSource.PixelWidth, bitmapSource.PixelHeight, DW.Imaging.PixelFormat.Format32bppPArgb);
      var data = bitmap.LockBits(new DW.Rectangle(DW.Point.Empty, bitmap.Size), DW.Imaging.ImageLockMode.WriteOnly, DW.Imaging.PixelFormat.Format32bppPArgb);
      bitmapSource.CopyPixels(Int32Rect.Empty, data.Scan0, data.Height * data.Stride, data.Stride);
      bitmap.UnlockBits(data);
      return bitmap;
    }

    有了这一切,它就相当简单明了。

    而且,如果您碰巧不需要指定自己的热点,您甚至可以将其缩短(您也不需要结构或 P/Invokes):

    1
    2
    3
    4
    5
    6
    7
    public Cursor ConvertToCursor(FrameworkElement cursor, Point HotSpot) {
      cursor.Arrange(new Rect(new Size(cursor.Width, cursor.Height)));
      var bitmap = new RenderTargetBitmap((int)cursor.Width, (int)cursor.Height, 96, 96, PixelFormats.Pbgra32);
      bitmap.Render(cursor);
      var icon = System.Drawing.Icon.FromHandle(bitmap.ToBitmap().GetHicon());
      return CursorInteropHelper.Create(new SafeFileHandle(icon.Handle, true));
    }

    • 这个效果很好(从我想要的任何 WPF 视觉创建一个 Cursor 真是太棒了),但是,每当关联对象被销毁时,我都会在由此方法创建的 Cursor 的 dtor 中不断收到 SEH 异常。唯一不能得到它的方法是创建一个单例游标并在任何地方重用它。您知道的任何原因都会导致 SEH 异常?我可以猜一整天,但似乎用于为光标创建图像的对象被丢弃了,并且 Cursor 类炸毁了它的 b/c。
    • 很好的例子,但有一个错误,即 info.yHotspot = (byte)(HotSpot.X * cursor.Height);(应该是 HotSpot.Y,而不是 HotSpot.X)。此示例还通过按源位图尺寸对其进行缩放来更改原始热点代码的范围,因此在指定偏移量时请记住这一点。


    我想从项目资源中加载自定义光标文件,但遇到了类似问题。我在互联网上搜索了一个解决方案,但没有找到我需要的:在运行时将 this.Cursor 设置为存储在我的项目的资源文件夹中的自定义光标。
    我试过 Ben 的 xaml 解决方案,但觉得它不够优雅。
    彼得艾伦说:

    Lamely, a relative path or Pack URI will not work. If you need to load the cursor from a relative path or from a resource packed with your assembly, you will need to get a stream from the file and pass it in to the Cursor(Stream cursorStream) constructor. Annoying but true.

    我偶然发现了一个很好的方法来解决我的问题:

    1
    2
    3
    4
    5
        System.Windows.Resources.StreamResourceInfo info =
            Application.GetResourceStream(new
            Uri("/MainApp;component/Resources/HandDown.cur", UriKind.Relative));

        this.Cursor = new System.Windows.Input.Cursor(info.Stream);

    MainApp 应替换为您的应用程序的名称。 Resources 应替换为项目中 *.cur 文件的相对文件夹路径。

    • "MainApp" 应替换为您的应用程序的名称。 "Resources" 应替换为项目中 *.cur 文件的相对文件夹路径。


    你可以通过像

    这样的代码来做到这一点

    1
    this.Cursor = new Cursor(@"<your address of icon>");

    你可以试试这个

    1
    <Window Cursor=""C:\\WINDOWS\\Cursors\\dinosaur.ani"" />

    它可能在 Visual Studio 2017 中发生了变化,但我能够将 .cur 文件作为嵌入式资源引用:

    1
    2
    3
    <Setter
        Property="Cursor"
        Value="/assembly-name;component/location-name/curser-name.cur" />

    如果你使用的是visual studio,你可以

  • 新建一个游标文件
  • 复制/粘贴图像
  • 将其保存到 .cur 文件。

  • 确保所有 GDI 资源(例如 bmp.GetHIcon)都被释放。否则你最终会出现内存泄漏。以下代码(图标的扩展方法)非常适用于 WPF。它会在右下角创建一个带有小图标的箭头光标。

    备注:此代码使用图标来创建光标。它不使用当前的 UI 控件。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
        public static Cursor CreateCursor(this Icon icon, bool includeCrossHair, System.Drawing.Color crossHairColor)
        {
            if (icon == null)
                return Cursors.Arrow;

            // create an empty image
            int width = icon.Width;
            int height = icon.Height;

            using (var cursor = new Bitmap(width * 2, height * 2))
            {
                // create a graphics context, so that we can draw our own cursor
                using (var gr = System.Drawing.Graphics.FromImage(cursor))
                {
                    // a cursor is usually 32x32 pixel so we need our icon in the lower right part of it
                    gr.DrawIcon(icon, new Rectangle(width, height, width, height));

                    if (includeCrossHair)
                    {
                        using (var pen = new System.Drawing.Pen(crossHairColor))
                        {
                            // draw the cross-hair
                            gr.DrawLine(pen, width - 3, height, width + 3, height);
                            gr.DrawLine(pen, width, height - 3, width, height + 3);
                        }
                    }
                }

                try
                {
                    using (var stream = new MemoryStream())
                    {
                        // Save to .ico format
                        var ptr = cursor.GetHicon();
                        var tempIcon = Icon.FromHandle(ptr);
                        tempIcon.Save(stream);

                        int x = cursor.Width/2;
                        int y = cursor.Height/2;

                        #region Convert saved stream into .cur format

                        // set as .cur file format
                        stream.Seek(2, SeekOrigin.Begin);
                        stream.WriteByte(2);

                        // write the hotspot information
                        stream.Seek(10, SeekOrigin.Begin);
                        stream.WriteByte((byte)(width));
                        stream.Seek(12, SeekOrigin.Begin);
                        stream.WriteByte((byte)(height));
                       
                        // reset to initial position
                        stream.Seek(0, SeekOrigin.Begin);

                        #endregion


                        DestroyIcon(tempIcon.Handle);  // destroy GDI resource

                        return new Cursor(stream);
                    }
                }
                catch (Exception)
                {
                    return Cursors.Arrow;
                }
            }
        }

        /// <summary>
        /// Destroys the icon.
        /// </summary>
        /// <param name="handle">The handle.</param>
        /// <returns></returns>
        [DllImport("user32.dll", CharSet = CharSet.Auto)]
        public extern static Boolean DestroyIcon(IntPtr handle);

    还可以查看 Scott Hanselman 的 BabySmash (www.codeplex.com/babysmash)。他使用了一种更"蛮力"的方法来隐藏窗口光标并在画布上显示他的新光标,然后将光标移动到"真正的"光标应该是

    在这里阅读更多:
    http://www.hanselman.com/blog/DeveloperDesigner.aspx


    这将使用附加属性将存储在项目中的任何图像转换为光标。图片必须编译为资源!

    例子

    1
    <Button MyLibrary:FrameworkElementExtensions.Cursor=""{MyLibrary:Uri MyAssembly, MyImageFolder/MyImage.png}""/>

    FrameworkElementExtensions

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    using System;
    using System.Windows;
    using System.Windows.Media;

    public static class FrameworkElementExtensions
    {
        #region Cursor

        public static readonly DependencyProperty CursorProperty = DependencyProperty.RegisterAttached("Cursor", typeof(Uri), typeof(FrameworkElementExtensions), new UIPropertyMetadata(default(Uri), OnCursorChanged));
        public static Uri GetCursor(FrameworkElement i) => (Uri)i.GetValue(CursorProperty);
        public static void SetCursor(FrameworkElement i, Uri input) => i.SetValue(CursorProperty, input);
        static void OnCursorChanged(object sender, DependencyPropertyChangedEventArgs e)
        {
            if (sender is FrameworkElement frameworkElement)
            {
                if (GetCursor(frameworkElement) != null)
                    frameworkElement.Cursor = new ImageSourceConverter().ConvertFromString(((Uri)e.NewValue).OriginalString).As<ImageSource>().Bitmap().Cursor(0, 0).Convert();
            }
        }

        #endregion
    }

    ImageSourceExtensions

    1
    2
    3
    4
    5
    6
    7
    8
    using System.Drawing;
    using System.Windows.Media;
    using System.Windows.Media.Imaging;

    public static class ImageSourceExtensions
    {
        public static Bitmap Bitmap(this ImageSource input) => input.As<BitmapSource>().Bitmap();
    }

    BitmapSourceExtensions

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    using System.IO;
    using System.Windows.Media.Imaging;

    public static class BitmapSourceExtensions
    {
        public static System.Drawing.Bitmap Bitmap(this BitmapSource input)
        {
            if (input == null)
                return null;

            System.Drawing.Bitmap result;
            using (var outStream = new MemoryStream())
            {
                var encoder = new PngBitmapEncoder();
                encoder.Frames.Add(BitmapFrame.Create(input));
                encoder.Save(outStream);
                result = new System.Drawing.Bitmap(outStream);
            }
            return result;
        }
    }

    位图扩展

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    using System;
    using System.Drawing;
    using System.Runtime.InteropServices;

    public static class BitmapExtensions
    {

        [StructLayout(LayoutKind.Sequential)]
        public struct ICONINFO
        {
            /// <summary>
            /// Specifies whether this structure defines an icon or a cursor. A value of TRUE specifies an icon; FALSE specifies a cursor.
            /// </summary>
            public bool fIcon;

            /// <summary>
            /// Specifies the x-coordinate of a cursor's hot spot. If this structure defines an icon, the hot spot is always in the center of the icon, and this member is ignored.
            /// </summary>
            public Int32 xHotspot;

            /// <summary>
            /// Specifies the y-coordinate of the cursor's hot spot. If this structure defines an icon, the hot spot is always in the center of the icon, and this member is ignored.
            /// </summary>
            public Int32 yHotspot;

            /// <summary>
            /// (HBITMAP) Specifies the icon bitmask bitmap. If this structure defines a black and white icon, this bitmask is formatted so that the upper half is the icon AND bitmask and the lower half is the icon XOR bitmask. Under this condition, the height should be an even multiple of two. If this structure defines a color icon, this mask only defines the AND bitmask of the icon.
            /// </summary>
            public IntPtr hbmMask;

            /// <summary>
            /// (HBITMAP) Handle to the icon color bitmap. This member can be optional if this structure defines a black and white icon. The AND bitmask of hbmMask is applied with the SRCAND flag to the destination; subsequently, the color bitmap is applied (using XOR) to the destination by using the SRCINVERT flag.
            /// </summary>
            public IntPtr hbmColor;
        }

        [DllImport("user32.dll")]
        static extern IntPtr CreateIconIndirect([In] ref ICONINFO piconinfo);

        [DllImport("user32.dll")]
        static extern bool GetIconInfo(IntPtr hIcon, out ICONINFO piconinfo);

        [DllImport("user32.dll", SetLastError = true)]
        public static extern bool DestroyIcon(IntPtr hIcon);

        public static System.Windows.Forms.Cursor Cursor(this Bitmap input, int hotX, int hotY)
        {
            ICONINFO Info = new ICONINFO();
            IntPtr Handle = input.GetHicon();
            GetIconInfo(Handle, out Info);

            Info.xHotspot = hotX;
            Info.yHotspot = hotY;
            Info.fIcon = false;

            IntPtr h = CreateIconIndirect(ref Info);
            return new System.Windows.Forms.Cursor(h);
        }
    }

    光标扩展

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    using Microsoft.Win32.SafeHandles;

    public static class CursorExtensions
    {
        public static System.Windows.Input.Cursor Convert(this System.Windows.Forms.Cursor Cursor)
        {
            SafeFileHandle h = new SafeFileHandle(Cursor.Handle, false);
            return System.Windows.Interop.CursorInteropHelper.Create(h);
        }
    }

    作为

    1
    public static Type As<Type>(this object input) => input is Type ? (Type)input : default;

    乌里

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    using System;
    using System.Windows.Markup;

    public class Uri : MarkupExtension
    {
        public string Assembly { get; set; } = null;

        public string RelativePath { get; set; }

        public Uri(string relativePath) : base()
        {
            RelativePath = relativePath;
        }

        public Uri(string assembly, string relativePath) : this(relativePath)
        {
            Assembly = assembly;
        }

        static Uri Get(string assemblyName, string relativePath) => new Uri($"pack://application:,,,/{assemblyName};component/{relativePath}", UriKind.Absolute);

        public override object ProvideValue(IServiceProvider serviceProvider)
        {
            if (Assembly == null)
                return new System.Uri(RelativePath, UriKind.Relative);

            return Get(Assembly, RelativePath);
        }
    }

    有关关于 .net:WPF 中的自定义光标?的更多相关文章

    1. ruby - 如何从 ruby​​ 中的字符串运行任意对象方法? - 2

      总的来说,我对ruby​​还比较陌生,我正在为我正在创建的对象编写一些rspec测试用例。许多测试用例都非常基础,我只是想确保正确填充和返回值。我想知道是否有办法使用循环结构来执行此操作。不必为我要测试的每个方法都设置一个assertEquals。例如:describeitem,"TestingtheItem"doit"willhaveanullvaluetostart"doitem=Item.new#HereIcoulddotheitem.name.shouldbe_nil#thenIcoulddoitem.category.shouldbe_nilendend但我想要一些方法来使用

    2. ruby - 其他文件中的 Rake 任务 - 2

      我试图在一个项目中使用rake,如果我把所有东西都放到Rakefile中,它会很大并且很难读取/找到东西,所以我试着将每个命名空间放在lib/rake中它自己的文件中,我添加了这个到我的rake文件的顶部:Dir['#{File.dirname(__FILE__)}/lib/rake/*.rake'].map{|f|requiref}它加载文件没问题,但没有任务。我现在只有一个.rake文件作为测试,名为“servers.rake”,它看起来像这样:namespace:serverdotask:testdoputs"test"endend所以当我运行rakeserver:testid时

    3. ruby-on-rails - Ruby net/ldap 模块中的内存泄漏 - 2

      作为我的Rails应用程序的一部分,我编写了一个小导入程序,它从我们的LDAP系统中吸取数据并将其塞入一个用户表中。不幸的是,与LDAP相关的代码在遍历我们的32K用户时泄漏了大量内存,我一直无法弄清楚如何解决这个问题。这个问题似乎在某种程度上与LDAP库有关,因为当我删除对LDAP内容的调用时,内存使用情况会很好地稳定下来。此外,不断增加的对象是Net::BER::BerIdentifiedString和Net::BER::BerIdentifiedArray,它们都是LDAP库的一部分。当我运行导入时,内存使用量最终达到超过1GB的峰值。如果问题存在,我需要找到一些方法来更正我的代

    4. ruby - Facter::Util::Uptime:Module 的未定义方法 get_uptime (NoMethodError) - 2

      我正在尝试设置一个puppet节点,但ruby​​gems似乎不正常。如果我通过它自己的二进制文件(/usr/lib/ruby/gems/1.8/gems/facter-1.5.8/bin/facter)在cli上运行facter,它工作正常,但如果我通过由ruby​​gems(/usr/bin/facter)安装的二进制文件,它抛出:/usr/lib/ruby/1.8/facter/uptime.rb:11:undefinedmethod`get_uptime'forFacter::Util::Uptime:Module(NoMethodError)from/usr/lib/ruby

    5. ruby-on-rails - Rails 3 中的多个路由文件 - 2

      Rails2.3可以选择随时使用RouteSet#add_configuration_file添加更多路由。是否可以在Rails3项目中做同样的事情? 最佳答案 在config/application.rb中:config.paths.config.routes在Rails3.2(也可能是Rails3.1)中,使用:config.paths["config/routes"] 关于ruby-on-rails-Rails3中的多个路由文件,我们在StackOverflow上找到一个类似的问题

    6. ruby-on-rails - Rails - 一个 View 中的多个模型 - 2

      我需要从一个View访问多个模型。以前,我的links_controller仅用于提供以不同方式排序的链接资源。现在我想包括一个部分(我假设)显示按分数排序的顶级用户(@users=User.all.sort_by(&:score))我知道我可以将此代码插入每个链接操作并从View访问它,但这似乎不是“ruby方式”,我将需要在不久的将来访问更多模型。这可能会变得很脏,是否有针对这种情况的任何技术?注意事项:我认为我的应用程序正朝着单一格式和动态页面内容的方向发展,本质上是一个典型的网络应用程序。我知道before_filter但考虑到我希望应用程序进入的方向,这似乎很麻烦。最终从任何

    7. ruby-on-rails - Rails 3.2.1 中 ActionMailer 中的未定义方法 'default_content_type=' - 2

      我在我的项目中添加了一个系统来重置用户密码并通过电子邮件将密码发送给他,以防他忘记密码。昨天它运行良好(当我实现它时)。当我今天尝试启动服务器时,出现以下错误。=>BootingWEBrick=>Rails3.2.1applicationstartingindevelopmentonhttp://0.0.0.0:3000=>Callwith-dtodetach=>Ctrl-CtoshutdownserverExiting/Users/vinayshenoy/.rvm/gems/ruby-1.9.3-p0/gems/actionmailer-3.2.1/lib/action_mailer

    8. ruby-on-rails - Rails 应用程序中的 Rails : How are you using application_controller. rb 是新手吗? - 2

      刚入门rails,开始慢慢理解。有人可以解释或给我一些关于在application_controller中编码的好处或时间和原因的想法吗?有哪些用例。您如何为Rails应用程序使用应用程序Controller?我不想在那里放太多代码,因为据我了解,每个请求都会调用此Controller。这是真的? 最佳答案 ApplicationController实际上是您应用程序中的每个其他Controller都将从中继承的类(尽管这不是强制性的)。我同意不要用太多代码弄乱它并保持干净整洁的态度,尽管在某些情况下ApplicationContr

    9. ruby - 如何模拟 Net::HTTP::Post? - 2

      是的,我知道最好使用webmock,但我想知道如何在RSpec中模拟此方法:defmethod_to_testurl=URI.parseurireq=Net::HTTP::Post.newurl.pathres=Net::HTTP.start(url.host,url.port)do|http|http.requestreq,foo:1endresend这是RSpec:let(:uri){'http://example.com'}specify'HTTPcall'dohttp=mock:httpNet::HTTP.stub!(:start).and_yieldhttphttp.shou

    10. ruby-on-rails - form_for 中不在模型中的自定义字段 - 2

      我想向我的Controller传递一个参数,它是一个简单的复选框,但我不知道如何在模型的form_for中引入它,这是我的观点:{:id=>'go_finance'}do|f|%>Transferirde:para:Entrada:"input",:placeholder=>"Quantofoiganho?"%>Saída:"output",:placeholder=>"Quantofoigasto?"%>Nota:我想做一个额外的复选框,但我该怎么做,模型中没有一个对象,而是一个要检查的对象,以便在Controller中创建一个ifelse,如果没有检查,请帮助我,非常感谢,谢谢

    随机推荐