Как создать пользовательский курсор в C # UWP [дубликат]

Я понимаю, что это очень старое сообщение и уже есть отличные ответы, но я хотел сделать простой MCVE, чтобы продемонстрировать один такой подход и позволить новым кодеру быстро увидеть концепцию в действии.

В этом примере мы будем использовать 5 файлов:

  1. Main.java - просто используется для запуска приложения и вызова первого контроллера.
  2. Controller1.java - контроллер для первого макета FXML.
  3. Controller2.java - контроллер для второго макета FXML.
  4. Layout1.fxml - макет FXML для первого сцена.
  5. Layout2.fxml - макет FXML для второй сцены.

Все файлы перечислены полностью в нижней части этого сообщения.

Цель: показать переданные значения от Controller1 до Controller2 и наоборот.

Поток программы:

  • Первая сцена содержит TextField, Button и Label. Когда щелчок Button щелкнут, второе окно загружается и отображается, включая текст, введенный в TextField.
  • Внутри второй сцены есть также TextField, Button , и Label. Label отобразит текст, введенный в TextField на первой сцене.
  • При вводе текста во второй сцене TextField и нажатии Button первая сцена Label сцены обновлен, чтобы показать введенный текст.

Это очень простая демонстрация и, безусловно, может означать некоторое улучшение, но должна сделать концепцию очень понятной.

Сам код также комментируется некоторые детали того, что происходит и как.

КОД

Main.java:

import javafx.application.Application;
import javafx.stage.Stage;

public class Main extends Application {

    public static void main(String[] args) {
        launch(args);
    }

    @Override
    public void start(Stage primaryStage) {

        // Create the first controller, which loads Layout1.fxml within its own constructor
        Controller1 controller1 = new Controller1();

        // Show the new stage
        controller1.showStage();

    }
}

Controller1.java:

import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.stage.Stage;

import java.io.IOException;

public class Controller1 {

    // Holds this controller's Stage
    private final Stage thisStage;

    // Define the nodes from the Layout1.fxml file. This allows them to be referenced within the controller
    @FXML
    private TextField txtToSecondController;
    @FXML
    private Button btnOpenLayout2;
    @FXML
    private Label lblFromController2;

    public Controller1() {

        // Create the new stage
        thisStage = new Stage();

        // Load the FXML file
        try {
            FXMLLoader loader = new FXMLLoader(getClass().getResource("Layout1.fxml"));

            // Set this class as the controller
            loader.setController(this);

            // Load the scene
            thisStage.setScene(new Scene(loader.load()));

            // Setup the window/stage
            thisStage.setTitle("Passing Controllers Example - Layout1");

        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * Show the stage that was loaded in the constructor
     */
    public void showStage() {
        thisStage.showAndWait();
    }

    /**
     * The initialize() method allows you set setup your scene, adding actions, configuring nodes, etc.
     */
    @FXML
    private void initialize() {

        // Add an action for the "Open Layout2" button
        btnOpenLayout2.setOnAction(event -> openLayout2());
    }

    /**
     * Performs the action of loading and showing Layout2
     */
    private void openLayout2() {

        // Create the second controller, which loads its own FXML file. We pass a reference to this controller
        // using the keyword [this]; that allows the second controller to access the methods contained in here.
        Controller2 controller2 = new Controller2(this);

        // Show the new stage/window
        controller2.showStage();

    }

    /**
     * Returns the text entered into txtToSecondController. This allows other controllers/classes to view that data.
     */
    public String getEnteredText() {
        return txtToSecondController.getText();
    }

    /**
     * Allows other controllers to set the text of this layout's Label
     */
    public void setTextFromController2(String text) {
        lblFromController2.setText(text);
    }
}

Controller2.java:

import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.stage.Stage;

import java.io.IOException;

public class Controller2 {

    // Holds this controller's Stage
    private Stage thisStage;

    // Will hold a reference to the first controller, allowing us to access the methods found there.
    private final Controller1 controller1;

    // Add references to the controls in Layout2.fxml
    @FXML
    private Label lblFromController1;
    @FXML
    private TextField txtToFirstController;
    @FXML
    private Button btnSetLayout1Text;

    public Controller2(Controller1 controller1) {
        // We received the first controller, now let's make it usable throughout this controller.
        this.controller1 = controller1;

        // Create the new stage
        thisStage = new Stage();

        // Load the FXML file
        try {
            FXMLLoader loader = new FXMLLoader(getClass().getResource("Layout2.fxml"));

            // Set this class as the controller
            loader.setController(this);

            // Load the scene
            thisStage.setScene(new Scene(loader.load()));

            // Setup the window/stage
            thisStage.setTitle("Passing Controllers Example - Layout2");

        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * Show the stage that was loaded in the constructor
     */
    public void showStage() {
        thisStage.showAndWait();
    }

    @FXML
    private void initialize() {

        // Set the label to whatever the text entered on Layout1 is
        lblFromController1.setText(controller1.getEnteredText());

        // Set the action for the button
        btnSetLayout1Text.setOnAction(event -> setTextOnLayout1());
    }

    /**
     * Calls the "setTextFromController2()" method on the first controller to update its Label
     */
    private void setTextOnLayout1() {
        controller1.setTextFromController2(txtToFirstController.getText());
    }

}

Layout1.fxml:

<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.VBox?>
<AnchorPane xmlns="http://javafx.com/javafx/9.0.1" xmlns:fx="http://javafx.com/fxml/1">
    <VBox alignment="CENTER" spacing="10.0">
        <padding>
            <Insets bottom="10.0" left="10.0" right="10.0" top="10.0"/>
        </padding>
        <Label style="-fx-font-weight: bold;" text="This is Layout1!"/>
        <HBox alignment="CENTER_LEFT" spacing="10.0">
            <Label text="Enter Text:"/>
            <TextField fx:id="txtToSecondController"/>
            <Button fx:id="btnOpenLayout2" mnemonicParsing="false" text="Open Layout2"/>
        </HBox>
        <VBox alignment="CENTER">
            <Label text="Text From Controller2:"/>
            <Label fx:id="lblFromController2" text="Nothing Yet!"/>
        </VBox>
    </VBox>
</AnchorPane>

Layout2.fxml:

<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.VBox?>
<AnchorPane xmlns="http://javafx.com/javafx/9.0.1" xmlns:fx="http://javafx.com/fxml/1">
    <VBox alignment="CENTER" spacing="10.0">
        <padding>
            <Insets bottom="10.0" left="10.0" right="10.0" top="10.0"/>
        </padding>
        <Label style="-fx-font-weight: bold;" text="Welcome to Layout 2!"/>
        <VBox alignment="CENTER">
            <Label text="Text From Controller1:"/>
            <Label fx:id="lblFromController1" text="Nothing Yet!"/>
        </VBox>
        <HBox alignment="CENTER_LEFT" spacing="10.0">
            <Label text="Enter Text:"/>
            <TextField fx:id="txtToFirstController"/>
            <Button fx:id="btnSetLayout1Text" mnemonicParsing="false" text="Set Text on Layout1"/>
        </HBox>
    </VBox>
</AnchorPane>
42
задан Dave Clemmer 1 August 2011 в 17:09
поделиться

15 ответов

У вас есть два основных варианта:

  1. Когда курсор мыши находится над вашим управлением, скройте системный курсор, установив this.Cursor = Cursors.None; и нарисуйте свой собственный курсор, используя любую технику, которая вам нравится. Затем обновите положение и внешний вид вашего курсора, отвечая на события мыши. Вот два примера: http://www.xamlog.com/2006/07/17/creating-a-custom-cursor/ http://www.hanselman.com/blog /DeveloperDesigner.aspx
  2. Создайте новый объект курсора, загрузив изображение из файла .cur или .ani. Вы можете создавать и редактировать эти файлы в Visual Studio. Есть также некоторые бесплатные утилиты, плавающие вокруг для борьбы с ними. В основном это изображения (или анимированные изображения), которые указывают «горячую точку», указывающую, на какой точке изображения находится курсор.

Если вы решите загрузить из файла, отметьте что вам нужен абсолютный путь к файловой системе для использования конструктора Cursor(string fileName). А именно, относительный путь или URI пакета не будут работать. Если вам нужно загрузить курсор из относительного пути или из ресурса, собранного вместе с вашей сборкой, вам нужно будет получить поток из файла и передайте его конструктору Cursor(Stream cursorStream).

С другой стороны, указание курсора как относительного пути при загрузке с помощью атрибута XAML делает работу , факт, который вы могли бы использовать для загрузки курсора на скрытый элемент управления, а затем скопируйте ссылку для использования на другом элементе управления. Я не пробовал, но он должен работать.

30
ответ дан PeterAllenWebb 19 August 2018 в 01:16
поделиться
  • 1
    к сожалению, первый пример больше не работает без разрешения. – Michael Niemand 24 August 2009 в 11:24
  • 2
    Также обратите внимание, что вы можете построить свой курсор на лету из любого содержимого WPF. См. stackoverflow.com/questions/2835502/… для примера того, как это делается. – Ray Burns 14 May 2010 в 20:42
  • 3
    Ссылка, опубликованная в предыдущем комментарии, касается поворота существующего курсора. Я только что опубликовал новый ответ на этот вопрос (см. Ниже), в котором рассказывается, как преобразовать произвольный Visual в Cursor. – Ray Burns 14 May 2010 в 20:53
  • 4
    скрытый контрольный трюк был гениальным трюком :-) – Bizhan 17 February 2013 в 19:05

вы можете сделать это с помощью кода типа

this.Cursor = new Cursor(@"<your address of icon>");
0
ответ дан Ahmad 19 August 2018 в 01:16
поделиться

Если вы используете визуальную студию, вы можете

  1. Создать файл курсора
  2. Копировать / Вставить изображение
  3. Сохранить его .cur файл.
1
ответ дан Alex Cube 19 August 2018 в 01:16
поделиться

Я знаю, что эта тема уже несколько лет, но вчера я захотел загрузить пользовательский файл курсора из ресурсов проекта и столкнулся с подобными проблемами. Я искал в Интернете решение и не нашел нужное: установить this.Cursor на пользовательский курсор, который хранится в моей папке ресурсов в моем проекте во время выполнения. Я попробовал решение xaml от Ben, но не нашел его достаточно элегантным. PeterAllen заявил:

А именно, относительный путь или 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); 
8
ответ дан Anne 19 August 2018 в 01:16
поделиться
  • 1
    & Quot; MainApp & Quot; следует заменить на name вашего приложения. & Quot; Ресурсы & Quot; следует заменить на относительный путь к папкам * .cur внутри вашего проекта. – Mark Miller 26 July 2016 в 03:43

Как упоминалось выше, 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

29
ответ дан Ben McIntosh 19 August 2018 в 01:16
поделиться
  • 1
    Есть ли причина, по которой вы использовали TextBlock для кэширования ссылок курсора над FrameworkElement, где сначала определено свойство Cursor? – PaulJ 21 March 2011 в 11:07
  • 2
    Нет причин; FrameworkElement будет лучшим выбором. Благодаря! – Ben McIntosh 22 March 2011 в 09:18

Возможно, он изменился с помощью Visual Studio 2017, но я смог ссылаться на файл .cur как на встроенный ресурс:

<Setter
    Property="Cursor"
    Value="/assembly-name;component/location-name/curser-name.cur" />
1
ответ дан CIMframe 19 August 2018 в 01:16
поделиться

В случае, если кто-то ищет сам UIElement в качестве курсора, я объединил решения Ray и 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);
    }
