Kopieren und Einfügen in Xamarin.Mac

In diesem Artikel wird die Arbeit mit dem Pasteboard behandelt, um Kopieren und Einfügen in einer Xamarin.Mac-Anwendung bereitzustellen. Es zeigt, wie Sie mit Standarddatentypen arbeiten, die zwischen mehreren Apps gemeinsam genutzt werden können, und wie benutzerdefinierte Daten in einer bestimmten App unterstützt werden.

Übersicht

Wenn Sie mit C# und .NET in einer Xamarin.Mac-Anwendung arbeiten, haben Sie Zugriff auf dasselbe Pasteboard (Kopieren und Einfügen), das ein Entwickler hat Objective-C .

In diesem Artikel werden die beiden Standard Möglichkeiten zum Verwenden des Pasteboards in einer Xamarin.Mac-App behandelt:

  1. Standarddatentypen : Da Pasteboard-Vorgänge in der Regel zwischen zwei nicht verknüpften Apps ausgeführt werden, kennt keine App die Datentypen, die die andere unterstützt. Um das Potenzial für die Freigabe zu maximieren, kann das Einfügebrett mehrere Darstellungen eines bestimmten Elements (mit einem Standardsatz allgemeiner Datentypen) enthalten. Dadurch kann die verbrauchende App die Version auswählen, die für ihre Anforderungen am besten geeignet ist.
  2. Benutzerdefinierte Daten – Um das Kopieren und Einfügen komplexer Daten in Ihrem Xamarin.Mac zu unterstützen, können Sie einen benutzerdefinierten Datentyp definieren, der vom Pasteboard verarbeitet wird. Beispielsweise eine Vektorzeichnungs-App, mit der der Benutzer komplexe Shapes kopieren und einfügen kann, die aus mehreren Datentypen und Punkten bestehen.

Example of the running app

In diesem Artikel befassen wir uns mit den Grundlagen der Arbeit mit dem Pasteboard in einer Xamarin.Mac-Anwendung zur Unterstützung von Kopier- und Einfügevorgängen. Es wird dringend empfohlen, dass Sie zuerst den Artikel "Hello, Mac " durcharbeiten, insbesondere die Abschnitte "Einführung in Xcode" und "Interface Builder " und "Outlets" und "Actions ", da es sich um wichtige Konzepte und Techniken handelt, die wir in diesem Artikel verwenden werden.

Möglicherweise möchten Sie sich auch die Exposing C#-Klassen /-Methoden im Objective-C Abschnitt des Xamarin.Mac Internals-Dokuments ansehen. Außerdem werden die Register C#-Klassen und Export -Attribute erläutert, die zum Verbinden Ihrer C#-Klassen mit Objective-C Objekten und UI-Elementen verwendet werden.

Erste Schritte mit dem Pasteboard

Das Pasteboard stellt einen standardisierten Mechanismus zum Austauschen von Daten innerhalb einer bestimmten Anwendung oder zwischen Anwendungen dar. Die typische Verwendung für ein Pasteboard in einer Xamarin.Mac-Anwendung besteht darin, Kopier- und Einfügevorgänge zu verarbeiten, aber auch eine Reihe anderer Vorgänge werden unterstützt (z. B. Drag & Drop und Application Services).

Damit Sie schnell loslegen können, beginnen wir mit einer einfachen, praktischen Einführung in die Verwendung von Pasteboards in einer Xamarin.Mac-App. Später erläutern wir, wie das Pasteboard funktioniert und welche Methoden verwendet werden.

In diesem Beispiel erstellen wir eine einfache dokumentbasierte Anwendung, die ein Fenster verwaltet, das eine Bildansicht enthält. Der Benutzer kann Bilder zwischen Dokumenten in der App und aus anderen Apps oder mehreren Fenstern innerhalb derselben App kopieren und einfügen.

Erstellen des Xamarin-Projekts

Zunächst erstellen wir eine neue dokumentbasierte Xamarin.Mac-App, für die wir Die Unterstützung für Kopieren und Einfügen hinzufügen.

Gehen Sie folgendermaßen vor:

  1. Starten Sie Visual Studio für Mac, und klicken Sie auf den Link "Neues Projekt...".

  2. Wählen Sie "Mac>App Cocoa App">aus, und klicken Sie dann auf die Schaltfläche "Weiter":

    Creating a new Cocoa app project

  3. Geben Sie MacCopyPaste für den Projektnamen ein, und behalten Sie alles andere als Standard bei. Klicken Sie auf „Weiter:

    Setting the name of the project

  4. Klicken Sie auf die Schaltfläche "Erstellen ":

    Confirming the new project settings

Hinzufügen eines NSDocument

Als Nächstes fügen wir eine benutzerdefinierte NSDocument Klasse hinzu, die als Hintergrundspeicher für die Benutzeroberfläche der Anwendung fungiert. Sie enthält eine einzelne Bildansicht und weiß, wie Sie ein Bild aus der Ansicht in das Standard-Pasteboard kopieren und wie Sie ein Bild aus dem Standard-Pasteboard aufnehmen und in der Bildansicht anzeigen.

Klicken Sie mit der rechten Maustaste auf das Projekt "Xamarin.Mac" im Projektmappenblock , und wählen Sie " Neue Datei hinzufügen>".:

Adding an NSDocument to the project

Geben Sie für den NamenImageDocument ein, und klicken Sie auf Neu. Bearbeiten Sie die ImageDocument.cs Klasse, und stellen Sie sicher, dass sie wie folgt aussieht:

using System;
using AppKit;
using Foundation;
using ObjCRuntime;

namespace MacCopyPaste
{
    [Register("ImageDocument")]
    public class ImageDocument : NSDocument
    {
        #region Computed Properties
        public NSImageView ImageView {get; set;}

        public ImageInfo Info { get; set; } = new ImageInfo();

        public bool ImageAvailableOnPasteboard {
            get {
                // Initialize the pasteboard
                NSPasteboard pasteboard = NSPasteboard.GeneralPasteboard;
                Class [] classArray  = { new Class ("NSImage") };

                // Check to see if an image is on the pasteboard
                return pasteboard.CanReadObjectForClasses (classArray, null);
            }
        }
        #endregion

        #region Constructor
        public ImageDocument ()
        {
        }
        #endregion

        #region Public Methods
        [Export("CopyImage:")]
        public void CopyImage(NSObject sender) {

            // Grab the current image
            var image = ImageView.Image;

            // Anything to process?
            if (image != null) {
                // Get the standard pasteboard
                var pasteboard = NSPasteboard.GeneralPasteboard;

                // Empty the current contents
                pasteboard.ClearContents();

                // Add the current image to the pasteboard
                pasteboard.WriteObjects (new NSImage[] {image});

                // Save the custom data class to the pastebaord
                pasteboard.WriteObjects (new ImageInfo[] { Info });

                // Using a Pasteboard Item
                NSPasteboardItem item = new NSPasteboardItem();
                string[] writableTypes = {"public.text"};

                // Add a data provier to the item
                ImageInfoDataProvider dataProvider = new ImageInfoDataProvider (Info.Name, Info.ImageType);
                var ok = item.SetDataProviderForTypes (dataProvider, writableTypes);

                // Save to pasteboard
                if (ok) {
                    pasteboard.WriteObjects (new NSPasteboardItem[] { item });
                }
            }

        }

        [Export("PasteImage:")]
        public void PasteImage(NSObject sender) {

            // Initialize the pasteboard
            NSPasteboard pasteboard = NSPasteboard.GeneralPasteboard;
            Class [] classArray  = { new Class ("NSImage") };

            bool ok = pasteboard.CanReadObjectForClasses (classArray, null);
            if (ok) {
                // Read the image off of the pasteboard
                NSObject [] objectsToPaste = pasteboard.ReadObjectsForClasses (classArray, null);
                NSImage image = (NSImage)objectsToPaste[0];

                // Display the new image
                ImageView.Image = image;
            }

            Class [] classArray2 = { new Class ("ImageInfo") };
            ok = pasteboard.CanReadObjectForClasses (classArray2, null);
            if (ok) {
                // Read the image off of the pasteboard
                NSObject [] objectsToPaste = pasteboard.ReadObjectsForClasses (classArray2, null);
                ImageInfo info = (ImageInfo)objectsToPaste[0];

            }

        }
        #endregion
    }
}

Sehen wir uns einen Teil des Codes im Detail unten an.

Der folgende Code stellt eine Eigenschaft bereit, die das Vorhandensein von Bilddaten im Standard-Pasteboard testet, falls ein Bild verfügbar ist, true andernfalls falsezurückgegeben wird:

public bool ImageAvailableOnPasteboard {
    get {
        // Initialize the pasteboard
        NSPasteboard pasteboard = NSPasteboard.GeneralPasteboard;
        Class [] classArray  = { new Class ("NSImage") };

        // Check to see if an image is on the pasteboard
        return pasteboard.CanReadObjectForClasses (classArray, null);
    }
}

Der folgende Code kopiert ein Bild aus der angefügten Bildansicht in das Standard-Pasteboard:

[Export("CopyImage:")]
public void CopyImage(NSObject sender) {

    // Grab the current image
    var image = ImageView.Image;

    // Anything to process?
    if (image != null) {
        // Get the standard pasteboard
        var pasteboard = NSPasteboard.GeneralPasteboard;

        // Empty the current contents
        pasteboard.ClearContents();

        // Add the current image to the pasteboard
        pasteboard.WriteObjects (new NSImage[] {image});

        // Save the custom data class to the pastebaord
        pasteboard.WriteObjects (new ImageInfo[] { Info });

        // Using a Pasteboard Item
        NSPasteboardItem item = new NSPasteboardItem();
        string[] writableTypes = {"public.text"};

        // Add a data provider to the item
        ImageInfoDataProvider dataProvider = new ImageInfoDataProvider (Info.Name, Info.ImageType);
        var ok = item.SetDataProviderForTypes (dataProvider, writableTypes);

        // Save to pasteboard
        if (ok) {
            pasteboard.WriteObjects (new NSPasteboardItem[] { item });
        }
    }

}

Der folgende Code fügt ein Bild aus dem Standard-Pasteboard ein und zeigt es in der angefügten Bildansicht an (wenn das Einfügebrett ein gültiges Bild enthält):

[Export("PasteImage:")]
public void PasteImage(NSObject sender) {

    // Initialize the pasteboard
    NSPasteboard pasteboard = NSPasteboard.GeneralPasteboard;
    Class [] classArray  = { new Class ("NSImage") };

    bool ok = pasteboard.CanReadObjectForClasses (classArray, null);
    if (ok) {
        // Read the image off of the pasteboard
        NSObject [] objectsToPaste = pasteboard.ReadObjectsForClasses (classArray, null);
        NSImage image = (NSImage)objectsToPaste[0];

        // Display the new image
        ImageView.Image = image;
    }

    Class [] classArray2 = { new Class ("ImageInfo") };
    ok = pasteboard.CanReadObjectForClasses (classArray2, null);
    if (ok) {
        // Read the image off of the pasteboard
        NSObject [] objectsToPaste = pasteboard.ReadObjectsForClasses (classArray2, null);
        ImageInfo info = (ImageInfo)objectsToPaste[0]
    }
}

Mit diesem Dokument erstellen wir die Benutzeroberfläche für die Xamarin.Mac-App.

Erstellen der Benutzeroberfläche

Doppelklicken Sie auf die Datei "Main.storyboard ", um sie in Xcode zu öffnen. Fügen Sie als Nächstes eine Symbolleiste und ein Bild hinzu, und konfigurieren Sie sie wie folgt:

Editing the toolbar

Fügen Sie auf der linken Seite der Symbolleiste ein Bildsymbolelement hinzu, und fügen Sie es ein. Wir verwenden diese Tastenkombinationen zum Kopieren und Einfügen aus dem Menü "Bearbeiten". Fügen Sie als Nächstes vier Bildsymbolleistenelemente rechts neben der Symbolleiste hinzu. Wir verwenden diese, um das Bild gut mit einigen Standardbildern aufzufüllen.

Weitere Informationen zum Arbeiten mit Symbolleisten finden Sie in unserer Dokumentation zu Symbolleisten .

Als Nächstes machen wir die folgenden Ausgänge und Aktionen für unsere Symbolleistenelemente und das Bild gut verfügbar:

Creating outlets and actions

Weitere Informationen zum Arbeiten mit Verkaufsstellen und Aktionen finden Sie im Abschnitt "Outlets und Aktionen " unserer Dokumentation zu Hello, Mac .

Aktivieren der Benutzeroberfläche

Mit unserer in Xcode erstellten Benutzeroberfläche und unserem UI-Element, das über Ausgänge und Aktionen verfügbar gemacht wird, müssen wir den Code hinzufügen, um die Benutzeroberfläche zu aktivieren. Doppelklicken Sie auf die ImageWindow.cs Datei auf dem Lösungspad , und sehen Sie wie folgt aus:

using System;
using Foundation;
using AppKit;

namespace MacCopyPaste
{
    public partial class ImageWindow : NSWindow
    {
        #region Private Variables
        ImageDocument document;
        #endregion

        #region Computed Properties
        [Export ("Document")]
        public ImageDocument Document {
            get {
                return document;
            }
            set {
                WillChangeValue ("Document");
                document = value;
                DidChangeValue ("Document");
            }
        }

        public ViewController ImageViewController {
            get { return ContentViewController as ViewController; }
        }

        public NSImage Image {
            get {
                return ImageViewController.Image;
            }
            set {
                ImageViewController.Image = value;
            }
        }
        #endregion

        #region Constructor
        public ImageWindow (IntPtr handle) : base (handle)
        {
        }
        #endregion

        #region Override Methods
        public override void AwakeFromNib ()
        {
            base.AwakeFromNib ();

            // Create a new document instance
            Document = new ImageDocument ();

            // Attach to image view
            Document.ImageView = ImageViewController.ContentView;
        }
        #endregion

        #region Public Methods
        public void CopyImage (NSObject sender)
        {
            Document.CopyImage (sender);
        }

        public void PasteImage (NSObject sender)
        {
            Document.PasteImage (sender);
        }

