Share via


Copier et coller dans Xamarin.Mac

Cet article traite de l’utilisation du collage pour fournir une copie et un collage dans une application Xamarin.Mac. Il montre comment utiliser des types de données standard qui peuvent être partagés entre plusieurs applications et comment prendre en charge des données personnalisées au sein d’une application donnée.

Vue d’ensemble

Lorsque vous utilisez C# et .NET dans une application Xamarin.Mac, vous avez accès à la même prise en charge du collage (copie et collage) qu’un développeur travaillant.Objective-C

Dans cet article, nous allons couvrir les deux principales façons d’utiliser le collage dans une application Xamarin.Mac :

  1. Types de données standard : étant donné que les opérations de collage sont généralement effectuées entre deux applications non liées, aucune application ne connaît les types de données pris en charge par l’autre. Pour optimiser le potentiel de partage, le collage peut contenir plusieurs représentations d’un élément donné (à l’aide d’un ensemble standard de types de données communs), ce qui permet à l’application consommatrice de choisir la version la mieux adaptée à ses besoins.
  2. Données personnalisées : pour prendre en charge la copie et le collage de données complexes au sein de votre Xamarin.Mac, vous pouvez définir un type de données personnalisé qui sera géré par le collage. Par exemple, une application de dessin vectoriel qui permet à l’utilisateur de copier et coller des formes complexes composées de plusieurs types de données et points.

Exemple d’application en cours d’exécution

Dans cet article, nous allons aborder les principes fondamentaux de l’utilisation du collage dans une application Xamarin.Mac pour prendre en charge les opérations de copie et de collage. Il est fortement suggéré que vous travaillez tout d’abord dans l’article Hello, Mac , en particulier les sections Introduction to Xcode and Interface Builder et Outlets and Actions , car elle couvre les concepts et techniques clés que nous utiliserons dans cet article.

Vous pouvez également examiner les classes /méthodes C# exposantes dans Objective-C la section du document interne Xamarin.Mac , ainsi que les RegisterExport attributs utilisés pour connecter vos classes C# à des objets et des Objective-C éléments d’interface utilisateur.

Prise en main du collage

Le collage présente un mécanisme standardisé permettant d’échanger des données au sein d’une application donnée ou entre des applications. L’utilisation classique d’un collage dans une application Xamarin.Mac consiste à gérer les opérations de copie et de collage, mais un certain nombre d’autres opérations sont également prises en charge (par exemple, glisser-déplacer et services d’application).

Pour vous aider rapidement, nous allons commencer par une introduction simple et pratique à l’utilisation de collages dans une application Xamarin.Mac. Plus tard, nous allons fournir une explication détaillée du fonctionnement du collage et des méthodes utilisées.

Pour cet exemple, nous allons créer une application simple basée sur des documents qui gère une fenêtre contenant une vue d’image. L’utilisateur peut copier et coller des images entre des documents de l’application et vers ou depuis d’autres applications ou plusieurs fenêtres à l’intérieur de la même application.

Création du projet Xamarin

Tout d’abord, nous allons créer une application Xamarin.Mac basée sur un document pour laquelle nous ajouterons la prise en charge de la copie et du collage.

Effectuez les actions suivantes :

  1. Démarrez Visual Studio pour Mac et cliquez sur le lien Nouveau projet...

  2. Sélectionnez Application Cacao d’application> Mac>, puis cliquez sur le bouton Suivant :

    Création d’un projet d’application Cocoa

  3. Entrez le nom du projet et conservez MacCopyPaste tout le reste comme valeur par défaut. Cliquez sur Suivant :

    Définition du nom du projet

  4. Cliquez sur le bouton Créer :

    Confirmation des nouveaux paramètres de projet

Ajouter un NSDocument

Ensuite, nous allons ajouter une classe personnalisée NSDocument qui servira de stockage en arrière-plan pour l’interface utilisateur de l’application. Il contient un affichage image unique et sait comment copier une image de l’affichage dans le collage par défaut et comment prendre une image à partir du collage par défaut et l’afficher dans l’affichage image.

Cliquez avec le bouton droit sur le projet Xamarin.Mac dans le panneau Solution, puis sélectionnez Ajouter>un nouveau fichier..

Ajout d’un NSDocument au projet

Entrez ImageDocument comme Nom, puis cliquez sur le bouton Nouveau. Modifiez la classe ImageDocument.cs et faites-la ressembler à ce qui suit :

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

Examinons en détail certains du code ci-dessous.

Le code suivant fournit une propriété permettant de tester l’existence de données d’image sur le collage par défaut, si une image est disponible, true est retournée sinon false:

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

Le code suivant copie une image de la vue d’image jointe dans le collage par défaut :

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

}

Le code suivant colle une image à partir du collage par défaut et l’affiche dans la vue d’image jointe (si le collage contient une image valide) :

[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]
    }
}

Avec ce document en place, nous allons créer l’interface utilisateur de l’application Xamarin.Mac.

Création de l’interface utilisateur

Double-cliquez sur le fichier Main.storyboard pour l’ouvrir dans Xcode. Ensuite, ajoutez une barre d’outils et une image et configurez-les comme suit :

Modification de la barre d’outils

Ajoutez une copie et collez l’élément de barre d’outils Image à gauche de la barre d’outils. Nous allons les utiliser comme raccourcis pour copier et coller à partir du menu Modifier. Ensuite, ajoutez quatre éléments de barre d’outils Image à droite de la barre d’outils. Nous allons les utiliser pour remplir correctement l’image avec certaines images par défaut.

Pour plus d’informations sur l’utilisation des barres d’outils, consultez notre documentation sur les barres d’outils .

Nous allons ensuite exposer les points de sortie et actions suivants pour nos éléments de barre d’outils et l’image bien :

Création de points de sortie et d’actions

Pour plus d’informations sur l’utilisation des points de vente et des actions, consultez la section Points de vente et actions de notre documentation Hello, Mac .

Activation de l’interface utilisateur

Avec notre interface utilisateur créée dans Xcode et notre élément d’interface utilisateur exposé via des points de sortie et des actions, nous devons ajouter le code pour activer l’interface utilisateur. Double-cliquez sur le fichier ImageWindow.cs dans le Panneau Solution et faites-le ressembler à ce qui suit :

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

Examinons ce code en détail ci-dessous.

Tout d’abord, nous exposons une instance de la ImageDocument classe que nous avons créée ci-dessus :

private ImageDocument _document;
...

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

En utilisant Export, WillChangeValue et DidChangeValue, nous avons configuré la Document propriété pour autoriser le codage clé-valeur et la liaison de données dans Xcode.

Nous exposons également l’image à partir de l’image que nous avons ajoutée à notre interface utilisateur dans Xcode avec la propriété suivante :

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

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

Lorsque la fenêtre principale est chargée et affichée, nous créons une instance de notre ImageDocument classe et attachons l’image de l’interface utilisateur correctement à celle-ci avec le code suivant :

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

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

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

Enfin, en réponse à l’utilisateur en cliquant sur les éléments de la barre d’outils copier-coller, nous appelons l’instance de la ImageDocument classe pour effectuer le travail réel :

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

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

Activation des menus Fichier et Édition

La dernière chose à faire est d’activer l’élément de menu Nouveau à partir du menu Fichier (pour créer de nouvelles instances de notre fenêtre principale) et d’activer les éléments de menu Couper, Copier et Coller à partir du menu Modifier .

Pour activer l’élément de menu Nouveau , modifiez le fichier AppDelegate.cs et ajoutez le code suivant :

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

Pour plus d’informations, consultez la section Working with Multiple Windows de notre documentation Windows .

Pour activer les éléments de menu Couper, Copier et Coller , modifiez le fichier AppDelegate.cs et ajoutez le code suivant :

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

Pour chaque élément de menu, nous obtenons la fenêtre actuelle, la plus haute, la fenêtre clé et la castons dans notre ImageWindow classe :

var window = NSApplication.SharedApplication.KeyWindow as ImageWindow;

À partir de là, nous appelons l’instance ImageDocument de classe de cette fenêtre pour gérer les actions de copie et de collage. Par exemple :

window.Document.CopyImage (sender);

Nous voulons uniquement que les éléments de menu Couper, Copier et Coller soient accessibles s’il existe des données d’image sur le collage par défaut ou dans l’image bien de la fenêtre active actuelle.

Nous allons ajouter un fichier EditMenuDelegate.cs au projet Xamarin.Mac et le faire ressembler à ce qui suit :

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

Là encore, nous obtenons la fenêtre actuelle la plus haute et nous utilisons son ImageDocument instance de classe pour voir si les données d’image requises existent. Ensuite, nous utilisons la MenuWillHighlightItem méthode pour activer ou désactiver chaque élément en fonction de cet état.

Modifiez le fichier AppDelegate.cs et faites en sorte que la DidFinishLaunching méthode ressemble à ce qui suit :

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

Tout d’abord, nous désactivons l’activation et la désactivation automatiques des éléments de menu dans le menu Modifier. Ensuite, nous attachons une instance de la EditMenuDelegate classe que nous avons créée ci-dessus.

Pour plus d’informations, consultez notre documentation menus .

Test de l’application

Avec tout ce qui est en place, nous sommes prêts à tester l’application. Générez et exécutez l’application et l’interface principale s’affiche :

Exécution de l'application

Si vous ouvrez le menu Modifier, notez que couper, copier et coller sont désactivés, car il n’existe aucune image dans l’image ou dans le collage par défaut :

Ouverture du menu Modifier

Si vous ajoutez une image à l’image et rouvrez le menu Modifier, les éléments seront désormais activés :

L’affichage des éléments de menu Modifier est activé

Si vous copiez l’image et sélectionnez Nouveau dans le menu fichier, vous pouvez coller cette image dans la nouvelle fenêtre :

Collage d’une image dans une nouvelle fenêtre

Dans les sections suivantes, nous allons examiner en détail l’utilisation du collage dans une application Xamarin.Mac.

À propos du collage

Dans macOS (anciennement OS X), le collage (NSPasteboard) fournit la prise en charge de plusieurs processus serveur tels que Copier et Coller, Glisser-déplacer et Application Services. Dans les sections suivantes, nous allons examiner de plus près plusieurs concepts de collage de clé.

Qu’est-ce qu’un collage ?

La NSPasteboard classe fournit un mécanisme standardisé pour échanger des informations entre des applications ou dans une application donnée. La fonction principale d’un collage est de gérer les opérations de copie et de collage :

  1. Lorsque l’utilisateur sélectionne un élément dans une application et utilise l’élément de menu Couper ou Copier , une ou plusieurs représentations de l’élément sélectionné sont placées sur le collage.
  2. Lorsque l’utilisateur utilise l’élément de menu Coller (dans la même application ou une autre), la version des données qu’il peut gérer est copiée à partir du collage et ajoutée à l’application.

Les opérations de recherche, de glisser-déplacer, de glisser-déplacer et de services d’application sont moins évidentes :

  • Lorsque l’utilisateur lance une opération de glissement, les données de glissement sont copiées dans le collage. Si l’opération de glissement se termine par une liste déroulante sur une autre application, cette application copie les données à partir du collage.
  • Pour les services de traduction, les données à traduire sont copiées dans le collage par l’application demandée. Le service d’application récupère les données à partir du collage, effectue la traduction, puis colle les données dans le collage.

Dans leur forme la plus simple, les collages sont utilisés pour déplacer des données à l’intérieur d’une application donnée ou entre des applications et existe dans une zone de mémoire globale spéciale en dehors du processus de l’application. Bien que les concepts des collages soient facilement saisis, plusieurs détails plus complexes doivent être pris en compte. Celles-ci seront abordées en détail ci-dessous.

Collages nommés

Un collage peut être public ou privé et peut être utilisé à diverses fins au sein d’une application ou entre plusieurs applications. macOS fournit plusieurs collages standard, chacun avec une utilisation spécifique et bien définie :

  • NSGeneralPboard - Le collage par défaut pour les opérations Couper, Copier et Coller .
  • NSRulerPboard - Prend en charge les opérations Couper, Copier et Coller sur les règles.
  • NSFontPboard - Prend en charge les opérations Couper, Copier et Coller sur les NSFont objets.
  • NSFindPboard - Prend en charge les panneaux de recherche spécifiques à l’application qui peuvent partager du texte de recherche.
  • NSDragPboard - Prend en charge les opérations glisser-déplacer .

Dans la plupart des cas, vous allez utiliser l’un des collages définis par le système. Toutefois, il peut y avoir des situations qui vous obligent à créer vos propres collages. Dans ces situations, vous pouvez utiliser la FromName (string name) méthode de la NSPasteboard classe pour créer un collage personnalisé avec le nom donné.

Si vous le souhaitez, vous pouvez appeler la CreateWithUniqueName méthode de la NSPasteboard classe pour créer un collage nommé de manière unique.

Éléments de collage

Chaque élément de données qu’une application écrit dans un collage est considéré comme un élément pasteboard et un collage peut contenir plusieurs éléments en même temps. De cette façon, une application peut écrire plusieurs versions des données copiées dans un collage (par exemple, du texte brut et du texte mis en forme) et l’application de récupération peut lire uniquement les données qu’elle peut traiter (par exemple, le texte brut uniquement).

Représentations de données et identificateurs de type uniforme

Les opérations de collage se produisent généralement entre deux applications (ou plus) qui n’ont aucune connaissance des autres ou les types de données que chacun peut gérer. Comme indiqué dans la section ci-dessus, pour maximiser le potentiel de partage des informations, un collage peut contenir plusieurs représentations des données copiées et collées.

Chaque représentation est identifiée via un identificateur de type uniforme (UTI), qui n’est rien de plus qu’une chaîne simple qui identifie de manière unique le type de date présenté (pour plus d’informations, consultez la documentation Vue d’ensemble des identificateurs de type uniforme d’Apple).

Si vous créez un type de données personnalisé (par exemple, un objet de dessin dans une application de dessin vectoriel), vous pouvez créer votre propre UTI pour l’identifier de manière unique dans les opérations de copie et de collage.

Lorsqu’une application se prépare à coller des données copiées à partir d’un collage, elle doit trouver la représentation qui correspond le mieux à ses capacités (le cas échéant). En règle générale, il s’agit du type le plus riche disponible (par exemple du texte mis en forme pour une application de traitement de texte), en rebastant vers les formulaires les plus simples disponibles en fonction des besoins (texte brut pour un éditeur de texte simple).

Données promises

En règle générale, vous devez fournir autant de représentations des données copiées que possible pour optimiser le partage entre les applications. Toutefois, en raison de contraintes de temps ou de mémoire, il peut être difficile d’écrire réellement chaque type de données dans le collage.

Dans ce cas, vous pouvez placer la première représentation des données sur le collage et l’application de réception peut demander une représentation différente, qui peut être générée à la volée juste avant l’opération de collage.

Lorsque vous placez l’élément initial dans le collage, vous spécifiez qu’une ou plusieurs des autres représentations disponibles sont fournies par un objet conforme à l’interface NSPasteboardItemDataProvider . Ces objets fournissent les représentations supplémentaires à la demande, comme demandé par l’application de réception.

Nombre de modifications

Chaque collage conserve un nombre de modifications qui incrémente chaque fois qu’un nouveau propriétaire est déclaré. Une application peut déterminer si le contenu du collage a changé depuis la dernière fois qu’il l’a examiné en case activée la valeur du nombre de modifications.

Utilisez les méthodes et ClearContents les ChangeCount méthodes de la NSPasteboard classe pour modifier le nombre de modifications d’un collage donné.

Copie de données dans un collage

Vous effectuez une opération de copie en accédant d’abord à un collage, en désactivant tout contenu existant et en écrivant autant de représentations des données que nécessaire au collage.

Par exemple :

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

En règle générale, vous allez simplement écrire dans le collage général, comme nous l’avons fait dans l’exemple ci-dessus. Tout objet que vous envoyez à la WriteObjects méthode doit être conforme à l’interface INSPasteboardWriting . Plusieurs classes intégrées (telles que NSString, , NSImage, NSURLNSColor, , NSAttributedStringet NSPasteboardItem) sont automatiquement conformes à cette interface.

Si vous écrivez une classe de données personnalisée dans le collage, elle doit être conforme à l’interface INSPasteboardWriting ou être encapsulée dans une instance de la NSPasteboardItem classe (voir la section Types de données personnalisés ci-dessous).

Lecture de données à partir d’un collage

Comme indiqué ci-dessus, pour maximiser le potentiel de partage de données entre les applications, plusieurs représentations des données copiées peuvent être écrites dans le collage. Il incombe à l’application de réception de sélectionner la version la plus riche possible pour ses fonctionnalités (le cas échéant).

Opération de collage simple

Vous lisez des données à partir du collage à l’aide de la ReadObjectsForClasses méthode. Il nécessite deux paramètres :

  1. Tableau de types de NSObject classes basés que vous souhaitez lire à partir du collage. Vous devez d’abord commander cela avec le type de données le plus souhaité, avec tous les autres types en préférence décroissante.
  2. Dictionnaire contenant des contraintes supplémentaires (telles que la limitation à des types de contenu d’URL spécifiques) ou un dictionnaire vide si aucune autre contrainte n’est requise.

La méthode retourne un tableau d’éléments répondant aux critères que nous avons passés et contient donc au maximum le même nombre de types de données demandés. Il est également possible qu’aucun des types demandés ne soit présent et qu’un tableau vide soit retourné.

Par exemple, le code suivant case activée pour voir s’il existe un NSImage collage général et l’affiche dans une image correctement si elle le fait :

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

Demande de plusieurs types de données

En fonction du type d’application Xamarin.Mac en cours de création, il peut être en mesure de gérer plusieurs représentations des données collées. Dans ce cas, il existe deux scénarios pour récupérer des données à partir du collage :

  1. Effectuez un appel unique à la ReadObjectsForClasses méthode et fournissez un tableau de toutes les représentations souhaitées (dans l’ordre préféré).
  2. Effectuez plusieurs appels à la ReadObjectsForClasses méthode qui demandent un tableau de types différent à chaque fois.

Pour plus d’informations sur la récupération des données à partir d’un collage, consultez la section Opération de collage simple ci-dessus.

Vérification des types de données existants

Il peut arriver que vous souhaitiez case activée si un collage contient une représentation de données donnée sans réellement lire les données à partir du collage (par exemple, l’activation de l’élément de menu Coller uniquement quand des données valides existent).

Appelez la CanReadObjectForClasses méthode du collage pour voir s’il contient un type donné.

Par exemple, le code suivant détermine si le collage général contient une NSImage instance :

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

Lecture d’URL à partir du collage

En fonction de la fonction d’une application Xamarin.Mac donnée, il peut être nécessaire de lire des URL à partir d’un collage, mais uniquement s’ils répondent à un ensemble donné de critères (par exemple, pointer vers des fichiers ou des URL d’un type de données spécifique). Dans ce cas, vous pouvez spécifier des critères de recherche supplémentaires à l’aide du deuxième paramètre des méthodes ou ReadObjectsForClasses des CanReadObjectForClasses méthodes.

Types de données personnalisés

Il arrive parfois que vous deviez enregistrer vos propres types personnalisés dans le collage à partir d’une application Xamarin.Mac. Par exemple, une application de dessin vectoriel qui permet à l’utilisateur de copier et coller des objets de dessin.

Dans ce cas, vous devez concevoir votre classe personnalisée de données afin qu’elle hérite et qu’elle soit NSObject conforme à quelques interfaces (INSCodingINSPasteboardWritinget INSPasteboardReading). Si vous le souhaitez, vous pouvez utiliser un NSPasteboardItem pour encapsuler les données à copier ou coller.

Ces deux options seront abordées en détail ci-dessous.

Utilisation d’une classe personnalisée

Dans cette section, nous allons développer l’exemple d’application simple que nous avons créé au début de ce document et ajouter une classe personnalisée pour suivre les informations sur l’image que nous copions et collons entre les fenêtres.

Ajoutez une nouvelle classe au projet et appelez-la ImageInfo.cs. Modifiez le fichier et faites-le ressembler à ce qui suit :

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

Dans les sections suivantes, nous allons examiner cette classe en détail.

Héritage et interfaces

Avant qu’une classe de données personnalisée puisse être écrite ou lue à partir d’un collage, elle doit être conforme aux interfaces et INSPasteboardReading aux INSPastebaordWriting interfaces. En outre, il doit hériter et NSObject également se conformer à l’interface INSCoding :

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

La classe doit également être exposée à l’utilisation de Objective-C la Register directive et doit exposer toutes les propriétés ou méthodes requises à l’aide Exportde . Par exemple :

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

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

Nous exposons les deux champs de données que cette classe contiendra : le nom de l’image et son type (jpg, png, etc.).

Pour plus d’informations, consultez les classes /méthodes C# exposantes dans Objective-C la section de la documentation interne Xamarin.Mac, elle explique les attributs et Export les Register attributs utilisés pour connecter vos classes C# à des objets et des Objective-C éléments d’interface utilisateur.

Constructeurs

Deux constructeurs (correctement exposés à Objective-C) seront requis pour notre classe de données personnalisée afin qu’elle puisse être lue à partir d’un collage :

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

Tout d’abord, nous exposons le constructeur vide sous la méthode par défaut Objective-C de init.

Ensuite, nous exposons un NSCoding constructeur conforme qui sera utilisé pour créer une nouvelle instance de l’objet à partir du collage lors du collage sous le nom exporté de initWithCoder.

Ce constructeur prend un NSCoder (tel qu’il est créé par un NSKeyedArchiver écrit dans le collage), extrait les données jumelées clé/valeur et les enregistre dans les champs de propriété de la classe de données.

Écriture dans le collage

En respectant l’interface INSPasteboardWriting , nous devons exposer deux méthodes, et éventuellement une troisième méthode, afin que la classe puisse être écrite dans le collage.

Tout d’abord, nous devons indiquer au collage les représentations de type de données dans lesquelles la classe personnalisée peut être écrite :

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

Chaque représentation est identifiée via un identificateur de type uniforme (UTI), qui n’est rien de plus qu’une chaîne simple qui identifie de manière unique le type de données présenté (pour plus d’informations, consultez la documentation Vue d’ensemble des identificateurs de type uniforme d’Apple).

Pour notre format personnalisé, nous créons notre propre UTI : « com.xamarin.image-info » (notez qu’il s’agit d’une notation inverse comme un identificateur d’application). Notre classe est également capable d’écrire une chaîne standard dans le collage (public.text).

Ensuite, nous devons créer l’objet dans le format demandé qui est réellement écrit dans le collage :

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

Pour le public.text type, nous renvoyons un objet simple et mis en forme NSString . Pour le type personnalisé com.xamarin.image-info , nous utilisons un NSKeyedArchiver et l’interface NSCoder pour encoder la classe de données personnalisée dans une archive jumelée clé/valeur. Nous devons implémenter la méthode suivante pour gérer réellement l’encodage :

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

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

Les paires clé/valeur individuelles sont écrites dans l’encodeur et seront décodées à l’aide du deuxième constructeur que nous avons ajouté ci-dessus.

Si vous le souhaitez, nous pouvons inclure la méthode suivante pour définir toutes les options lors de l’écriture de données dans le collage :

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

Actuellement, seule l’option WritingPromised est disponible et doit être utilisée lorsqu’un type donné n’est promis qu’et n’est pas réellement écrit dans le collage. Pour plus d’informations, consultez la section Données promises ci-dessus.

Avec ces méthodes en place, le code suivant peut être utilisé pour écrire notre classe personnalisée dans le collage :

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

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

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

Lecture à partir du collage

En respectant l’interface INSPasteboardReading , nous devons exposer trois méthodes afin que la classe de données personnalisée puisse être lue à partir du collage.

Tout d’abord, nous devons indiquer au collage les représentations de type de données que la classe personnalisée peut lire à partir du Presse-papiers :

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

Là encore, ils sont définis comme des UTIs simples et sont les mêmes types que ceux que nous avons définis dans la section Écriture dans la section Coller au tableau ci-dessus.

Ensuite, nous devons indiquer au collage comment chacun des types UTI sera lu à l’aide de la méthode suivante :

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

Pour le com.xamarin.image-info type, nous demandons au collage de décoder la paire clé/valeur que nous avons créée avec la NSKeyedArchiver classe lors de l’écriture de la classe dans le collage en appelant le initWithCoder: constructeur que nous avons ajouté à la classe.

Enfin, nous devons ajouter la méthode suivante pour lire les autres représentations de données UTI à partir du collage :

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

Avec toutes ces méthodes en place, la classe de données personnalisée peut être lue à partir du collage à l’aide du code suivant :

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

Utilisation d’un NSPasteboardItem

Il peut arriver que vous deviez écrire des éléments personnalisés dans le collage qui ne justifient pas la création d’une classe personnalisée ou que vous souhaitez fournir des données dans un format commun, uniquement si nécessaire. Pour ces situations, vous pouvez utiliser un NSPasteboardItem.

Un NSPasteboardItem contrôle précis sur les données écrites dans le collage et conçu pour un accès temporaire doit être supprimé une fois qu’il a été écrit dans le collage.

Écriture de données

Pour écrire vos données personnalisées dans un fichier NSPasteboardItem personnalisé, vous devez fournir un fichier personnalisé NSPasteboardItemDataProvider. Ajoutez une nouvelle classe au projet et appelez-la ImageInfoDataProvider.cs. Modifiez le fichier et faites-le ressembler à ce qui suit :

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

Comme nous l’avons fait avec la classe de données personnalisée, nous devons utiliser les directives et Export les Register utiliser pour l’exposer Objective-C. La classe doit hériter et NSPasteboardItemDataProvider doit implémenter les méthodes et ProvideDataForType les FinishedWithDataProvider méthodes.

Utilisez la ProvideDataForType méthode pour fournir les données qui seront encapsulées comme NSPasteboardItem suit :

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

}

Dans ce cas, nous stockons deux informations sur notre image (Name et ImageType) et écrivons-les dans une chaîne simple (public.text).

Tapez écrire les données dans le collage, utilisez le code suivant :

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

Lecture des données en cours

Pour lire les données à partir du collage, utilisez le code suivant :

// 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
    ...
}

Résumé

Cet article a examiné en détail l’utilisation du collage dans une application Xamarin.Mac pour prendre en charge les opérations de copie et de collage. Tout d’abord, il a introduit un exemple simple pour vous familiariser avec les opérations de collage standard. Ensuite, il a fallu un examen détaillé du collage et comment lire et écrire des données à partir de celui-ci. Enfin, il a examiné l’utilisation d’un type de données personnalisé pour prendre en charge la copie et le collage de types de données complexes au sein d’une application.