通常在實作系統的時候,為了配合不同的需求,可能會需要自訂游標的形狀,雖然WPF中有Cursors類別可以用,但是提供的游標仍然有限,所以用圖片來當作游標,或是用XAML寫出來的UserControl來當作游標,似乎就成為不錯的選擇。
這次就來介紹怎麼在WPF中覆寫原來的系統游標,以自訂的游標取代。
通常在實作系統的時候,為了配合不同的需求,可能會需要自訂游標的形狀,雖然WPF中有Cursors類別可以用,但是提供的游標仍然有限,所以用圖片來當作游標,或是用XAML寫出來的UserControl來當作游標,似乎就成為不錯的選擇。
這次就來介紹怎麼在WPF中覆寫原來的系統游標,以自訂的游標取代。
首先,我們需要實作一個CursorHelper類別,用來覆寫原來的系統游標,而這個類別會使用到System.Drawing類別庫,所以別忘記引用這個類別庫喔~
接著就是建立CursorHelper.cs啦(這邊就不詳述了,直接提供原始碼供各位服用)~
namespace Wpf_CustomCursor
{
using System;
using System.IO;
using System.Runtime.InteropServices;
using System.Security.Permissions;
using System.Windows;
using System.Windows.Input;
using System.Windows.Interop;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using Microsoft.Win32.SafeHandles;
public class CursorHelper
{
private static class NativeMethods
{
//參考 http://msdn.microsoft.com/en-us/library/ms648052(v=vs.85).aspx ,做出一樣的Struct,用來複寫系統的游標。
public struct IconInfo
{
public bool fIcon;
public int xHotspot;
public int yHotspot;
public IntPtr hbmMask;
public IntPtr hbmColor;
}
[DllImport( "user32.dll" )]
public static extern SafeIconHandle CreateIconIndirect( ref IconInfo icon );
[DllImport( "user32.dll" )]
public static extern bool DestroyIcon( IntPtr hIcon );
[DllImport( "user32.dll" )]
[return: MarshalAs( UnmanagedType.Bool )]
public static extern bool GetIconInfo( IntPtr hIcon , ref IconInfo pIconInfo );
}
[SecurityPermission( SecurityAction.LinkDemand , UnmanagedCode = true )]
private class SafeIconHandle : SafeHandleZeroOrMinusOneIsInvalid
{
public SafeIconHandle()
: base( true )
{
}
override protected bool ReleaseHandle()
{
return NativeMethods.DestroyIcon( handle );
}
}
/// <summary>
/// 透過Bitmap建立圖形游標
/// </summary>
/// <param name="bitmap">要當成游標的Bitmap</param>
/// <param name="xHotSpot">游標頂點的X軸位移</param>
/// <param name="yHotSpot">游標頂點的Y軸位移</param>
/// <returns>自訂的游標物件</returns>
private static Cursor InternalCreateCursor( System.Drawing.Bitmap bitmap , int xHotSpot , int yHotSpot )
{
var iconInfo = new NativeMethods.IconInfo();
NativeMethods.GetIconInfo( bitmap.GetHicon() , ref iconInfo );
iconInfo.xHotspot = xHotSpot;
iconInfo.yHotspot = yHotSpot;
iconInfo.fIcon = false;
SafeIconHandle cursorHandle = NativeMethods.CreateIconIndirect( ref iconInfo );
return CursorInteropHelper.Create( cursorHandle );
}
/// <summary>
/// 用來以UIElement建立自定游標
/// </summary>
/// <param name="element">要當成游標的UIElement</param>
/// <param name="xHotSpot">游標頂點的X軸位移</param>
/// <param name="yHotSpot">游標頂點的Y軸位移</param>
/// <returns>自訂的游標物件</returns>
public static Cursor CreateCursor( UIElement element , int xHotSpot = 0 , int yHotSpot = 0 )
{
element.Measure( new Size( double.PositiveInfinity , double.PositiveInfinity ) );
element.Arrange( new Rect( new Point() , element.DesiredSize ) );
RenderTargetBitmap renderTargetBitmap = new RenderTargetBitmap(
( int ) element.DesiredSize.Width , ( int ) element.DesiredSize.Height ,
96 , 96 , PixelFormats.Pbgra32 );
renderTargetBitmap.Render( element );
var encoder = new PngBitmapEncoder();
encoder.Frames.Add( BitmapFrame.Create( renderTargetBitmap ) );
using( var memoryStream = new MemoryStream() )
{
encoder.Save( memoryStream );
using( var bitmap = new System.Drawing.Bitmap( memoryStream ) )
{
return InternalCreateCursor( bitmap , xHotSpot , yHotSpot );
}
}
}
}
}
原理是透過Window的API,將UIElement轉為Bitmap去覆寫作業系統內建的游標 (但是因為會轉成Bitmap,所以如果XAML中套用有動畫效果的話,動畫將不會起作用)。
用法也相當的簡單,只要呼叫CursorHelper.CreateCursor,傳入UIElement(可以直接像範例一樣實作自己的UserControl當做游標,並依照需求給予游標頂點的位移值就行啦~
我自己寫了一個小範例,大概長得像下面的樣子:
![]() |
![]() |
![]() |
不過這次的範例如果要放上來讓大家體驗的話,必需要將專案發佈而且設定為Full Trust Application才行,所以就只先提供原始碼讓有興趣的朋友們自行體驗啦!!~
範例原始碼在此,請自行服用: