2008-09-05 53 views

回答

28

你有兩個基本的選擇:

  1. 當鼠標光標在你的控制,通過設置this.Cursor = Cursors.None;隱藏系統光標,並使用你喜歡的任何技術,繪製自己的光標。然後,通過響應鼠標事件來更新光標的位置和外觀。這裏有兩個例子:

  2. 由一個或.CUR文件.ani的加載圖像創建一個新的光標對象。您可以在Visual Studio中創建和編輯這些類型的文件。還有一些免費的實用程序可以處理它們。基本上它們是圖像(或動畫圖像),它們指定一個「熱點」,指示光標位於圖像中的哪個點。

如果選擇從文件加載,請注意您需要一個絕對的文件系統路徑使用Cursor(string fileName)構造。 Lamely,相對路徑或Pack URI將不起作用。如果您需要從相對路徑或與您的程序集一起打包的資源加載光標,則需要從文件中獲取流並將其傳遞給Cursor(Stream cursorStream)構造函數。令人討厭但真實。

。另一方面,指定一個遊標的相對路徑使用XAML屬性工作,你可以用它來得到加載到一個隱藏的控制光標,然後一個事實複製參考使用上加載時另一種控制。我沒有嘗試過,但它應該工作。

+7

不幸的是,第一個例子確實不再工作未經授權。 – 2009-08-24 10:24:11

+0

另請注意,您可以從任何WPF內容中隨時構建遊標。請參閱http://stackoverflow.com/questions/2835502/rotating-cursor-according-to-rotated-textbox/2836904#2836904,瞭解如何完成此操作的示例。 – 2010-05-14 19:42:40

+0

我在前面的提交中發佈的鏈接處理旋轉現有的光標。我剛剛發佈了一個關於這個問題的新答案(見下文),它告訴我們如何將任意的Visual轉換爲Cursor。 – 2010-05-14 19:53:12

2

你可以試試這個

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

Xamlog鏈接是會員制:( – jschroedl 2009-09-08 13:57:13

26

像Peter上面提到的那樣,如果您已經有了.cur文件,您可以通過在資源部分創建一個虛擬元素,然後在需要時引用虛擬光標,將其用作嵌入式資源。

例如,假設您想根據所選工具顯示非標準遊標。

添加到資源:

<Window.Resources> 
    <ResourceDictionary> 
     <TextBlock x:Key="CursorGrab" Cursor="Resources/Cursors/grab.cur"/> 
     <TextBlock x:Key="CursorMagnify" Cursor="Resources/Cursors/magnify.cur"/> 
    </ResourceDictionary> 
</Window.Resources> 

實施例代碼中引用嵌入光標的:

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; 

-Ben

+2

是否有任何理由爲什麼你已經使用TextBlock緩存Cursor引用超過FrameworkElement,第一次定義Cursor屬性? – PaulJ 2011-03-21 10:07:58

+2

沒理由; FrameworkElement將是更好的選擇。謝謝! – 2011-03-22 08:18:26

14

有比管理光標顯示自己或使用Visual更簡單的方法Studio來構建大量的自定義遊標。

如果你有一個FrameworkElement的,你可以使用下面的代碼構建從它光標:

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的的尺寸必須是標準的光標大小(例如16×16或32×32),例如:

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

它會像這樣使用:

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

顯然如果你有一個現有的圖像,你的FrameworkElement可能是一個<Image>控件,或者你可以使用WPF的內置繪圖工具繪製任何你喜歡的東西。

請注意,.cur文件格式的詳細信息可在ICO (file format)找到。

+3

嘿,我試圖用這個代碼片斷來定義一個用xaml定製的遊標。不幸的是,它只顯示任何東西,而不是我定義的「」元素。調試代碼後,我意識到'CopyPixels()`方法運行後,每個像素的`var pixels`-array只包含0。我爲`CopyPixels()`方法的`stride`參數產生了一個錯誤,所以我根據其他一些代碼片斷改變了代碼: int stride = width *((bitmapSource.Format.BitsPerPixel + 7)/ 8);`除了代碼看起來像上面一樣。 `visual`是:`` – andineupert 2013-10-30 19:06:00

8

我知道這個主題現在已經有幾年了,但是昨天我想從項目資源中加載一個自定義光標文件,並遇到類似的問題。我在網上搜索了一個解決方案,但沒有找到我需要的:在運行時將this.Cursor設置爲存儲在我的項目資源文件夾中的自定義光標。 我試過本的xaml解決方案,但沒有發現它足夠優雅。 PeterAllen說:

Lamely,相對路徑或Pack URI將不起作用。如果需要從相對路徑或裝配到程序集的資源加載遊標,則需要從文件中獲取流並將其傳遞給Cursor(Stream cursorStream)構造函數。令人討厭但真實。

我無意中發現了一個很好的方式做到這一點,解決了我的問題:

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); 
+0

「MainApp」應該替換爲應用程序的*名稱*。 「資源」應該替換爲項目內部的* .cur文件的相關文件夾路徑。 – 2016-07-26 03:43:39

8

一個非常簡單的方法是創建Visual Studio中的文件的.cur內的光標,然後添加到項目資源。

然後,只需添加以下代碼,當你要分配光標:

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

確保,任何GDI資源(例如bmp.GetHIcon)得到處理。否則,最終會發生內存泄漏。下面的代碼(圖標的擴展方法)完美適用於WPF。它會在右下方創建一個帶有小圖標的箭頭光標。

備註:此代碼使用圖標來創建光標。它不使用當前的UI控件。

馬蒂亞斯

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); 
8

要使用XAML自定義光標我改變本麥金託什略提供的代碼:

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

使用遊標只是參考資源:

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

如果您使用visual studio,您可以

  1. 新光標文件
  2. 複製/粘貼圖像
  3. 保存它的.cur文件。
4

還有一個解決方案有點類似雷人的,但不是緩慢和繁瑣像素複製,這種使用了一些Windows內部:

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)); 
} 

有一個在中間的擴展方法,我更喜歡有一個擴展類爲這種情況下:

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 /調用,要麼):

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)); 
} 
8

如果有人正在尋找的UIElement本身作爲一個光標,我結合的RayArcturus的解決方案:

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); 
    } 
0

您可以通過如下代碼

this.Cursor = new Cursor(@"<your address of icon>"); 
做到這一點
0

它可能已與Visual Studio 2017年改變,但我能夠引用的.cur文件作爲嵌入資源:

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