8
ответ дан Community 19 August 2018 в 01:16
поделиться
  • 1
    Я бы очистил его с помощью утверждений вокруг ваших потоков, но кроме этого у меня нет проблем с этим методом (в отличие от других реализаций). – outbred 26 August 2015 в 19:42
  • 2
    Я заметил, что вызов Arrange в элементе управления приводит к тому, что как ListBoxItems, так и TreeViewItems на мгновение исчезают, только для повторного появления позже, после изменения макетов своих родителей (например, расширения TreeViewItem). Любая идея, почему это так? – James M 25 April 2016 в 03:27

Вот многофункциональная бесплатная утилита, которая позволяет создавать cur-файл из любого изображения: http://www.rw-designer.com/cursor-maker

Он называется RealWorld Cursor Editor.

И вот ссылка о том, как вставить курсор в проект:

http://wpf.2000things.com/tag / курсора /

0
ответ дан Eternal21 19 August 2018 в 01:16
поделиться

Еще одно решение, похожее на Ray, но вместо медленного и громоздкого копирования пикселей, это использует некоторые внутренние элементы 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));
}
4
ответ дан Gábor 19 August 2018 в 01:16
поделиться
  • 1
    Это прекрасно работает (здорово создавать курсор из любой визуализации WPF, которую я хочу), однако я продолжал получать исключение SEH в dtor курсора, созданного этим методом всякий раз, когда связанный объект был уничтожен. Единственный способ не получить это - создать синглтон курсора и повторно использовать его повсюду. Любая причина, по которой вы знаете, вызвала бы исключение SEH? Я могу догадаться об этом весь день, но действительно кажется, что объект, используемый для создания изображения для курсора, удаляется, а класс Cursor взрывает его. – outbred 26 August 2015 в 19:03
  • 2
    Хороший пример, который работает хорошо, но есть ошибка, то есть info.yHotspot = (byte)(HotSpot.X * cursor.Height); (должен быть HotSpot.Y, а не HotSpot.X). Этот пример также изменяет диапазон исходного кода точки доступа, масштабируя его по размеру растрового изображения источника, поэтому имейте это в виду при указании вашего смещения. – Mark Feldman 23 July 2017 в 01:40
  • 3
    Достаточно справедливо, исправлено. – Gábor 23 July 2017 в 08:59

