Con trỏ là một thành phần quan trọng trong Window nó cần cho tất cả các ứng dụng, hỗ trợ người dùng thao tác với Hệ điều hành, những lợi ích mà nó mang lại là điều không thể chối cãi, nh
Trang 1Collaborators
Tham gia ngày: Jun 2010
Bài gửi: 23 Cấp độ: 6 Điểm: 299
WPF Tutorial - Sử dụng Custom Cursor
WPF Tutorial - Sử dụng Custom Cursor
Các bạn có thể xem bài viết về sử dụng Custom Cursor trong WinForm ở đây Bài hướng dẫn này cũng có nhiều vấn đề tương tự, cho nên mình nghĩ là bạn nên xem qua nó trước
Thật sự là tạo và sử dụng một con trỏ trong WPF khó khăn hơn rất nhiều so với trong
WinForm Điều này là do một vấn đề duy nhất - đó là WPF không dùng GDI, nhưng con trỏ thì
lại dùng Con trỏ là một thành phần quan trọng trong Window (nó cần cho tất cả các ứng dụng,
hỗ trợ người dùng thao tác với Hệ điều hành), những lợi ích mà nó mang lại là điều không thể chối cãi, nhưng nó vẫn là đối tượng GDI Cho nên ta cần phải vượt qua ranh giới này để có thể
làm gì đó đặc biệt với con trỏ trong WPF.
WPF vẫn hỗ trợ những cursor chuẩn, nhưng khi bạn muốn tạo một hình vẽ gì đấy cho cursor,
phải làm nhiều việc đấy
Đầu tiên, bạn nhớ rằng, nếu muốn tạo một con trỏ trong WinForm, bạn phải dùng một số code
như sau:
PHP Code:
IntPtr curPtr;
/* CurPtr gets set to
a pointer to an icon */
Cursor myCur = new Cursor(curPtr);
Nhưng trong WPF, nó rất khác Trong WinForm, đối tượng Cursor là
System.Windows.Forms.Cursor, còn trong WPF, nó là System.Windows.Input.Cursor, và
Constructor của class này ko hỗ trợ đối số nào là IntPtr cả Nhưng cũng có một cách tương tự
như trên:
PHP Code:
IntPtr ptr;
/* CurPtr gets set to
a pointer to an icon */
SafeFileHandle handle = new SafeFileHandle(ptr, true);
Cursor myCur = System.Windows.Interop.CursorInteropHelper.Create(handle);
Chúng ta sẽ convert một đối tượng IntPtr sang một đối tượng SafeHandle (SafeFileHandle kế thừa từ SafeHandle - bạn không thể tạo một đối tượng SafeHandle, vì nó là một abstract class)
Sau đó bạn khởi tạo Cursor từ phương thức
System.Windows.Interop.CursorInteropHelper.Create với đối số là đối tượng SafeHandle đó Chúng ta liên kết code các phần trên, sẽ có một lớp như sau:
Trang 2PHP Code:
using System;
using System.Windows.Interop;
using Microsoft.Win32.SafeHandles;
using System.Runtime.InteropServices;
using System.Windows.Input;
namespace WPFCursorTest
{
public class CursorHelper
{
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 static Cursor CreateCursor(System.Drawing.Bitmap bmp, int xHotSpot,
int yHotSpot)
{
IconInfo tmp = new IconInfo();
GetIconInfo(bmp.GetHicon(), ref tmp);
tmp.xHotspot = xHotSpot;
tmp.yHotspot = yHotSpot;
tmp.fIcon = false;
IntPtr ptr = CreateIconIndirect(ref tmp);
SafeFileHandle handle = new SafeFileHandle(ptr, true);
return CursorInteropHelper.Create(handle);
}
}
}
Bạn cần lưu ý rằng chúng ta đang sử dụng một đối tượng System.Drawing.Bitmap ở đây (bạn
nên Add Reference thư viện System.Drawing để có thể sử dụng nó) Đây là một đối tượng
Bitmap dạng GDI cũ - một khái niệm nằm ngoài các lớp WPF Mặc định WPF không sử dụng
thư viện System.Drawing cho nên bạn cần phải Add chúng vào (Project/ Add Reference ).
Mình đã thử, khá khó, để tìm ra một cách tạo một con trỏ trong WPF mà không cần phải dựa vào
System.Drawing.Bitmap Mình cần phải nhận được một con trỏ (Pointer chứ không phải
Cursor) Icon trỏ ra ngoài bitmap (như hàm GetHicon), nhưng đối tượng bitmap trong WPF lại
không thể tạo ra một Pointer nào cả
Trang 3Nhưng ko phải đã hết hi vọng! Hãy thử với một overload của phương thức CreateCursor dưới đây xem:
PHP Code:
public static Cursor CreateCursor(UIElement element, int xHotSpot, int yHotSpot)
{
element.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
element.Arrange(new Rect(0 0 element.DesiredSize.Width,
element.DesiredSize.Height));
RenderTargetBitmap rtb = new RenderTargetBitmap((int)element.DesiredSize.Width,
(int)element.DesiredSize.Height, 96, 96, PixelFormats.Pbgra32);
rtb.Render(element);
PngBitmapEncoder encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(rtb));
MemoryStream ms = new MemoryStream();
encoder.Save(ms);
System.Drawing.Bitmap bmp = new System.Drawing.Bitmap(ms);
ms.Close();
ms.Dispose();
Cursor cur = InternalCreateCursor(bmp, xHotSpot, yHotSpot);
bmp.Dispose();
return cur;
}
Ở đây, mình dùng một UIElement - cốt lõi của cấu trúc WPF Mình đo đạc và sắp xếp nó để
chắc chắn nó được trả về những giá trị đúng, và sau đó mình "tạo một bức ảnh từ nó" Mình thực hiện bằng cách tạo một đối tượng RenderTargetBitmap Đây là một lớp đối tượng trong WPF
cho phép bạn lấy một đối tượng UIElement và vẽ nó lên một bitmap - nhưng nhớ là, đây là
bitmap của WPF (chính xác là một đối tượng
System.Windows.Media.Imaging.RenderTargetBitmap) Nó không giống như đối tượng
System.Drawing.Bitmap Mình hiệu chỉnh kích thước cho nó, sau đó render UIElement trên bitmap đó
Bây giờ mình có một đối tượng RenderTargetBitmap trong tay, mình muốn tạo một đối tượng System.Drawing.Bitmap Microsoft hoàn toàn có khả năng làm điều này dễ dàng hơn (nhưng họ không làm), và mình thực sự khó chịu về nó Có một vài phương pháp cho phép bạn chuyển từ đối tượng System.Drawing.Bitmap sang đối tượng bitmap của WPF, nhưng ngược lại thì hoàn toàn không Ít nhất là mình chưa thể tìm ra Nếu có bạn nào biết những phương thức tương tự như vậy (hoặc tốt hơn cách mình đã miêu tả), hãy chia sẻ^_^
Mình dùng một Encoder, và mã hóa bitmap (RenderTargetBitmap) như một đối tượng PNG
(bạn có thể mã hóa như thế nào tùy thích) Sau đó, mình tạo một MemoryStream và lưu mã của bitmap (đã được mã hóa) vào trong stream đó Tiếp theo mình tạo một đối tượng
System.Drawing.Bitmap với Contructor có tham số là một MemoryStream Nên nhớ phải giải phóng các đối tượng không dùng đến nữa
Toàn bộ code chúng ta cần ở đây:
PHP Code:
using System;
using System.Windows.Interop;
using Microsoft.Win32.SafeHandles;
using System.Runtime.InteropServices;
using System.Windows.Input;
using System.Windows.Media;
Trang 4using System.Windows.Media.Imaging;
using System.IO;
using System.Windows;
namespace WPFCursorTest
{
public class CursorHelper
{
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);
private static Cursor InternalCreateCursor(System.Drawing.Bitmap bmp,
int xHotSpot, int yHotSpot)
{
IconInfo tmp = new IconInfo();
GetIconInfo(bmp.GetHicon(), ref tmp);
tmp.xHotspot = xHotSpot;
tmp.yHotspot = yHotSpot;
tmp.fIcon = false;
IntPtr ptr = CreateIconIndirect(ref tmp);
SafeFileHandle handle = new SafeFileHandle(ptr, true);
return CursorInteropHelper.Create(handle);
}
public static Cursor CreateCursor(UIElement element, int xHotSpot, int yHotSpot) {
element.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
element.Arrange(new Rect(0 0 element.DesiredSize.Width,
element.DesiredSize.Height));
RenderTargetBitmap rtb = new RenderTargetBitmap((int)element.DesiredSize.Width, (int)element.DesiredSize.Height, 96, 96, PixelFormats.Pbgra32);
rtb.Render(element);
PngBitmapEncoder encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(rtb));
MemoryStream ms = new MemoryStream();
encoder.Save(ms);
Trang 5System.Drawing.Bitmap bmp = new System.Drawing.Bitmap(ms);
ms.Close();
ms.Dispose();
Cursor cur = InternalCreateCursor(bmp, xHotSpot, yHotSpot);
bmp.Dispose();
return cur;
}
}
}
Bây giờ là cách sử dụng class:
PHP Code:
public partial class CursorTest : Window
{
public CursorTest()
{
InitializeComponent();
TextBlock tb = new TextBlock();
tb.Text = "{ } Switch On The Code" ;
tb.FontSize = 10;
tb.Foreground = Brushes.Green;
this.Cursor = CursorHelper.CreateCursor(tb, , );
}
}
Và kết quả là:
Translate from http://www.switchonthecode.com
Try my best