2008-09-05 13 views

Odpowiedz

28

masz dwie podstawowe opcje:

  1. Gdy kursor myszy znajduje się nad kontrolą, ukryć kursor systemowy poprzez ustawienie this.Cursor = Cursors.None; i wyciągnąć własne za pomocą kursora dowolną techniką chcesz. Następnie zaktualizuj położenie i wygląd kursora, odpowiadając na zdarzenia myszy. Oto dwa przykłady:

  2. utworzyć nowy obiekt Cursor poprzez załadowanie obrazu z .cur ANI lub pliku. Możesz tworzyć i edytować tego rodzaju pliki w Visual Studio. Istnieją również wolne zasoby, które mogą się nimi zająć. Zasadniczo są to obrazy (lub animowane obrazy), które określają "gorący punkt" wskazujący, w którym punkcie obrazu znajduje się kursor.

Jeśli zdecydujesz się załadować z pliku, należy pamiętać, że trzeba bezwzględną ścieżkę do systemu plików użyć konstruktora Cursor(string fileName). Lamely, ścieżka względna lub URI Pack nie będą działać. Jeśli chcesz załadować kursor ze ścieżki względnej lub z zasobu spakowanego z zestawem, musisz pobrać strumień z pliku i przekazać go do konstruktora Cursor(Stream cursorStream). Irytujące, ale prawdziwe.

Z drugiej strony, określając kursor jako ścieżkę względną podczas ładowania za pomocą atrybutu XAML wykonuje się, fakt, którego można użyć do załadowania kursora do ukrytego elementu sterującego, a następnie skopiowania odwołania do użycia na kolejna kontrola. Nie próbowałem tego, ale powinno działać.

+7

1st przykład nie pracuje już bez autoryzacji. –

+0

Należy również pamiętać, że można skonstruować kursor w locie z dowolnej zawartości WPF. Zobacz, jak to zrobić, zobacz http://stackoverflow.com/questions/2835502/rotating-cursor- recording-to-rotated-textbox/2836904#2836904. –

+0

Link opublikowany w poprzedniej commem dotyczy obracania istniejącego kursora.Właśnie napisałem nową odpowiedź na to pytanie (patrz poniżej), która mówi, jak przekonwertować dowolny wizualny na kursor. –

2

można spróbować to

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

Xamlog link jest tylko dla członków-niestety :( – jschroedl

1

Również sprawdzić Scott Hanselman za BabySmash (www.codeplex.com/babysmash). Używał bardziej „brute force” Metoda ukrywanie kursora PC i pokazano jego nowy kursor na płótnie, a następnie przesuwając kursor były „prawdziwe” kursor byłby

Czytaj więcej tutaj: http://www.hanselman.com/blog/DeveloperDesigner.aspx

26

Tak jak wspomniany wyżej Peter, jeśli już posiadasz plik .cur, możesz go użyć jako zasobu osadzonego, tworząc fikcyjny element w sekcji zasobów, a następnie odwołując się do kursora manekina, kiedy go potrzebujesz.

Załóżmy na przykład, że chcesz wyświetlić niestandardowe kursory w zależności od wybranego narzędzia.

Dodaj do zasobów:

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

Przykład wbudowanego kursora odwołanie w kodzie:

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

Czy istnieje jakiś powód, dla którego użyłeś TextBlock do buforowania odniesień kursora w stosunku do FrameworkElement, gdzie najpierw zdefiniowano właściwość Cursor? – PaulJ

+2

Bez powodu; FrameworkElement byłby lepszym wyborem. Dzięki! –

14

Jest łatwiejszy sposób niż kierowanie kursorem wyświetla się lub przy użyciu programu Visual Studio do skonstruowania wielu niestandardowych kursorów.

Jeśli masz FrameworkElement można stworzyć kursor z nim za pomocą następującego kodu:

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

pamiętać, że wielkość Twój FrameworkElement „s musi być standardowy rozmiar kursora (np 16x16 lub 32x32), na przykład :

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

byłoby stosować tak:

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

Oczywiście Twój obiekt FrameworkElement może być formantem <Image>, jeśli masz istniejący obraz lub możesz narysować co chcesz, używając wbudowanych narzędzi do rysowania WPF.

Należy pamiętać, że szczegółowe informacje dotyczące formatu pliku .cur można znaleźć pod adresem ICO (file format).

+3

Hej, próbowałem użyć tego fragmentu kodu do zdefiniowania niestandardowego kursora z xaml. Niestety po prostu nic nie wyświetla zamiast "zdefiniowanego przeze mnie" ". Debugując kod zdałem sobie sprawę, że tablica 'var pixels' zawiera po 0 dla każdego piksela po metodzie' CopyPixels() '. Dostałem błąd dla parametru 'stride' dla' CopyPixels() '-metoda, więc zmieniłem trochę kod według niektórych innych fragmentów, które znalazłem: ' int kroku = szerokość * ((bitmapSource.Format.BitsPerPixel + 7)/8); 'Z tym, że kod wygląda tak samo jak powyżej. "Wizualny" to: '' – andineupert

8

Wiem, że ten temat ma teraz kilka lat, ale wczoraj chciałem załadować niestandardowy plik kursora z zasobów projektu i napotkałem podobne problemy. Szukałem rozwiązania w Internecie i nie znalazłem tego, czego potrzebowałem: ustawić this.Cursor na niestandardowy kursor przechowywany w folderze Moje zasoby w moim projekcie w czasie wykonywania. Wypróbowałem rozwiązanie Xaml firmy Ben, ale nie znalazłem wystarczająco eleganckiego. PeterAllen stwierdził:

Niestety, względna ścieżka lub URI opakowania nie będą działać. Jeśli chcesz załadować kursor ze ścieżki względnej lub z zasobu spakowanego z zestawem, musisz pobrać strumień z pliku i przekazać go do konstruktora Cursor (Stream cursorStream). Irytujące, ale prawdziwe.

Natknąłem się na miły sposób, aby to zrobić i rozwiązuje mój problem:

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" należy zastąpić * nazwą * twojej aplikacji. "Zasoby" powinny zostać zastąpione względną ścieżką do plików * .cur wewnątrz twojego projektu. –

8

Bardzo łatwym sposobem jest stworzenie kursora w Visual Studio jako plik .cur, a następnie dodać, że do zasoby projektów.

Następnie wystarczy dodać następujący kod kiedy chcesz przypisać kursor:

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

Wielkie dzięki za to! – Cipher

1

Upewnij się, że każdy zasób GDI (np bmp.GetHIcon) zostanie usunięte. W przeciwnym razie skończy się przeciek pamięci. Poniższy kod (metoda rozszerzenia dla ikony) działa idealnie w przypadku WPF. Tworzy kursor strzałki z małą ikoną w prawym dolnym rogu.

Uwaga: Ten kod używa ikony do utworzenia kursora. Nie używa aktualnej kontroli interfejsu użytkownika.

Matthias

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

Aby użyć niestandardowego kursora w XAML I zmieniony kod Ben McIntosh przewidzianego nieznacznie:

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

używać kursora tylko odwoływać się do zasobów:

<StackPanel Cursor="{StaticResource OpenHandCursor}" /> 
+0

Używanie zasobu Cursor zamiast elementu szkieletowego "manekin" ma dużo więcej sensu – DiamondDrake

1

Jeśli korzystasz z wizualnego studio, możesz

  1. Nowy kursor plik
  2. skopiować/wkleić obraz
  3. zapisać go do .cur pliku.
4

Jeszcze rozwiązanie nieco podobny do Raya, ale zamiast powolnego i uciążliwego kopiowania pikseli ta wykorzystuje pewne wewnętrzne 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)); 
} 

istnieje metoda rozszerzenie w środku, że wolę mieć w sposób klasa rozszerzenia dla takich przypadków:

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

To wszystko jest proste i proste.

I, jeśli zdarzy ci się nie należy określić swój własny hotspot, można nawet wyciąć ten krótszy (nie trzeba się struct lub P/powołuje albo):

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

Ten działa świetnie (to niesamowite tworzyć Cursor z dowolnego grafiku WPF, jakiego potrzebuję), jednak ciągle otrzymywałem wyjątek SEH w dtor Kursor utworzony za pomocą tej metody, gdy powiązany obiekt został zniszczony. Jedynym sposobem, aby tego nie uzyskać, jest utworzenie singletonu kursora i ponowne użycie go wszędzie. Jakiś powód, dla którego to wiesz, spowoduje wyjątek SEH? Mogę zgadywać cały dzień, ale wygląda na to, że obiekt użyty do stworzenia obrazu dla kursora zostaje usunięty, a klasa Cursor wysadza w powietrze b/c tego. – outbred

+0

Dobry przykład, który działa dobrze, ale istnieje błąd, np. 'Info.yHotspot = (byte) (HotSpot.X * cursor.Height);' (powinien być HotSpot.Y, a nie HotSpot.X). W tym przykładzie zmienia się także zakres oryginalnego kodu punktu aktywnego, skalując go według wymiarów źródłowej bitmapy, więc należy o tym pamiętać przy określaniu przesunięcia. –

+0

Wystarczająco uczciwe, poprawione. –

8

W przypadku ktoś szuka UIElement się jako kursorem, I połączył rozwiązania Ray i Arcturus:

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

Czyściłbym to, używając instrukcji do twoich strumieni, ale poza tym nie mam problemów z tą metodą (w przeciwieństwie do innych implementacji). – outbred

+0

Zauważyłem, że wywoływanie polecenia 'Rozmieść' na kontrolce powoduje chwilowe zniknięcie zarówno obiektu ListBoxItems, jak i obiektu TreeViewItems, a następnie ponowne pojawienie się później po zmianie układu macierzystego (np. Rozwinięcie elementu TreeViewItem). Masz pomysł, dlaczego tak jest? –

0

można to zrobić przez kodeksu jak

this.Cursor = new Cursor(@"<your address of icon>"); 
0

Może to się zmieniło z Visual Studio 2017, ale udało mi się odwoływać plik .cur jako osadzonego zasobu:

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