        public void ImageOne (NSObject sender)
        {
            // Load image
            Image = NSImage.ImageNamed ("Image01.jpg");

            // Set image info
            Document.Info.Name = "city";
            Document.Info.ImageType = "jpg";
        }

        public void ImageTwo (NSObject sender)
        {
            // Load image
            Image = NSImage.ImageNamed ("Image02.jpg");

            // Set image info
            Document.Info.Name = "theater";
            Document.Info.ImageType = "jpg";
        }

        public void ImageThree (NSObject sender)
        {
            // Load image
            Image = NSImage.ImageNamed ("Image03.jpg");

            // Set image info
            Document.Info.Name = "keyboard";
            Document.Info.ImageType = "jpg";
        }

        public void ImageFour (NSObject sender)
        {
            // Load image
            Image = NSImage.ImageNamed ("Image04.jpg");

            // Set image info
            Document.Info.Name = "trees";
            Document.Info.ImageType = "jpg";
        }
        #endregion
    }
}

Sehen wir uns diesen Code im Folgenden ausführlich an.

Zunächst stellen wir eine Instanz der ImageDocument Klasse zur Verfügung, die wir oben erstellt haben:

private ImageDocument _document;
...

[Export ("Document")]
public ImageDocument Document {
    get { return _document; }
    set {
        WillChangeValue ("Document");
        _document = value;
        DidChangeValue ("Document");
    }
}

ExportMithilfe von Und WillChangeValue und DidChangeValue, haben wir die Document Eigenschaft so eingerichtet, dass Key-Value Coding und Data Binding in Xcode zulässig sind.

Außerdem machen wir das Bild aus dem Bild gut verfügbar, das wir unserer Benutzeroberfläche in Xcode mit der folgenden Eigenschaft hinzugefügt haben:

public ViewController ImageViewController {
    get { return ContentViewController as ViewController; }
}

public NSImage Image {
    get {
        return ImageViewController.Image;
    }
    set {
        ImageViewController.Image = value;
    }
}

Wenn das Hauptfenster geladen und angezeigt wird, erstellen wir eine Instanz unserer ImageDocument Klasse und fügen das Bild der Benutzeroberfläche gut mit dem folgenden Code hinzu:

public override void AwakeFromNib ()
{
    base.AwakeFromNib ();

    // Create a new document instance
    Document = new ImageDocument ();

    // Attach to image view
    Document.ImageView = ImageViewController.ContentView;
}

Als Reaktion auf den Benutzer, der auf die Elemente der Symbolleiste zum Kopieren und Einfügen klickt, rufen wir die Instanz der ImageDocument Klasse auf, um die eigentliche Arbeit zu erledigen:

partial void CopyImage (NSObject sender) {
    Document.CopyImage(sender);
}

partial void PasteImage (Foundation.NSObject sender) {
    Document.PasteImage(sender);
}

Aktivieren der Menüs "Datei" und "Bearbeiten"

Als Letztes müssen wir das Menüelement "Neu" über das Menü "Datei" aktivieren (um neue Instanzen unseres Standard Fensters zu erstellen) und die Menüelemente "Ausschneiden", "Kopieren" und "Einfügen" über das Menü "Bearbeiten" zu aktivieren.

Um das Menüelement "Neu " zu aktivieren, bearbeiten Sie die AppDelegate.cs Datei, und fügen Sie den folgenden Code hinzu:

public int UntitledWindowCount { get; set;} =1;
...

[Export ("newDocument:")]
void NewDocument (NSObject sender) {
    // Get new window
    var storyboard = NSStoryboard.FromName ("Main", null);
    var controller = storyboard.InstantiateControllerWithIdentifier ("MainWindow") as NSWindowController;

    // Display
    controller.ShowWindow(this);

    // Set the title
    controller.Window.Title = (++UntitledWindowCount == 1) ? "untitled" : string.Format ("untitled {0}", UntitledWindowCount);
}

Weitere Informationen finden Sie im Abschnitt "Arbeiten mit mehreren Windows" in unserer Windows-Dokumentation .

Um die Menüelemente "Ausschneiden", "Kopieren " und "Einfügen " zu aktivieren, bearbeiten Sie die AppDelegate.cs Datei, und fügen Sie den folgenden Code hinzu:

[Export("copy:")]
void CopyImage (NSObject sender)
{
    // Get the main window
    var window = NSApplication.SharedApplication.KeyWindow as ImageWindow;

    // Anything to do?
    if (window == null)
        return;

    // Copy the image to the clipboard
    window.Document.CopyImage (sender);
}

[Export("cut:")]
void CutImage (NSObject sender)
{
    // Get the main window
    var window = NSApplication.SharedApplication.KeyWindow as ImageWindow;

    // Anything to do?
    if (window == null)
        return;

    // Copy the image to the clipboard
    window.Document.CopyImage (sender);

    // Clear the existing image
    window.Image = null;
}

[Export("paste:")]
void PasteImage (NSObject sender)
{
    // Get the main window
    var window = NSApplication.SharedApplication.KeyWindow as ImageWindow;

    // Anything to do?
    if (window == null)
        return;

    // Paste the image from the clipboard
    window.Document.PasteImage (sender);
}

Für jedes Menüelement erhalten wir das aktuelle, oberste Schlüsselfenster und wandeln es in unsere ImageWindow Klasse um:

var window = NSApplication.SharedApplication.KeyWindow as ImageWindow;

Von dort aus rufen wir die ImageDocument Klasseninstanz dieses Fensters auf, um die Kopier- und Einfügeaktionen zu behandeln. Zum Beispiel:

window.Document.CopyImage (sender);

Wir möchten nur, dass auf die Menüelemente "Ausschneiden", "Kopieren " und "Einfügen " zugegriffen werden kann, wenn im Standard-Pasteboard oder in der Bildquelle des aktuellen aktiven Fensters Bilddaten vorhanden sind.

Fügen wir dem Xamarin.Mac-Projekt eine EditMenuDelegate.cs Datei hinzu, und sehen sie wie folgt aus:

using System;
using AppKit;

namespace MacCopyPaste
{
    public class EditMenuDelegate : NSMenuDelegate
    {
        #region Override Methods
        public override void MenuWillHighlightItem (NSMenu menu, NSMenuItem item)
        {
        }

        public override void NeedsUpdate (NSMenu menu)
        {
            // Get list of menu items
            NSMenuItem[] Items = menu.ItemArray ();

            // Get the key window and determine if the required images are available
            var window = NSApplication.SharedApplication.KeyWindow as ImageWindow;
            var hasImage = (window != null) && (window.Image != null);
            var hasImageOnPasteboard = (window != null) && window.Document.ImageAvailableOnPasteboard;

            // Process every item in the menu
            foreach(NSMenuItem item in Items) {
                // Take action based on the menu title
                switch (item.Title) {
                case "Cut":
                case "Copy":
                case "Delete":
                    // Only enable if there is an image in the view
                    item.Enabled = hasImage;
                    break;
                case "Paste":
                    // Only enable if there is an image on the pasteboard
                    item.Enabled = hasImageOnPasteboard;
                    break;
                default:
                    // Only enable the item if it has a sub menu
                    item.Enabled = item.HasSubmenu;
                    break;
                }
            }
        }
        #endregion
    }
}

