Kopírování a vkládání v Xamarin.Macu
Tento článek se věnuje práci s vložením a kopírováním a vkládáním do aplikace Xamarin.Mac. Ukazuje, jak pracovat se standardními datovými typy, které je možné sdílet mezi více aplikacemi, a jak podporovat vlastní data v rámci dané aplikace.
Přehled
Při práci s C# a .NET v aplikaci Xamarin.Mac máte přístup ke stejné podpoře vložení (kopírování a vkládání), ve které pracuje Objective-C vývojář.
V tomto článku probýme dva hlavní způsoby použití tohoto vložení v aplikaci Xamarin.Mac:
- Standardní datové typy – Vzhledem k tomu, že se operace vložení obvykle provádějí mezi dvěma nesouvisejícími aplikacemi, žádná aplikace nezná typy dat, které podporuje druhá aplikace. Aby se maximalizoval potenciál sdílení, může vložení obsahovat více reprezentací dané položky (pomocí standardní sady běžných datových typů), které aplikaci umožňují vybrat verzi, která je pro její potřeby nejlepší.
- Vlastní data – Pro podporu kopírování a vkládání složitých dat v rámci Xamarin.Mac můžete definovat vlastní datový typ, který bude pracovní panel zpracovat. Například aplikace pro vektorové kreslení, která uživateli umožňuje kopírovat a vkládat složité tvary, které se skládají z více datových typů a bodů.
V tomto článku se budeme seznamovat se základy práce s pasteboardem v aplikaci Xamarin.Mac, která podporuje operace kopírování a vkládání. Důrazně doporučujeme, abyste si nejprve prošli článek Hello, Mac, konkrétně oddíly Úvod do Xcode a Interface Builder a Výstupy a Akce, protože se zabývá klíčovými koncepty a technikami, které budeme v tomto článku používat.
Možná se budete chtít podívat i na část dokumentu Exposing C# classes / methods to Objective-CExposing C# classes / methods to Objective-C která vysvětluje atributy a používané k připojení tříd jazyka C# k objektům a prvkům uživatelského RegisterExportObjective-C rozhraní.
Začínáme s vložením
Vložení představuje standardizovaný mechanismus pro výměnu dat v rámci dané aplikace nebo mezi aplikacemi. Vložení v aplikaci Xamarin.Mac se obvykle používá ke zpracování operací kopírování a vložení, ale podporuje se i řada dalších operací (například přetažení a & aplikační služby).
Pro rychlé odlesky začneme jednoduchým praktickým úvodem do používání pasteboardů v aplikaci Xamarin.Mac. Později poskytneme podrobné vysvětlení toho, jak pracovní panel funguje a jaké metody se používají.
V tomto příkladu vytvoříme jednoduchou aplikaci založenou na dokumentu, která spravuje okno obsahující zobrazení obrázku. Uživatel bude moct kopírovat a vkládat obrázky mezi dokumenty v aplikaci a do nebo z jiných aplikací nebo z více oken uvnitř stejné aplikace.
Vytvoření projektu Xamarin
Nejprve vytvoříme novou aplikaci Xamarin.Mac založenou na dokumentu, pro kterou přidáme podporu kopírování a vkládání.
Postupujte následovně:
Spusťte Visual Studio pro Mac a klikněte na odkaz Nový Project....
Vyberte MacAppCocoa Appa pak klikněte na tlačítko Další:
Jako
MacCopyPastenázev Project zadejteMacCopyPastejako výchozí podržte všechno ostatní. Klikněte na Další:Klikněte na tlačítko Vytvořit:
Přidání dokumentu NSDocument
Dále přidáme vlastní třídu, která bude fungovat jako úložiště na pozadí pro NSDocument uživatelské rozhraní aplikace. Bude obsahovat jedno zobrazení obrázku a bude vědět, jak zkopírovat obrázek ze zobrazení do výchozího pasteboardu a jak pojmout obrázek z výchozího pasteboardu a zobrazit ho v zobrazení obrázku.
Klikněte pravým tlačítkem na projekt Xamarin.Mac v okně Oblast řešenívyberte Přidatnový soubor.:

Jako ImageDocument Název zadejte ImageDocument klikněte na tlačítko Nový. Upravte třídu ImageDocument.cs, aby vypadala takto:
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
}
}
Podívejme se na část kódu podrobněji níže.
Následující kód poskytuje vlastnost k otestování existence dat obrázku na výchozím pasteboardu, pokud je obrázek k dispozici, true se vrátí jinak 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);
}
}
Následující kód zkopíruje obrázek z připojeného zobrazení obrázku do výchozího pasteboardu:
[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 });
}
}
}
A následující kód vloží obrázek z výchozího vložení a zobrazí ho v připojeném zobrazení obrázku (pokud pracovní panel obsahuje platný obrázek):
[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]
}
}
Po vytvoření tohoto dokumentu vytvoříme uživatelské rozhraní pro aplikaci Xamarin.Mac.
Vytvoření uživatelského rozhraní
Poklikejte na soubor Main.storyboard a otevřete ho v Xcode. Dále přidejte panel nástrojů a obrázek a nakonfigurujte je následujícím způsobem:
Přidejte položku panelu nástrojů Kopírovat a vložit obrázek na levou stranu panelu nástrojů. Tyto klávesové zkratky budeme používat ke kopírování a vkládání z nabídky Upravit. Pak na pravou stranu panelu nástrojů přidejte čtyři položky panelu nástrojů Obrázek. Použijeme je k naplnění image některými výchozími imagemi.
Další informace o práci s panely nástrojů najdete v naší dokumentaci k panelům nástrojů.
V dalším kroku zpřístupníme následující výstupy a akce pro položky panelu nástrojů a obrázek:
Další informace o práci s výstupy a akcemi najdete v části Výstupy a akce v naší dokumentaci Hello, Mac.
Povolení uživatelského rozhraní
Když máme uživatelské rozhraní vytvořené v Xcode a prvek uživatelského rozhraní vystavený prostřednictvím výstupů a akcí, musíme přidat kód, který uživatelské rozhraní povolí. Poklikejte na soubor ImageWindow.cs v Oblast řešení, aby vypadal takto:
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
}
}
Podívejme se na tento kód podrobněji níže.
Nejprve zveřejňujeme instanci ImageDocument třídy, kterou jsme vytvořili výše:
private ImageDocument _document;
...
[Export ("Document")]
public ImageDocument Document {
get { return _document; }
set {
WillChangeValue ("Document");
_document = value;
DidChangeValue ("Document");
}
}
Pomocí , a jsme na nastavení vlastnosti umožnili kódování kódu Key-Value datové ExportWillChangeValue vazby v DidChangeValueDocument Xcode.
Obrázek také zveřejňujeme z image, který jsme přidali do našeho uživatelského rozhraní v Xcode, s následující vlastností:
public ViewController ImageViewController {
get { return ContentViewController as ViewController; }
}
public NSImage Image {
get {
return ImageViewController.Image;
}
set {
ImageViewController.Image = value;
}
}
Při načtení a zobrazení hlavního okna vytvoříme instanci třídy a připojíme k ní image uživatelského rozhraní pomocí ImageDocument následujícího kódu:
public override void AwakeFromNib ()
{
base.AwakeFromNib ();
// Create a new document instance
Document = new ImageDocument ();
// Attach to image view
Document.ImageView = ImageViewController.ContentView;
}
Nakonec v reakci na to, že uživatel klikne na položky panelu nástrojů pro kopírování a vkládání, zavoláme instanci třídy , aby ImageDocument skutečně pracoval:
partial void CopyImage (NSObject sender) {
Document.CopyImage(sender);
}
partial void PasteImage (Foundation.NSObject sender) {
Document.PasteImage(sender);
}
Povolení nabídek Soubor a Upravit
Poslední věcí, kterou musíme udělat, je povolit položku nabídky Nový z nabídky Soubor (vytvořit nové instance hlavního okna) a povolit položky nabídky Vyjmout,Kopírovat a Vložit z nabídky Upravit.
Pokud chcete povolit položku nabídky Nová, upravte soubor AppDelegate.cs a přidejte následující kód:
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);
}
Další informace najdete v části Práce s více Windows v naší Windows dokumentaci.
Pokud chcete povolit položky nabídky Vyjmout,Kopírovat a Vložit, upravte soubor AppDelegate.cs a přidejte následující kód:
[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);
}
Pro každou položku nabídky získáte aktuální okno klíče, které je na nejvyšším místě, a přetypováme ho na naši ImageWindow třídu:
var window = NSApplication.SharedApplication.KeyWindow as ImageWindow;
Odtud voláme instanci ImageDocument třídy tohoto okna pro zpracování akcí kopírování a vložení. Například:
window.Document.CopyImage (sender);
Chceme, aby byly položky nabídky Vyjmout, Kopírovat a Vložit přístupné jenom v případě, že jsou ve výchozím vkládání nebo v imutu obrázku aktuálního aktivního okna data obrázků.
Pojďme do projektu Xamarin.Mac přidat soubor EditMenuDelegate.cs a vypadat takto:
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
}
}
Opět dostaneme aktuální okno nejvyšší kategorie a pomocí instance třídy uvidíme, jestli požadovaná ImageDocument data image existují. Pak pomocí metody MenuWillHighlightItem povolíme nebo zakážeme každou položku na základě tohoto stavu.
Upravte soubor AppDelegate.cs a zajistěte, aby metoda vypadala takto:
public override void DidFinishLaunching (NSNotification notification)
{
// Disable automatic item enabling on the Edit menu
EditMenu.AutoEnablesItems = false;
EditMenu.Delegate = new EditMenuDelegate ();
}
Nejprve zakážeme automatické povolení a zakázání položek nabídky v nabídce Upravit. Dále připojíme instanci třídy EditMenuDelegate , kterou jsme vytvořili výše.
Další informace najdete v naší dokumentaci k nabídkám.
Otestování aplikace
Když máte všechno připravené, můžeme aplikaci otestovat. Sestavte a spusťte aplikaci a zobrazí se hlavní rozhraní:

Pokud otevřete nabídku Upravit, všimněte si, že operace Vyjmout, Kopírovat a Vložit jsou zakázané, protože na obrázku není žádný obrázek ani na výchozím pasteboardu:

Pokud do image přidáte obrázek a znovu otevřete nabídku Upravit, položky se teď povolí:

Pokud obrázek zkopírujete a v nabídce soubor vyberete Nový, můžete ho vložit do nového okna:
Vložení obrázku do nového
V následujících částech se podrobně podíváme na práci s tabuli v aplikaci Xamarin.Mac.
Informace o vložení
V systému macOS (dříve OS X) poskytuje vložení ( ) podporu několika serverových procesů, jako je kopírování NSPasteboard& vložení, přetažení a & aplikační služby. V následujících částech se blíže podíváme na několik klíčových konceptů vkládání.
Co je pracovní panel?
Třída poskytuje standardizovaný mechanismus pro výměnu informací mezi NSPasteboard aplikacemi nebo v rámci dané aplikace. Hlavní funkcí pracovní plochy je pro zpracování operací kopírování a vložení:
- Když uživatel vybere položku v aplikaci a použije položku nabídky Vyjmout nebo Kopírovat , jedno nebo více reprezentace vybrané položky se umístí na pracovní plochu.
- Když uživatel použije položku nabídky Vložit (v rámci stejné aplikace nebo jiné), verze dat, kterou může zpracovat, se zkopíruje z pracovní plochy a přidá do aplikace.
Méně zřejmá pracovní plocha používá operace najít, přetáhnout, přetáhnout a aplikační služby:
- Když uživatel zahájí operaci přetažení, data přetažení se zkopírují na pracovní plochu. Pokud operace přetažení skončí s přesunutím do jiné aplikace, aplikace zkopíruje data z pracovní plochy.
- Pro překladatelské služby se data, která mají být přeložena, zkopírují na pracovní plochu tím, že aplikace požaduje. Aplikační služba, načte data z pracovní plochy, provede překlad a pak vloží data zpátky na pracovní plochu.
V nejjednodušším tvaru se pracovní plochy používají k přesunu dat v dané aplikaci nebo mezi aplikacemi a existují ve speciální globální oblasti paměti mimo proces aplikace. I když jsou koncepty pracovní plochy snadno uchopit, existuje několik složitějších podrobností, které je třeba zvážit. Na tyto podrobnosti se dozvíte podrobněji.
Pojmenované pracovní plochy
Pracovní plocha může být veřejná nebo soukromá a může se používat pro nejrůznější účely v rámci aplikace nebo mezi několika aplikacemi. macOS poskytuje několik standardních pracovní ploch, z nichž každá má konkrétní, jasně definované použití:
NSGeneralPboard– Výchozí pracovní plocha pro operaceNSGeneralPboard, kopírování a vložení .NSRulerPboard– Podporuje operaceNSRulerPboard, kopírování a vložení na pravítkech.NSFontPboard– Podporuje operaceNSFontPboard, kopírování a vložení u objektů.NSFindPboard– Podporuje panely hledání specifické pro aplikaci, které mohou sdílet hledaný text.NSDragPboard-PodporujeNSDragPboard
Ve většině případů použijete jednu z definovaných systémových ploch. Mohou však nastat situace, kdy budete potřebovat vytvořit vlastní pracovní plochu. V těchto situacích lze pomocí FromName (string name) metody NSPasteboard třídy vytvořit vlastní pracovní plochu s daným názvem.
Volitelně můžete zavolat CreateWithUniqueName metodu NSPasteboard třídy a vytvořit tak jedinečnou pojmenovanou pracovní plochu.
Položky pracovní plochy
Každá část dat, kterou aplikace zapisuje na pracovní plochu, je považována za položku pracovní plochy a pracovní plocha může obsahovat více položek současně. Tímto způsobem může aplikace zapisovat více verzí dat kopírovaných na pracovní plochu (například prostý text a formátovaný text) a načítání aplikace může číst pouze data, která může zpracovat (například pouze prostý text).
Reprezentace dat a identifikátory uniformního typu
K operacím pracovní plochy obvykle dochází mezi dvěma (nebo více) aplikacemi bez jakýchkoli znalostí nebo typů dat, která může každý z nich zpracovat. Jak je uvedeno v části výše, aby bylo možné maximalizovat možnosti sdílení informací, může pracovní plocha obsahovat více reprezentace kopírovaných a vkládaných dat.
Každá reprezentace je identifikována prostřednictvím identifikátoru Uniform Type (identifikátor UTI), který není větší než jednoduchý řetězec, který jednoznačně identifikuje typ data, který se zobrazí (Další informace najdete v dokumentaci k přehledu jednotných typů identifikátorů ) společnosti Apple.
Pokud vytváříte vlastní datový typ (například nakreslený objekt v aplikaci Vector Drawing), můžete vytvořit vlastní identifikátor UTI a jednoznačně ho identifikovat v operacích kopírování a vložení.
Když se aplikace připraví, aby vložila data zkopírovaná z pracovní plochy, musí najít reprezentace, která nejlépe odpovídá jejich schopnostem (pokud existuje). Obvykle se jedná o dostupný typ, který je k dispozici (například formátovaný text pro aplikaci pro zpracování textu), návrat k nejjednodušším formulářům dostupným podle potřeby (prostý text v jednoduchém textovém editoru).
Přislíbená data
Obecně řečeno, měli byste zadat tolik reprezentace dat, která se mají kopírovat, aby bylo možné maximalizovat sdílení mezi aplikacemi. Vzhledem k omezením času nebo paměti ale může být nepraktické ve skutečnosti zapisovat každý datový typ do pracovní plochy.
V této situaci můžete první datovou prezentaci umístit na pracovní plochu a přijímající aplikace si může vyžádat jinou reprezentaci, kterou je možné vygenerovat těsně před operací vložení.
Když umístíte počáteční položku na pracovní plochu, určíte, že jeden nebo více dostupných dalších reprezentace je zajištěno objektem, který odpovídá NSPasteboardItemDataProvider rozhraní. Tyto objekty budou poskytovat dodatečné reprezentace na vyžádání, jak požaduje přijímající aplikace.
Počet změn
Každá pracovní plocha udržuje počet změn , který se zvýší při každé deklaraci nového vlastníka. Aplikace může určit, jestli se obsah pracovní plochy od posledního zkoumání změnil, a to tak, že zkontroluje hodnotu počet změn.
Použijte ChangeCount metody a ClearContentsNSPasteboard třídy pro úpravu počtu změn dané pracovní plochy.
Kopírování dat na pracovní plochu
Operaci kopírování provedete tak, že nejprve přistupujete k pracovní ploše a vymažete stávající obsah a zapisujete tolik reprezentace dat, kolik je potřeba pro pracovní plochu.
Například:
// 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});
Obvykle budete psát jenom na plochu General, jak jsme to udělali v příkladu výše. Libovolný objekt, který odesíláte do WriteObjects metody, WriteObjects odpovídat INSPasteboardWriting rozhraní. Několik vestavěných tříd (například,,, NSString , NSImageNSURLNSColorNSAttributedString a NSPasteboardItem ) automaticky odpovídají tomuto rozhraní.
Pokud píšete vlastní datovou třídu na pracovní plochu, musí odpovídat INSPasteboardWriting rozhraní nebo být zabalena do instance NSPasteboardItem třídy (viz část INSPasteboardWriting ).
Čtení dat z pracovní plochy
Jak je uvedeno výše, k maximalizaci potenciálu pro sdílení dat mezi aplikacemi se můžou na pracovní plochu zapsat víc reprezentace kopírovaných dat. Je k dispozici až přijímající aplikace, která vybere bohatou verzi, která je možné využít pro své možnosti (pokud existuje).
Operace jednoduchého vložení
Data z pracovní plochy načtete pomocí ReadObjectsForClasses metody. Bude vyžadovat dva parametry:
- Pole
NSObjecttypů tříd založených na pracovní ploše, které chcete číst. Nejdříve byste měli tento typ seřadit s požadovaným datovým typem s případnými zbývajícími typy v klesajících preferencích. - Slovník obsahující další omezení (například omezení na konkrétní typy obsahu URL) nebo prázdný slovník, pokud nejsou vyžadována žádná další omezení.
Metoda vrátí pole položek, které splňují kritéria, která předáváme, a proto obsahuje maximálně stejný počet datových typů, které jsou požadovány. je také možné, že žádný z požadovaných typů není přítomen a prázdné pole bude vráceno.
Například následující kód zkontroluje, zda NSImage existuje v obecné pracovní ploše a zobrazí jej v obrazové příručce, pokud má:
[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];
}
}
Vyžádání více datových typů
V závislosti na typu vytvářené aplikace Xamarin. Mac může být možné zpracovávat více reprezentace vkládaných dat. V této situaci existují dva scénáře načítání dat z pracovní plochy:
- Proveďte jedno volání
ReadObjectsForClassesmetody a poskytněte pole všech reprezentace, kterou si přejete (v upřednostňovaném pořadí). - Pokaždé, když se
ReadObjectsForClassespožádá o jiné pole typů, proveďte několik volání metody.
Další informace o načítání dat z pracovní plochy najdete v části věnované jednoduché operaci vložení výše.
Kontrola existujících datových typů
Existují situace, kdy byste měli chtít zjistit, zda pracovní plocha obsahuje danou datovou prezentaci, aniž by bylo nutné číst data z pracovní plochy (například povolení položky nabídky Vložit pouze v případě, že existují platná data).
Zavolejte CanReadObjectForClasses metodu pracovní plochy, abyste viděli, zda obsahuje daný typ.
Například následující kód určuje, zda Obecná pracovní plocha obsahuje NSImage instanci:
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);
}
}
Čtení adres URL z pracovní plochy
Na základě funkce dané aplikace Xamarin. Mac může být nutné číst adresy URL z pracovní plochy, ale pouze v případě, že splňují danou sadu kritérií (například nasměrování na soubory nebo adresy URL konkrétního datového typu). V této situaci můžete zadat další kritéria vyhledávání pomocí druhého parametru CanReadObjectForClassesReadObjectsForClasses metod nebo.
Vlastní datové typy
K dispozici jsou situace, kdy budete potřebovat uložit vlastní typy na pracovní plochu z aplikace Xamarin. Mac. Například aplikace vektorového kreslení, která umožňuje uživateli kopírovat a vkládat objekty kresby.
V této situaci budete muset navrhnout vlastní třídu dat tak, aby dědila z NSObject a byla v souladu s několika rozhraními ( INSCodingINSPasteboardWriting a INSPasteboardReading ). Volitelně můžete použít NSPasteboardItem k zapouzdření dat, která mají být kopírována nebo vložena.
Obě tyto možnosti jsou podrobněji popsány níže.
Použití vlastní třídy
V této části se seznámíte s jednoduchou ukázkovou aplikací, kterou jsme vytvořili na začátku tohoto dokumentu, a přidáním vlastní třídy, která sleduje informace o imagi, kterou kopírujeme a vkládáme mezi Windows.
Přidejte do projektu novou třídu a zavolejte ji IMAGEINFO. cs. Upravte soubor a nastavte jeho vzhled jako na následující:
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
}
}
V následujících částech podíváme se na tuto třídu podrobněji.
Dědičnost a rozhraní
Předtím, než lze vlastní datovou třídu zapsat nebo číst z pracovní plochy, musí být v souladu s INSPastebaordWritingINSPasteboardReading rozhraními a. Kromě toho musí dědit od NSObject a také splňovat INSCoding rozhraní:
[Register("ImageInfo")]
public class ImageInfo : NSObject, INSCoding, INSPasteboardWriting, INSPasteboardReading
...
Třída musí být také vystavena pro Objective-C použití Register direktivy a musí vystavit všechny požadované vlastnosti nebo metody pomocí Export . Například:
[Export("name")]
public string Name { get; set; }
[Export("imageType")]
public string ImageType { get; set; }
Zveřejňujeme dvě pole dat, která tato třída bude obsahovat – název a typ obrázku (jpg, PNG atd.).
Další informace naleznete v Exposing C# classes / methods to Objective-C části dokumentace k Exposing C# classes / methods to Objective-C , kde jsou vysvětleny RegisterExport atributy a použité k navýšení tříd jazyka C# do Objective-C objektů a prvků uživatelského rozhraní.
Konstruktory
Objective-CPro naši vlastní datovou třídu se vyžadují dva konstruktory (správné vystavené pro), aby je bylo možné číst z pracovní plochy:
[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 ();
}
Nejdřív zveřejňujeme prázdný konstruktor pod výchozí metodou init .
Dále vystavíme NSCoding vyhovující konstruktor, který se použije k vytvoření nové instance objektu z pracovní plochy při vložení pod exportovaným názvem initWithCoder .
Tento konstruktor přebírá NSCoder (jak je vytvořeno NSKeyedArchiver při zápisu na pracovní plochu), extrahuje spárovaná data klíč/hodnota a uloží je do polí vlastností datové třídy.
Zápis na pracovní plochu
V souladu s INSPasteboardWriting rozhraním je nutné vystavit dvě metody a volitelně třetí metodu, aby bylo možné třídu zapsat na pracovní plochu.
Nejprve musíme tabuli říct, do jakých reprezentací datového typu je možné zapsat vlastní třídu:
[Export ("writableTypesForPasteboard:")]
public virtual string[] GetWritableTypesForPasteboard (NSPasteboard pasteboard) {
string[] writableTypes = {"com.xamarin.image-info", "public.text"};
return writableTypes;
}
Každá reprezentace je identifikována prostřednictvím identifikátoru UTI (Uniform Type Identifier), což není nic jiného než jednoduchý řetězec, který jednoznačně identifikuje typ prezentovaných dat (další informace najdete v dokumentaci Přehled jednotných identifikátorů typů společnosti Apple).
Pro vlastní formát vytváříme vlastní UTI: com.xamarin.image-info (všimněte si, že je v obráceném formátu stejně jako identifikátor aplikace). Naše třída je také schopná napsat standardní řetězec na tabuli ( public.text ).
Dále potřebujeme vytvořit objekt v požadovaném formátu, který se ve skutečnosti zapisovat do pasteboardu:
[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;
}
Pro public.text typ vracíme jednoduchý formátovaný NSString objekt. Pro vlastní typ používáme rozhraní a ke kódování vlastní datové třídy do spárovaného archivu com.xamarin.image-infoNSKeyedArchiverNSCoder klíč/hodnota. Pro vlastní zpracování kódování budeme muset implementovat následující metodu:
[Export ("encodeWithCoder:")]
public void EncodeTo (NSCoder encoder) {
// Encode data
encoder.Encode(new NSString(Name),"name");
encoder.Encode(new NSString(ImageType),"imageType");
}
Jednotlivé páry klíč-hodnota se zapisou do kodéru a dekóduje se pomocí druhého konstruktoru, který jsme přidali výše.
Volitelně můžeme zahrnout následující metodu, která definuje jakékoli možnosti při zápisu dat na pracovní panel:
[Export ("writingOptionsForType:pasteboard:"), CompilerGenerated]
public virtual NSPasteboardWritingOptions GetWritingOptionsForType (string type, NSPasteboard pasteboard) {
return NSPasteboardWritingOptions.WritingPromised;
}
V současné době je k dispozici pouze možnost , která by se měla použít, když je daný typ slíben a ve skutečnosti se WritingPromised nezapisuje do pasteboardu. Další informace najdete výše v části slíbená data.
Když jsou tyto metody k dispozici, můžete k zápisu vlastní třídy na tabuli použít následující kód:
// Get the standard pasteboard
var pasteboard = NSPasteboard.GeneralPasteboard;
// Empty the current contents
pasteboard.ClearContents();
// Add info to the pasteboard
pasteboard.WriteObjects (new ImageInfo[] { Info });
Čtení z tabule
V souladu s rozhraním musíme zveřejnit tři metody, aby bylo možné vlastní třídu dat číst INSPasteboardReading z tabule.
Nejprve musíme tabuli říct, jaké reprezentace datového typu může vlastní třída číst ze schránky:
[Export ("readableTypesForPasteboard:")]
public static string[] GetReadableTypesForPasteboard (NSPasteboard pasteboard){
string[] readableTypes = {"com.xamarin.image-info", "public.text"};
return readableTypes;
}
Opět platí, že jsou definovány jako jednoduché uti a jsou to stejné typy, které jsme definovali v části Zápis do pasteboardu výše.
Dále musíme tabuli říct, jak se budou číst jednotlivé typy UTI, pomocí následující metody:
[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;
}
U typu říkáme, že má vložení dekódovat pár klíč/hodnota, který jsme vytvořili pomocí třídy při zápisu třídy do tabule voláním konstruktoru, který jsme přidali com.xamarin.image-infoNSKeyedArchiver do initWithCoder: třídy.
Nakonec musíme přidat následující metodu, která načte další reprezentace dat UTI z tabule:
[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;
}
Když jsou všechny tyto metody k dispozici, můžete pomocí následujícího kódu načíst vlastní třídu dat z tabule:
// 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];
}
Použití NSPasteboardItem
Můžou se zobrazit časy, kdy potřebujete na vložení zapsat vlastní položky, které nezaručují vytvoření vlastní třídy, nebo když chcete zadat data ve společném formátu, pouze pokud je to nutné. V takových situacích můžete použít NSPasteboardItem .
Objekt poskytuje přesnou kontrolu nad daty, která jsou zapsána na pracovní panel a jsou navržena pro dočasný přístup – měla by se po zápisu na vložení NSPasteboardItem ukončovat.
Zápis dat
Pokud chcete zapsat vlastní data NSPasteboardItem do , budete muset zadat vlastní NSPasteboardItemDataProvider . Přidejte do projektu novou třídu a volejte ji ImageInfoDataProvider.cs. Upravte soubor tak, aby vypadal takto:
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
}
}
Stejně jako u vlastní třídy dat musíme použít direktivy a , aby RegisterExport byla přístupná pro Objective-C . Třída musí dědit z a NSPasteboardItemDataProvider musí implementovat metody a FinishedWithDataProviderProvideDataForType .
Pomocí metody ProvideDataForType zadejte data, která se zabalí NSPasteboardItem do souboru , následujícím způsobem:
[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;
}
}
V tomto případě ukládáme dva údaje o obrázku (Name a ImageType) a zapisujeme je do jednoduchého řetězce ( public.text ).
Napište data do pasteboardu a použijte následující kód:
// 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 });
}
Čtení dat
Pokud chcete načíst data zpět z tabule, použijte následující kód:
// 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
...
}
Souhrn
Tento článek podrobně popisuje práci s vložením v aplikaci Xamarin.Mac na podpoře operací kopírování a vkládání. Nejprve se představil jednoduchý příklad, který vás seznámí se standardními operacemi s vkládáním. Dále se podrobně podíval na pracovní panel a na to, jak z něj číst a zapisovat data. Nakonec se podíval na použití vlastního datového typu pro podporu kopírování a vkládání složitých datových typů v aplikaci.





