53

イメージまたはアイコンを WPF アプリでカスタム カーソルとして使用したい。どうやってやるの?

4

15 に答える 15

37

2つの基本的なオプションがあります。

  1. マウスカーソルがコントロール上にある場合は、設定してシステムカーソルを非表示にし、this.Cursor = Cursors.None;好きなテクニックを使用して独自のカーソルを描画します。次に、マウスイベントに応答して、カーソルの位置と外観を更新します。次に2つの例を示します。

  2. .curまたは.aniファイルから画像をロードして、新しいCursorオブジェクトを作成します。これらの種類のファイルは、VisualStudioで作成および編集できます。それらに対処するために浮かんでいるいくつかの無料のユーティリティもあります。基本的には、カーソルが画像内のどのポイントにあるかを示す「ホットスポット」を指定する画像(またはアニメーション画像)です。

Cursor(string fileName)ファイルからロードすることを選択した場合、コンストラクターを使用するには絶対ファイルシステムパスが必要であることに注意してください。残念ながら、相対パスまたはパックURIは機能しません。相対パスまたはアセンブリにパックされたリソースからカーソルをロードする必要がある場合は、ファイルからストリームを取得してCursor(Stream cursorStream)コンストラクターに渡す必要があります。迷惑ですが本当です。

一方、XAML属性を使用してカーソルをロードするときに相対パスとしてカーソルを指定すること機能します。これを使用して、カーソルを非表示のコントロールにロードし、参照をコピーして別のコントロールで使用できます。私はそれを試していませんが、うまくいくはずです。

于 2008-11-05T17:38:10.477 に答える
35

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;
于 2009-01-04T05:40:27.493 に答える
17

カーソル表示を自分で管理したり、Visual Studio を使用して多数のカスタム カーソルを作成したりするよりも簡単な方法があります。

FrameworkElement がある場合は、次のコードを使用して Cursor を構築できます。

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 など) でなければならないことに注意してください。次に例を示します。

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

次のように使用されます。

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

<Image>明らかに、既存の画像がある場合、または WPF の組み込み描画ツールを使用して好きなものを描画できる場合、FrameworkElement はコントロールになる可能性があります。

.cur ファイル形式の詳細については、ICO (ファイル形式)を参照してください。

于 2010-05-14T19:51:12.640 に答える
9

非常に簡単な方法は、Visual Studio 内でカーソルを .cur ファイルとして作成し、それをプロジェクトのリソースに追加することです。

次に、カーソルを割り当てたいときに次のコードを追加します。

myCanvas.Cursor = new Cursor(new System.IO.MemoryStream(myNamespace.Properties.Resources.Cursor1));
于 2012-01-03T16:46:00.457 に答える
8

Ray のソリューションにやや似たもう 1 つのソリューションがありますが、遅くて面倒なピクセル コピーの代わりに、これはいくつかの 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/Invokes も必要ありません)。

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));
}
于 2014-06-16T15:17:10.090 に答える
8

プロジェクト リソースからカスタム カーソル ファイルをロードしたかったのですが、同様の問題に遭遇しました。インターネットで解決策を検索しましたが、必要なものが見つかりませんでしたthis.Cursor。実行時にプロジェクトのリソース フォルダーに保存されているカスタム カーソルに設定することです。Ben の xaml ソリューションを試してみましたが、十分にエレガントではありませんでした。ピーターアレンは次のように述べています。

残念なことに、相対パスまたはパック 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); 

MainAppアプリケーションの名前に置き換える必要があります。Resourcesプロジェクト内の *.cur ファイルへの相対フォルダー パスに置き換える必要があります。

于 2011-12-01T19:10:21.750 に答える
2

あなたはこれを試すことができます

<Window Cursor=""C:\WINDOWS\Cursors\dinosaur.ani"" />
于 2008-09-05T20:21:47.057 に答える
1

Scott Hanselman の BabySmash (www.codeplex.com/babysmash) もチェックしてください。彼は、Windows カーソルを非表示にし、キャンバス上に新しいカーソルを表示してから、カーソルを移動するという、より「ブルート フォース」な方法を使用しました。

詳細はこちら: http://www.hanselman.com/blog/DeveloperDesigner.aspx

于 2008-09-08T08:57:03.993 に答える
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);
于 2012-11-15T10:15:48.037 に答える
1

ビジュアルスタジオを使用している場合は、次のことができます

  1. カーソルファイルを新規作成
  2. 画像をコピー/貼り付け
  3. .cur ファイルに保存します。
于 2013-08-16T07:42:50.003 に答える