Auch hier wird das aktuelle Fenster ganz oben abgerufen und die ImageDocument Klasseninstanz verwendet, um festzustellen, ob die erforderlichen Bilddaten vorhanden sind. Anschließend verwenden wir die MenuWillHighlightItem Methode, um jedes Element basierend auf diesem Zustand zu aktivieren oder zu deaktivieren.

Bearbeiten Sie die AppDelegate.cs Datei, und stellen Sie sicher, dass die DidFinishLaunching Methode wie folgt aussieht:

public override void DidFinishLaunching (NSNotification notification)
{
    // Disable automatic item enabling on the Edit menu
    EditMenu.AutoEnablesItems = false;
    EditMenu.Delegate = new EditMenuDelegate ();
}

Zunächst deaktivieren wir die automatische Aktivierung und Deaktivierung von Menüelementen im Menü "Bearbeiten". Als Nächstes fügen wir eine Instanz der EditMenuDelegate Klasse an, die wir oben erstellt haben.

Weitere Informationen finden Sie in unserer Menüdokumentation .

Testen der App

Mit allem, was vorhanden ist, sind wir bereit, die Anwendung zu testen. Erstellen sie die App, und führen Sie sie aus, und die Standard-Schnittstelle wird angezeigt:

Running the application

Wenn Sie das Menü "Bearbeiten" öffnen, beachten Sie, dass "Ausschneiden", "Kopieren" und "Einfügen" deaktiviert sind, da es kein Bild im Bild oder im Standard-Einfügebrett gibt:

Opening the Edit menu

Wenn Sie dem Bild ein Bild hinzufügen und das Menü "Bearbeiten" erneut öffnen, werden die Elemente jetzt aktiviert:

Showing the Edit menu items are enabled

Wenn Sie das Bild kopieren und im Menü "Datei" "Neu" auswählen, können Sie dieses Bild in das neue Fenster einfügen:

Pasting an image into a new window

In den folgenden Abschnitten werden wir einen detaillierten Blick auf die Arbeit mit dem Pasteboard in einer Xamarin.Mac-Anwendung werfen.

Informationen zum Pasteboard

In macOS (früher als OS X bezeichnet) bietet das Pasteboard (NSPasteboard) Unterstützung für mehrere Serverprozesse wie Copy & Paste, Drag & Drop und Application Services. In den folgenden Abschnitten werfen wir einen genaueren Blick auf mehrere Konzepte für das Key Pasteboard.

Was ist ein Pasteboard?

Die NSPasteboard Klasse bietet einen standardisierten Mechanismus für den Austausch von Informationen zwischen Anwendungen oder innerhalb einer bestimmten App. Die Standard Funktion eines Einfügebretts dient zum Behandeln von Kopier- und Einfügevorgängen:

  1. Wenn der Benutzer ein Element in einer App auswählt und das Menüelement "Ausschneiden " oder "Kopieren " verwendet, wird eine oder mehrere Darstellungen des ausgewählten Elements auf dem Einfügebrett platziert.
  2. Wenn der Benutzer das Menüelement "Einfügen " (innerhalb derselben App oder einer anderen App) verwendet, wird die Version der Daten, die er verarbeiten kann, aus dem Pasteboard kopiert und der App hinzugefügt.

Weniger offensichtliche Verwendungen von Pasteboards umfassen Such-, Drag-, Drag- und Drop-Vorgänge und Anwendungsdienste:

  • Wenn der Benutzer einen Ziehvorgang initiiert, werden die Ziehdaten in das Pasteboard kopiert. Wenn der Ziehvorgang mit einem Drop in einer anderen App endet, kopiert diese App die Daten aus dem Pasteboard.
  • Für Übersetzungsdienste werden die zu übersetzenden Daten von der anfordernden App in das Einfügebrett kopiert. Der Anwendungsdienst ruft die Daten aus dem Pasteboard ab, führt die Übersetzung durch und fügt die Daten dann wieder in das Einfügebrett ein.

In ihrer einfachsten Form werden Pasteboards verwendet, um Daten innerhalb einer bestimmten App oder zwischen Apps zu verschieben und hierfür in einem speziellen globalen Speicherbereich außerhalb des App-Prozesses zu existieren. Während die Konzepte der Pasteboards leicht zu erfassen sind, gibt es mehrere komplexere Details, die berücksichtigt werden müssen. Diese werden unten ausführlich behandelt.

Benannte Pasteboards

Ein Pasteboard kann öffentlich oder privat sein und kann für eine Vielzahl von Zwecken innerhalb einer Anwendung oder zwischen mehreren Apps verwendet werden. macOS bietet mehrere Standard-Pasteboards, die jeweils eine bestimmte, gut definierte Verwendung haben:

  • NSGeneralPboard - Das Standard-Pasteboard für Ausschneiden-, Kopieren - und Einfügevorgänge .
  • NSRulerPboard – Unterstützt Die Vorgänge "Ausschneiden", "Kopieren " und "Einfügen " für Lineale.
  • NSFontPboard – Unterstützt Ausschneiden-, Kopieren - und Einfügevorgänge für NSFont Objekte.
  • NSFindPboard – Unterstützt anwendungsspezifische Suchbereiche, die Suchtext freigeben können.
  • NSDragPboard - Unterstützt Drag &Drop-Vorgänge .

In den meisten Fällen verwenden Sie eines der vom System definierten Pasteboards. Es kann jedoch Situationen geben, in denen Sie eigene Pasteboards erstellen müssen. In diesen Situationen können Sie die FromName (string name) Methode der NSPasteboard Klasse verwenden, um ein benutzerdefiniertes Pasteboard mit dem angegebenen Namen zu erstellen.

Optional können Sie die CreateWithUniqueName Methode der NSPasteboard Klasse aufrufen, um ein eindeutig benanntes Pasteboard zu erstellen.

Einfügen von Elementen

Jede Datenmenge, die eine Anwendung in ein Einfügebrett schreibt, wird als Pasteboardelement betrachtet, und ein Einfügebrett kann mehrere Elemente gleichzeitig enthalten. Auf diese Weise kann eine App mehrere Versionen der Daten schreiben, die in ein Einfügebrett kopiert werden (z. B. Nur-Text und formatierter Text), und die abgerufene App kann nur die Daten lesen, die sie verarbeiten kann (z. B. nur den Nur-Text).

Datendarstellungen und Uniform-Typ-IDs

Pasteboard-Vorgänge erfolgen in der Regel zwischen zwei (oder mehr) Anwendungen, die keine Kenntnisse übereinander oder die Datentypen haben, die jeweils behandelt werden können. Wie im vorstehenden Abschnitt beschrieben, kann ein Einfügebrett mehrere Darstellungen der kopierten und eingefügten Daten enthalten, um das Potenzial für die Freigabe von Informationen zu maximieren.

Jede Darstellung wird über einen Uniform Type Identifier (UTI) identifiziert, bei dem es sich nicht mehr um eine einfache Zeichenfolge handelt, die den Typ des präsentierten Datums eindeutig identifiziert (weitere Informationen finden Sie in der Dokumentation zur Übersicht über die Uniform Type Identifiers von Apple).

Wenn Sie einen benutzerdefinierten Datentyp erstellen (z. B. ein Zeichnungsobjekt in einer Vektorzeichnungs-App), können Sie eine eigene UTI erstellen, um ihn in Kopier- und Einfügevorgängen eindeutig zu identifizieren.

Wenn eine App auf das Einfügen von Daten vorbereitet, die aus einem Pasteboard kopiert wurden, muss sie die Darstellung finden, die ihren Fähigkeiten am besten entspricht (sofern vorhanden). In der Regel ist dies der reichste verfügbare Typ (z. B. formatierter Text für eine Textverarbeitungs-App), der auf die einfachsten verfügbaren Formulare zurückfällt (Nur-Text für einen einfachen Text-Editor).

Zugesagte Daten

Im Allgemeinen sollten Sie so viele Darstellungen der Daten bereitstellen, die so kopiert werden, wie möglich, um die Freigabe zwischen Apps zu maximieren. Aufgrund von Zeit- oder Speichereinschränkungen kann es jedoch unpraktisch sein, jeden Datentyp tatsächlich in das Pasteboard zu schreiben.

In diesem Fall können Sie die erste Datendarstellung auf dem Pasteboard platzieren, und die empfangende App kann eine andere Darstellung anfordern, die direkt vor dem Einfügevorgang generiert werden kann.

Wenn Sie das ursprüngliche Element im Pasteboard platzieren, geben Sie an, dass eine oder mehrere der anderen verfügbaren Darstellungen von einem Objekt bereitgestellt werden, das der NSPasteboardItemDataProvider Schnittstelle entspricht. Diese Objekte stellen die zusätzlichen Darstellungen bei Bedarf bereit, wie sie von der empfangenden App angefordert werden.

Anzahl ändern

Jedes Pasteboard Standard enthält eine Änderungsanzahl, die jedes Mal erhöht wird, wenn ein neuer Besitzer deklariert wird. Eine App kann ermitteln, ob sich der Inhalt des Pasteboards seit dem letzten Mal geändert hat, indem er den Wert der Änderungsanzahl überprüft.

Verwenden Sie die ChangeCount Methoden und ClearContents Methoden der NSPasteboard Klasse, um die Änderungsanzahl eines bestimmten Pasteboards zu ändern.

Kopieren von Daten in ein Einfügebrett

Sie führen einen Kopiervorgang aus, indem Sie zuerst auf ein Einfügebrett zugreifen und vorhandene Inhalte löschen und so viele Darstellungen der Daten schreiben, wie für das Pasteboard erforderlich sind.

Zum Beispiel:

// Get the standard pasteboard
var pasteboard = NSPasteboard.GeneralPasteboard;

// Empty the current contents
pasteboard.ClearContents();

// Add the current image to the pasteboard
pasteboard.WriteObjects (new NSImage[] {image});

In der Regel schreiben Sie einfach in das allgemeine Einfügebrett, wie im obigen Beispiel getan. Jedes Objekt, das Sie an die WriteObjects Methode senden, muss der INSPasteboardWriting Schnittstelle entsprechen. Mehrere integrierte Klassen (zNSString. B. , , NSImage, NSURL, NSColorund NSAttributedStringNSPasteboardItem) entsprechen automatisch dieser Schnittstelle.

Wenn Sie eine benutzerdefinierte Datenklasse in das Pasteboard schreiben, muss sie der INSPasteboardWriting Schnittstelle entsprechen oder in eine Instanz der NSPasteboardItem Klasse eingeschlossen werden (siehe abschnitt "Benutzerdefinierte Datentypen ").

Lesen von Daten aus einem Pasteboard

Wie oben erwähnt, können mehrere Darstellungen der kopierten Daten in das Pasteboard geschrieben werden, um das Potenzial für die Gemeinsame Nutzung von Daten zwischen Apps zu maximieren. Es liegt an der empfangenden App, die größtmögliche Version für ihre Funktionen auszuwählen (sofern vorhanden).

Einfacher Einfügevorgang

Sie lesen Daten aus dem Pasteboard mithilfe der ReadObjectsForClasses Methode. Es sind zwei Parameter erforderlich:

  1. Ein Array von NSObject basierten Klassentypen, die Sie aus dem Pasteboard lesen möchten. Sie sollten dies zuerst mit dem am häufigsten gewünschten Datentyp sortieren, wobei alle Typen erneut Standard bei abnehmender Einstellung angezeigt werden.
  2. Ein Wörterbuch mit zusätzlichen Einschränkungen (z. B. das Einschränken auf bestimmte URL-Inhaltstypen) oder ein leeres Wörterbuch, wenn keine weiteren Einschränkungen erforderlich sind.

Die Methode gibt ein Array von Elementen zurück, die den kriterien entsprechen, die wir übergeben haben, und enthält daher höchstens die gleiche Anzahl von Datentypen, die angefordert werden. Es ist auch möglich, dass keine der angeforderten Typen vorhanden ist und ein leeres Array zurückgegeben wird.

Mit dem folgenden Code wird z. B. überprüft, ob im allgemeinen Pasteboard ein NSImage vorhandenes Element vorhanden ist, und zeigt es in einem Bild gut an, wenn dies der Fall ist:

[Export("PasteImage:")]
public void PasteImage(NSObject sender) {

    // Initialize the pasteboard
    NSPasteboard pasteboard = NSPasteboard.GeneralPasteboard;
    Class [] classArray  = { new Class ("NSImage") };

    bool ok = pasteboard.CanReadObjectForClasses (classArray, null);
    if (ok) {
        // Read the image off of the pasteboard
        NSObject [] objectsToPaste = pasteboard.ReadObjectsForClasses (classArray, null);
        NSImage image = (NSImage)objectsToPaste[0];

        // Display the new image
        ImageView.Image = image;
    }

    Class [] classArray2 = { new Class ("ImageInfo") };
    ok = pasteboard.CanReadObjectForClasses (classArray2, null);
    if (ok) {
        // Read the image off of the pasteboard
        NSObject [] objectsToPaste = pasteboard.ReadObjectsForClasses (classArray2, null);
        ImageInfo info = (ImageInfo)objectsToPaste[0];
            }
}

Anfordern mehrerer Datentypen

Basierend auf dem Typ der zu erstellenden Xamarin.Mac-Anwendung kann es in der Lage sein, mehrere Darstellungen der eingefügten Daten zu verarbeiten. In dieser Situation gibt es zwei Szenarien zum Abrufen von Daten aus dem Pasteboard:

  1. Führen Sie einen einzelnen Aufruf der ReadObjectsForClasses Methode durch, und stellen Sie ein Array aller gewünschten Darstellungen bereit (in der bevorzugten Reihenfolge).
  2. Führen Sie mehrere Aufrufe an die ReadObjectsForClasses Methode durch, die jedes Mal ein anderes Array von Typen anfordert.

Weitere Informationen zum Abrufen von Daten aus einem Pasteboard finden Sie oben im Abschnitt "Einfacher Einfügevorgang ".

Überprüfen auf vorhandene Datentypen

Es gibt Zeiten, in denen Sie überprüfen möchten, ob ein Einfügebrett eine bestimmte Datendarstellung enthält, ohne die Daten tatsächlich aus dem Pasteboard zu lesen (z. B. das Aktivieren des Menüelements "Einfügen ", wenn gültige Daten vorhanden sind).

Rufen Sie die CanReadObjectForClasses Methode des Pasteboards auf, um festzustellen, ob sie einen bestimmten Typ enthält.

Der folgende Code bestimmt beispielsweise, ob das allgemeine Pasteboard eine NSImage Instanz enthält:

public bool ImageAvailableOnPasteboard {
    get {
        // Initialize the pasteboard
        NSPasteboard pasteboard = NSPasteboard.GeneralPasteboard;
        Class [] classArray  = { new Class ("NSImage") };

        // Check to see if an image is on the pasteboard
        return pasteboard.CanReadObjectForClasses (classArray, null);
    }
}

Lese-URLs aus dem Pasteboard

Basierend auf der Funktion einer bestimmten Xamarin.Mac-App kann es erforderlich sein, URLs aus einem Pasteboard zu lesen, aber nur, wenn sie eine bestimmte Gruppe von Kriterien erfüllen (z. B. verweisen auf Dateien oder URLs eines bestimmten Datentyps). In diesem Fall können Sie zusätzliche Suchkriterien mithilfe des zweiten Parameters oder der CanReadObjectForClassesReadObjectsForClasses Methoden angeben.

Benutzerdefinierte Datentypen

Es gibt Zeiten, in denen Sie Ihre eigenen benutzerdefinierten Typen aus einer Xamarin.Mac-App auf dem Pasteboard speichern müssen. Beispielsweise eine Vektorzeichnungs-App, mit der der Benutzer Zeichnungsobjekte kopieren und einfügen kann.

In diesem Fall müssen Sie Ihre benutzerdefinierte Datenklasse so entwerfen, dass sie von diesen erbt NSObject und mit einigen Schnittstellen (INSCodingINSPasteboardWritingund INSPasteboardReading) übereinstimmt. Optional können Sie eine NSPasteboardItem zum Kapseln der zu kopierenden oder eingefügten Daten verwenden.

Beide Optionen werden unten ausführlich behandelt.

Verwenden einer benutzerdefinierten Klasse

In diesem Abschnitt erweitern wir die einfache Beispiel-App, die wir zu Beginn dieses Dokuments erstellt haben, und fügen eine benutzerdefinierte Klasse hinzu, um Informationen zum Bild nachzuverfolgen, das wir zwischen Fenstern kopieren und einfügen.

Fügen Sie dem Projekt eine neue Klasse hinzu, und rufen Sie sie ImageInfo.cs auf. Bearbeiten Sie die Datei, und stellen Sie sicher, dass sie wie folgt aussieht:

using System;
using AppKit;
using Foundation;

namespace MacCopyPaste
{
    [Register("ImageInfo")]
    public class ImageInfo : NSObject, INSCoding, INSPasteboardWriting, INSPasteboardReading
    {
        #region Computed Properties
        [Export("name")]
        public string Name { get; set; }

        [Export("imageType")]
        public string ImageType { get; set; }
        #endregion

        #region Constructors
        [Export ("init")]
        public ImageInfo ()
        {
        }
        
        public ImageInfo (IntPtr p) : base (p)
        {
        }

        [Export ("initWithCoder:")]
        public ImageInfo(NSCoder decoder) {

            // Decode data
            NSString name = decoder.DecodeObject("name") as NSString;
            NSString type = decoder.DecodeObject("imageType") as NSString;

            // Save data
            Name = name.ToString();
            ImageType = type.ToString ();
        }
        #endregion

        #region Public Methods
        [Export ("encodeWithCoder:")]
        public void EncodeTo (NSCoder encoder) {

            // Encode data
            encoder.Encode(new NSString(Name),"name");
            encoder.Encode(new NSString(ImageType),"imageType");
        }

        [Export ("writableTypesForPasteboard:")]
        public virtual string[] GetWritableTypesForPasteboard (NSPasteboard pasteboard) {
            string[] writableTypes = {"com.xamarin.image-info", "public.text"};
            return writableTypes;
        }

        [Export ("pasteboardPropertyListForType:")]
        public virtual NSObject GetPasteboardPropertyListForType (string type) {

            // Take action based on the requested type
            switch (type) {
            case "com.xamarin.image-info":
                return NSKeyedArchiver.ArchivedDataWithRootObject(this);
            case "public.text":
                return new NSString(string.Format("{0}.{1}", Name, ImageType));
            }

            // Failure, return null
            return null;
        }

        [Export ("readableTypesForPasteboard:")]
        public static string[] GetReadableTypesForPasteboard (NSPasteboard pasteboard){
            string[] readableTypes = {"com.xamarin.image-info", "public.text"};
            return readableTypes;
        }

        [Export ("readingOptionsForType:pasteboard:")]
        public static NSPasteboardReadingOptions GetReadingOptionsForType (string type, NSPasteboard pasteboard) {

            // Take action based on the requested type
            switch (type) {
            case "com.xamarin.image-info":
                return NSPasteboardReadingOptions.AsKeyedArchive;
            case "public.text":
                return NSPasteboardReadingOptions.AsString;
            }

            // Default to property list
            return NSPasteboardReadingOptions.AsPropertyList;
        }

        [Export ("initWithPasteboardPropertyList:ofType:")]
        public NSObject InitWithPasteboardPropertyList (NSObject propertyList, string type) {

            // Take action based on the requested type
            switch (type) {
            case "com.xamarin.image-info":
                return new ImageInfo();
            case "public.text":
                return new ImageInfo();
            }

            // Failure, return null
            return null;
        }
        #endregion
    }
}
    

In den folgenden Abschnitten werfen wir einen detaillierten Blick auf diese Klasse.

Vererbung und Schnittstellen

Bevor eine benutzerdefinierte Datenklasse in ein Einfügebrett geschrieben oder gelesen werden kann, muss sie den INSPastebaordWriting Schnittstellen entsprechen INSPasteboardReading . Darüber hinaus muss sie von NSObject der INSCoding Schnittstelle erben und auch entsprechen:

[Register("ImageInfo")]
public class ImageInfo : NSObject, INSCoding, INSPasteboardWriting, INSPasteboardReading
...

Die Klasse muss auch für Objective-C die Verwendung der Register Direktive verfügbar gemacht werden, und sie muss alle erforderlichen Eigenschaften oder Methoden mit verfügbar Exportmachen. Zum Beispiel:

[Export("name")]
public string Name { get; set; }

[Export("imageType")]
public string ImageType { get; set; }

Wir stellen die beiden Datenfelder offen, die diese Klasse enthalten soll – den Namen und den Typ des Bilds (jpg, png usw.).

Weitere Informationen finden Sie in den Verfügbarmachen von C#-Klassen /-Methoden im Objective-C Abschnitt der Xamarin.Mac Internals-Dokumentation . Außerdem werden die Register C#-Klassen und Export Attribute erläutert, die zum Verbinden Ihrer C#-Klassen mit Objective-C Objekten und UI-Elementen verwendet werden.

Konstruktoren

Für unsere benutzerdefinierte Datenklasse sind zwei Konstruktoren (ordnungsgemäß verfügbar Objective-Cgemacht) erforderlich, damit sie aus einem Pasteboard gelesen werden können:

[Export ("init")]
public ImageInfo ()
{
}

[Export ("initWithCoder:")]
public ImageInfo(NSCoder decoder) {

    // Decode data
    NSString name = decoder.DecodeObject("name") as NSString;
    NSString type = decoder.DecodeObject("imageType") as NSString;

    // Save data
    Name = name.ToString();
    ImageType = type.ToString ();
}

Zunächst stellen wir den leeren Konstruktor unter der Standardmethode von .initObjective-C

Als Nächstes machen wir einen NSCoding kompatiblen Konstruktor verfügbar, der zum Erstellen einer neuen Instanz des Objekts aus dem Pasteboard verwendet wird, wenn er unter dem exportierten Namen initWithCodereingefügt wird.

Dieser Konstruktor extrahiert die NSCoder Schlüssel-Wert-Paardaten (wie von einem NSKeyedArchiver in das Pasteboard geschriebenen), und speichert ihn in den Eigenschaftenfeldern der Datenklasse.

Schreiben in das Pasteboard

Durch die Einhaltung der INSPasteboardWriting Schnittstelle müssen wir zwei Methoden und optional eine dritte Methode verfügbar machen, damit die Klasse in das Pasteboard geschrieben werden kann.

Zunächst müssen wir dem Pasteboard mitteilen, in welche Datentypdarstellungen die benutzerdefinierte Klasse geschrieben werden kann:

[Export ("writableTypesForPasteboard:")]
public virtual string[] GetWritableTypesForPasteboard (NSPasteboard pasteboard) {
    string[] writableTypes = {"com.xamarin.image-info", "public.text"};
    return writableTypes;
}

Jede Darstellung wird über einen Uniform Type Identifier (UTI) identifiziert, bei dem es sich nicht mehr um eine einfache Zeichenfolge handelt, die den Typ der präsentierten Daten eindeutig identifiziert (weitere Informationen finden Sie in der Dokumentation zur Übersicht über die Uniform Type Identifiers von Apple).

Für unser benutzerdefiniertes Format erstellen wir unsere eigene UTI: "com.xamarin.image-info" (beachten Sie, dass die Schreibweise umgekehrt ist wie ein App-Bezeichner). Unsere Klasse ist auch in der Lage, eine Standardzeichenfolge in das Pasteboard (public.text) zu schreiben.

Als Nächstes müssen wir das Objekt im angeforderten Format erstellen, das tatsächlich in das Pasteboard geschrieben wird:

[Export ("pasteboardPropertyListForType:")]
public virtual NSObject GetPasteboardPropertyListForType (string type) {

    // Take action based on the requested type
    switch (type) {
    case "com.xamarin.image-info":
        return NSKeyedArchiver.ArchivedDataWithRootObject(this);
    case "public.text":
        return new NSString(string.Format("{0}.{1}", Name, ImageType));
    }

    // Failure, return null
    return null;
}

Für den public.text Typ geben wir ein einfaches formatiertes NSString Objekt zurück. Für den benutzerdefinierten Typ verwenden wir eine NSKeyedArchiver und die NSCoder Schnittstelle, um die benutzerdefinierte com.xamarin.image-info Datenklasse in ein Schlüssel-Wert-Paararchiv zu codieren. Wir müssen die folgende Methode implementieren, um die Codierung tatsächlich zu verarbeiten:

[Export ("encodeWithCoder:")]
public void EncodeTo (NSCoder encoder) {

    // Encode data
    encoder.Encode(new NSString(Name),"name");
    encoder.Encode(new NSString(ImageType),"imageType");
}

Die einzelnen Schlüssel-Wert-Paare werden in den Encoder geschrieben und mit dem zweiten Konstruktor decodiert, den wir oben hinzugefügt haben.

Optional können wir die folgende Methode einschließen, um optionen beim Schreiben von Daten in das Pasteboard zu definieren:

[Export ("writingOptionsForType:pasteboard:"), CompilerGenerated]
public virtual NSPasteboardWritingOptions GetWritingOptionsForType (string type, NSPasteboard pasteboard) {
    return NSPasteboardWritingOptions.WritingPromised;
}

Derzeit ist nur die WritingPromised Option verfügbar und sollte verwendet werden, wenn nur ein bestimmter Typ versprochen und nicht tatsächlich in das Pasteboard geschrieben wird. Weitere Informationen finden Sie im Abschnitt "Zugesagte Daten " weiter oben.

Mit diesen Methoden kann der folgende Code verwendet werden, um unsere benutzerdefinierte Klasse in das Pasteboard zu schreiben:

// Get the standard pasteboard
var pasteboard = NSPasteboard.GeneralPasteboard;

// Empty the current contents
pasteboard.ClearContents();

// Add info to the pasteboard
pasteboard.WriteObjects (new ImageInfo[] { Info });

Lesen aus dem Pasteboard

Durch die Einhaltung der INSPasteboardReading Schnittstelle müssen wir drei Methoden verfügbar machen, damit die benutzerdefinierte Datenklasse aus dem Pasteboard gelesen werden kann.

Zunächst müssen wir dem Pasteboard mitteilen, welche Datentypdarstellungen die benutzerdefinierte Klasse aus der Zwischenablage lesen kann:

[Export ("readableTypesForPasteboard:")]
public static string[] GetReadableTypesForPasteboard (NSPasteboard pasteboard){
    string[] readableTypes = {"com.xamarin.image-info", "public.text"};
    return readableTypes;
}

Auch hier werden diese als einfache UTIs definiert und sind dieselben Typen, die wir im Abschnitt "Schreiben in das Pasteboard " oben definiert haben.

Als Nächstes müssen wir dem Pasteboard mitteilen, wie die einzelnen UTI-Typen mit der folgenden Methode gelesen werden:

[Export ("readingOptionsForType:pasteboard:")]
public static NSPasteboardReadingOptions GetReadingOptionsForType (string type, NSPasteboard pasteboard) {

    // Take action based on the requested type
    switch (type) {
    case "com.xamarin.image-info":
        return NSPasteboardReadingOptions.AsKeyedArchive;
    case "public.text":
        return NSPasteboardReadingOptions.AsString;
    }

    // Default to property list
    return NSPasteboardReadingOptions.AsPropertyList;
}

Für den com.xamarin.image-info Typ wird dem Pasteboard mitgeteilt, das Schlüssel-Wert-Paar zu decodieren, das wir beim NSKeyedArchiver Schreiben der Klasse in das Pasteboard erstellt haben, indem wir den Konstruktor aufrufen, den initWithCoder: wir der Klasse hinzugefügt haben.

Schließlich müssen wir die folgende Methode hinzufügen, um die anderen UTI-Datendarstellungen aus dem Pasteboard zu lesen:

[Export ("initWithPasteboardPropertyList:ofType:")]
public NSObject InitWithPasteboardPropertyList (NSObject propertyList, string type) {

    // Take action based on the requested type
    switch (type) {
    case "public.text":
        return new ImageInfo();
    }

    // Failure, return null
    return null;
}

Mit all diesen Methoden kann die benutzerdefinierte Datenklasse mithilfe des folgenden Codes aus dem Pasteboard gelesen werden:

// Initialize the pasteboard
NSPasteboard pasteboard = NSPasteboard.GeneralPasteboard;
var classArrayPtrs = new [] { Class.GetHandle (typeof(ImageInfo)) };
NSArray classArray = NSArray.FromIntPtrs (classArrayPtrs);

// NOTE: Sending messages directly to the base Objective-C API because of this defect:
// https://bugzilla.xamarin.com/show_bug.cgi?id=31760
// Check to see if image info is on the pasteboard
ok = bool_objc_msgSend_IntPtr_IntPtr (pasteboard.Handle, Selector.GetHandle ("canReadObjectForClasses:options:"), classArray.Handle, IntPtr.Zero);

if (ok) {
    // Read the image off of the pasteboard
    NSObject [] objectsToPaste = NSArray.ArrayFromHandle<Foundation.NSObject>(IntPtr_objc_msgSend_IntPtr_IntPtr (pasteboard.Handle, Selector.GetHandle ("readObjectsForClasses:options:"), classArray.Handle, IntPtr.Zero));
    ImageInfo info = (ImageInfo)objectsToPaste[0];
}

Verwenden eines NSPasteboardItem

Es kann vorkommen, dass Sie benutzerdefinierte Elemente in das Einfügebrett schreiben müssen, das nicht die Erstellung einer benutzerdefinierten Klasse garantiert oder Sie Daten in einem gängigen Format bereitstellen möchten, nur nach Bedarf. In diesen Situationen können Sie eine NSPasteboardItem.

Eine NSPasteboardItem bietet eine fein abgestimmte Kontrolle über die Daten, die in das Einfügebrett geschrieben werden und für den temporären Zugriff konzipiert sind – es sollte gelöscht werden, nachdem sie in das Pasteboard geschrieben wurde.

Schreiben von Daten

Wenn Sie Ihre benutzerdefinierten Daten in ein NSPasteboardItem Benutzerdefiniertes Schreiben schreiben möchten, müssen Sie eine benutzerdefinierte NSPasteboardItemDataProvider. Fügen Sie dem Projekt eine neue Klasse hinzu, und rufen Sie sie ImageInfoDataProvider.cs auf. Bearbeiten Sie die Datei, und stellen Sie sicher, dass sie wie folgt aussieht:

using System;
using AppKit;
using Foundation;

namespace MacCopyPaste
{
    [Register("ImageInfoDataProvider")]
    public class ImageInfoDataProvider : NSPasteboardItemDataProvider
    {
        #region Computed Properties
        public string Name { get; set;}
        public string ImageType { get; set;}
        #endregion

        #region Constructors
        [Export ("init")]
        public ImageInfoDataProvider ()
        {
        }

        public ImageInfoDataProvider (string name, string imageType)
        {
            // Initialize
            this.Name = name;
            this.ImageType = imageType;
        }

        protected ImageInfoDataProvider (NSObjectFlag t){
        }

        protected internal ImageInfoDataProvider (IntPtr handle){

        }
        #endregion

        #region Override Methods
        [Export ("pasteboardFinishedWithDataProvider:")]
        public override void FinishedWithDataProvider (NSPasteboard pasteboard)
        {
            
        }

        [Export ("pasteboard:item:provideDataForType:")]
        public override void ProvideDataForType (NSPasteboard pasteboard, NSPasteboardItem item, string type)
        {

            // Take action based on the type
            switch (type) {
            case "public.text":
                // Encode the data to string 
                item.SetStringForType(string.Format("{0}.{1}", Name, ImageType),type);
                break;
            }

        }
        #endregion
    }
}

Wie bei der benutzerdefinierten Datenklasse müssen wir diese und Export die Register Direktiven verwenden, um sie verfügbar zu Objective-Cmachen. Die Klasse muss von der Klasse erben NSPasteboardItemDataProvider und die FinishedWithDataProvider Methoden ProvideDataForType implementieren.

Verwenden Sie die ProvideDataForType Methode, um die Daten bereitzustellen, die NSPasteboardItem wie folgt umschlossen werden:

[Export ("pasteboard:item:provideDataForType:")]
public override void ProvideDataForType (NSPasteboard pasteboard, NSPasteboardItem item, string type)
{

    // Take action based on the type
    switch (type) {
    case "public.text":
        // Encode the data to string 
        item.SetStringForType(string.Format("{0}.{1}", Name, ImageType),type);
        break;
    }

}

In diesem Fall speichern wir zwei Informationen zu unserem Bild (Name und ImageType) und schreiben diese in eine einfache Zeichenfolge (public.text).

Geben Sie die Daten in das Einfügebrett ein, verwenden Sie den folgenden Code:

// Get the standard pasteboard
var pasteboard = NSPasteboard.GeneralPasteboard;

// Using a Pasteboard Item
NSPasteboardItem item = new NSPasteboardItem();
string[] writableTypes = {"public.text"};

// Add a data provider to the item
ImageInfoDataProvider dataProvider = new ImageInfoDataProvider (Info.Name, Info.ImageType);
var ok = item.SetDataProviderForTypes (dataProvider, writableTypes);

// Save to pasteboard
if (ok) {
    pasteboard.WriteObjects (new NSPasteboardItem[] { item });
}

Lesen von Daten

Verwenden Sie den folgenden Code, um die Daten aus dem Pasteboard zurückzulesen:

// Initialize the pasteboard
NSPasteboard pasteboard = NSPasteboard.GeneralPasteboard;
Class [] classArray  = { new Class ("NSImage") };

bool ok = pasteboard.CanReadObjectForClasses (classArray, null);
if (ok) {
    // Read the image off of the pasteboard
    NSObject [] objectsToPaste = pasteboard.ReadObjectsForClasses (classArray, null);
    NSImage image = (NSImage)objectsToPaste[0];

    // Do something with data
    ...
}
            
Class [] classArray2 = { new Class ("ImageInfo") };
ok = pasteboard.CanReadObjectForClasses (classArray2, null);
if (ok) {
    // Read the image off of the pasteboard
    NSObject [] objectsToPaste = pasteboard.ReadObjectsForClasses (classArray2, null);
    
    // Do something with data
    ...
}

Zusammenfassung

Dieser Artikel hat einen detaillierten Blick auf die Arbeit mit dem Pasteboard in einer Xamarin.Mac-Anwendung zur Unterstützung von Kopier- und Einfügevorgängen gemacht. Zunächst wurde ein einfaches Beispiel eingeführt, um Sie mit standardmäßigen Pasteboards-Vorgängen vertraut zu machen. Als Nächstes hat es einen detaillierten Blick auf das Einfügebrett und das Lesen und Schreiben von Daten daraus gedauert. Schließlich wurde die Verwendung eines benutzerdefinierten Datentyps untersucht, um das Kopieren und Einfügen komplexer Datentypen in einer App zu unterstützen.