Очень простой способ - создать курсор в Visual Studio в качестве .cur-файла, а затем добавить его к ресурсам проектов.

Затем просто добавьте следующий код, если вы хотите назначить курсор :

myCanvas.Cursor = new Cursor(new System.IO.MemoryStream(myNamespace.Properties.Resources.Cursor1));
8
ответ дан Greg 19 August 2018 в 01:16
поделиться

Существует более простой способ, чем управлять отображением курсора самостоятельно или с помощью 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 должен быть стандартным размером курсора (например, 16x16 или 32x32), например:

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

Он будет использоваться следующим образом:

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

Очевидно, что ваш FrameworkElement может быть элементом <Image>, если у вас есть существующее изображение, или вы можете рисовать все, что вам нравится, используя встроенные инструменты рисования WPF.

Обратите внимание, что Подробные сведения о формате .cur можно найти в ICO (формат файла) .

14
ответ дан Jens 19 August 2018 в 01:16
поделиться
  • 1
    Эй, я попытался использовать этот фрагмент кода для определения пользовательского курсора с помощью xaml. К сожалению, он просто ничего не отображает вместо элемента <Image />, определенного мной. Отлаживая код, я понял, что var pixels -array просто содержит 0 для каждого пикселя после запуска CopyPixels() -метода. Я получил ошибку для stride -параметра для CopyPixels() -метода, поэтому я немного изменил код в соответствии с некоторыми другими фрагментами, которые я нашел: int stride = width * ((bitmapSource.Format.BitsPerPixel + 7) / 8); За исключением того, что код выглядит так же, как и выше. visual: <Image Height="32" Width="32"/> – andineupert 30 October 2013 в 21:06

Чтобы использовать пользовательский курсор в XAML, я изменил код Ben McIntosh немного:

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

Чтобы использовать курсор только ссылку на ресурс:

<StackPanel Cursor="{StaticResource OpenHandCursor}" />
9
ответ дан kkCosmo 19 August 2018 в 01:16
поделиться
  • 1
    Использование ресурса курсора вместо элемента структуры "манекен" имеет гораздо больше смысла – DiamondDrake 1 August 2016 в 06:46

Убедитесь, что любой ресурс GDI (например, bmp.GetHIcon) удаляется. В противном случае вы получите утечку памяти. Следующий код (метод расширения для значка) отлично работает для WPF. Он создает стрелочный указатель с маленьким значком справа внизу.

Примечание. Этот код использует значок для создания курсора. Он не использует текущий контроль пользовательского интерфейса.

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);
1
ответ дан Matthias 19 August 2018 в 01:16
поделиться

Вы можете попробовать это

<Window Cursor=""C:\WINDOWS\Cursors\dinosaur.ani"" />
2
ответ дан palehorse 19 August 2018 в 01:16
поделиться
  • 1
    Ссылка Xamlog предназначена только для участников :( – jschroedl 8 September 2009 в 14:57

Также проверьте BabySmash Скотта Гензельмана (www.codeplex.com/babysmash). Он использовал метод «грубой силы», скрывающий курсор Windows и показывающий его новый курсор на холсте, а затем перемещение курсора в «реальный» курсор был бы

. Подробнее здесь: http://www.hanselman.com/blog/DeveloperDesigner.aspx

1
ответ дан rudigrobler 19 August 2018 в 01:16
поделиться
Другие вопросы по тегам:

Похожие вопросы: