Custom cursor in WPF?
我想在 WPF 应用程序中使用图像或图标作为自定义光标。我该怎么做?
你有两个基本的选择:
当鼠标光标在您的控件上时,通过设置
- 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 中创建和编辑这些类型的文件。还有一些免费的实用程序可以处理它们。基本上,它们是指定”热点”的图像(或动画图像),指示光标位于图像中的哪个点。
如果您选择从文件加载,请注意您需要一个绝对文件系统路径才能使用
另一方面,在使用 XAML 属性加载光标时将光标指定为相对路径确实有效,您可以使用这一事实将光标加载到隐藏控件上,然后复制引用以在另一个控件上使用。我没试过,但应该可以。
就像 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; |
有一种比自己管理光标显示或使用 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 // Convert to System.Drawing.Bitmap // Save to .ico format // Convert saved file into .cur format // Construct Cursor |
请注意,您的 FrameworkElement 的大小必须是标准光标大小(例如 16×16 或 32×32),例如:
1
2 3 |
<Grid x:Name="customCursor" Width="32" Height="32">
… </Grid> |
它会这样使用:
1
|
someControl.Cursor = ConvertToCursor(customCursor, new Point(0.5, 0.5));
|
如果你有一个现有的图像,你的 FrameworkElement 显然可以是一个
请注意,有关 .cur 文件格式的详细信息可以在 ICO(文件格式)中找到。
为了在 XAML 中使用自定义光标,我稍微修改了 Ben McIntosh 提供的代码:
1
2 3 |
<Window.Resources>
<Cursor x:Key="OpenHandCursor">Resources/openhand.cur</Cursor> </Window.Resources> |
要使用光标,只需引用资源:
1
|
<StackPanel Cursor="{StaticResource OpenHandCursor}" />
|
如果有人正在寻找 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); PngBitmapEncoder png = new PngBitmapEncoder(); // write cursor header info // copy PNG stream to cursor stream // return cursor stream |
一种非常简单的方法是在 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")] [DllImport("user32.dll")] public Cursor ConvertToCursor(FrameworkElement cursor, Point HotSpot) { var info = new IconInfo(); 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) { |
有了这一切,它就相当简单明了。
而且,如果您碰巧不需要指定自己的热点,您甚至可以将其缩短(您也不需要结构或 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)); } |
我想从项目资源中加载自定义光标文件,但遇到了类似问题。我在互联网上搜索了一个解决方案,但没有找到我需要的:在运行时将
我试过 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); |
你可以通过像
这样的代码来做到这一点
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,你可以
确保所有 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 using (var cursor = new Bitmap(width * 2, height * 2)) if (includeCrossHair) try int x = cursor.Width/2; #region Convert saved stream into .cur format // set as .cur file format // write the hotspot information #endregion DestroyIcon(tempIcon.Handle); // destroy GDI resource return new Cursor(stream); /// <summary> |
还可以查看 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 public static readonly DependencyProperty CursorProperty = DependencyProperty.RegisterAttached("Cursor", typeof(Uri), typeof(FrameworkElementExtensions), new UIPropertyMetadata(default(Uri), OnCursorChanged)); #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 |
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 System.Drawing.Bitmap 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)] /// <summary> /// <summary> /// <summary> /// <summary> [DllImport("user32.dll")] [DllImport("user32.dll")] [DllImport("user32.dll", SetLastError = true)] public static System.Windows.Forms.Cursor Cursor(this Bitmap input, int hotX, int hotY) Info.xHotspot = hotX; IntPtr h = CreateIconIndirect(ref Info); |
光标扩展
1
2 3 4 5 6 7 8 9 10 |
using Microsoft.Win32.SafeHandles;
public static class CursorExtensions |
作为
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 RelativePath { get; set; } public Uri(string relativePath) : base() public Uri(string assembly, string relativePath) : this(relativePath) static Uri Get(string assemblyName, string relativePath) => new Uri($"pack://application:,,,/{assemblyName};component/{relativePath}", UriKind.Absolute); public override object ProvideValue(IServiceProvider serviceProvider) return Get(Assembly, RelativePath); |
原创文章,作者:ItWorker,如若转载,请注明出处:https://blog.ytso.com/269735.html