Salin dan tempel di Xamarin.Mac
Artikel ini membahas bekerja dengan papan tempel untuk menyediakan salin dan tempel di aplikasi Xamarin.Mac. Ini menunjukkan cara bekerja dengan jenis data standar yang dapat dibagikan antara beberapa aplikasi dan cara mendukung data kustom dalam aplikasi tertentu.
Gambaran Umum
Saat bekerja dengan C# dan .NET dalam aplikasi Xamarin.Mac, Anda memiliki akses ke dukungan pasteboard (salin dan tempel) yang sama dengan yang dimiliki pengembang yang bekerja Objective-C .
Dalam artikel ini kita akan membahas dua cara utama untuk menggunakan pasteboard di aplikasi Xamarin.Mac:
- Jenis Data Standar - Karena operasi pasteboard biasanya dilakukan di antara dua aplikasi yang tidak terkait, tidak ada aplikasi yang mengetahui jenis data yang didukung oleh yang lain. Untuk memaksimalkan potensi berbagi, papan tempel dapat menyimpan beberapa representasi item tertentu (menggunakan sekumpulan standar jenis data umum), ini memungkinkan aplikasi yang menggunakan untuk memilih versi yang paling cocok untuk kebutuhannya.
- Data Kustom - Untuk mendukung penyalinan dan penempelan data kompleks dalam Xamarin.Mac Anda, Anda dapat menentukan jenis data kustom yang akan ditangani oleh pasteboard. Misalnya, aplikasi gambar vektor yang memungkinkan pengguna menyalin dan menempelkan bentuk kompleks yang terdiri dari beberapa jenis dan titik data.
Dalam artikel ini, kita akan membahas dasar-dasar bekerja dengan pasteboard dalam aplikasi Xamarin.Mac untuk mendukung operasi salin dan tempel. Sangat disarankan agar Anda bekerja melalui artikel Hello, Mac terlebih dahulu, khususnya bagian Pengenalan Xcode dan Penyusun Antarmuka dan Outlet dan Tindakan , karena mencakup konsep dan teknik utama yang akan kita gunakan dalam artikel ini.
Anda mungkin ingin melihat kelas /metode Exposing C# ke Objective-Cbagian dari dokumen Xamarin.Mac Internals juga, ini menjelaskan Register
atribut dan Export
yang digunakan untuk menghubungkan kelas C# Anda ke Objective-C objek dan elemen UI.
Mulai menggunakan papan tempel
Papan tempel menyajikan mekanisme standar untuk bertukar data dalam aplikasi tertentu atau antar aplikasi. Penggunaan umum untuk pasteboard dalam aplikasi Xamarin.Mac adalah untuk menangani operasi salin dan tempel, namun sejumlah operasi lain juga didukung (seperti Drag & Drop dan Application Services).
Untuk membuat Anda keluar dari tanah dengan cepat, kita akan mulai dengan pengantar sederhana dan praktis untuk menggunakan papan tempel di aplikasi Xamarin.Mac. Nantinya, kami akan memberikan penjelasan mendalam tentang cara kerja papan tempel dan metode yang digunakan.
Untuk contoh ini, kita akan membuat aplikasi berbasis dokumen sederhana yang mengelola jendela yang berisi tampilan gambar. Pengguna akan dapat menyalin dan menempelkan gambar antara dokumen di aplikasi dan ke atau dari aplikasi lain atau beberapa jendela di dalam aplikasi yang sama.
Membuat proyek Xamarin
Pertama, kita akan membuat aplikasi Xamarin.Mac berbasis dokumen baru yang akan kita tambahkan dukungan salin dan tempel.
Lakukan:
Mulai Visual Studio untuk Mac dan klik tautan Proyek Baru... .
Pilih App Mac>>Cocoa App, lalu klik tombol Berikutnya:
Masukkan
MacCopyPaste
untuk Nama Proyek dan pertahankan yang lain sebagai default. Klik Berikutnya:Klik tombol Buat :
Menambahkan NSDocument
Selanjutnya kita akan menambahkan kelas kustom NSDocument
yang akan bertindak sebagai penyimpanan latar belakang untuk antarmuka pengguna aplikasi. Ini akan berisi satu Tampilan Gambar dan tahu cara menyalin gambar dari tampilan ke papan tempel default dan cara mengambil gambar dari papan tempel default dan menampilkannya di Tampilan Gambar.
Klik kanan pada proyek Xamarin.Mac di Solution Pad dan pilih Tambahkan>File Baru..:
Masukkan ImageDocument
untuk Nama dan klik tombol Baru . Edit kelas ImageDocument.cs dan buat terlihat seperti berikut ini:
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
}
}
Mari kita lihat beberapa kode secara rinci di bawah ini.
Kode berikut menyediakan properti untuk menguji keberadaan data gambar pada pasteboard default, jika gambar tersedia, true
dikembalikan lagi 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);
}
}
Kode berikut menyalin gambar dari tampilan gambar terlampir ke dalam papan tempel default:
[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 });
}
}
}
Dan kode berikut menempelkan gambar dari papan tempel default dan menampilkannya dalam tampilan gambar terlampir (jika papan tempel berisi gambar yang valid):
[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]
}
}
Dengan dokumen ini di tempat, kita akan membuat antarmuka pengguna untuk aplikasi Xamarin.Mac.
Membangun antarmuka pengguna
Klik dua kali file Main.storyboard untuk membukanya di Xcode. Selanjutnya, tambahkan toolbar dan gambar dengan baik dan konfigurasikan sebagai berikut:
Tambahkan salin dan tempel Item Bilah Alat Gambar di sisi kiri toolbar. Kami akan menggunakannya sebagai pintasan untuk menyalin dan menempelkan dari menu Edit. Selanjutnya, tambahkan empat Item Toolbar Gambar ke sisi kanan toolbar. Kami akan menggunakannya untuk mengisi gambar dengan baik dengan beberapa gambar default.
Untuk informasi selengkapnya tentang bekerja dengan toolbar, silakan lihat dokumentasi Toolbar kami.
Selanjutnya, mari kita ekspos outlet dan tindakan berikut untuk item toolbar kami dan gambar dengan baik:
Untuk informasi selengkapnya tentang bekerja dengan outlet dan tindakan, silakan lihat bagian Outlet dan Tindakan dari dokumentasi Hello, Mac kami.
Mengaktifkan antarmuka pengguna
Dengan antarmuka pengguna kami yang dibuat di Xcode dan elemen UI kami yang diekspos melalui outlet dan tindakan, kita perlu menambahkan kode untuk mengaktifkan UI. Klik dua kali file ImageWindow.cs di Solution Pad dan buatlah terlihat seperti berikut ini:
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
}
}
Mari kita lihat kode ini secara rinci di bawah ini.
Pertama, kami mengekspos instans ImageDocument
kelas yang kami buat di atas:
private ImageDocument _document;
...
[Export ("Document")]
public ImageDocument Document {
get { return _document; }
set {
WillChangeValue ("Document");
_document = value;
DidChangeValue ("Document");
}
}
Dengan menggunakan Export
, WillChangeValue
dan DidChangeValue
, kami telah menyiapkan Document
properti untuk memungkinkan Pengodean Kunci-Nilai dan Pengikatan Data di Xcode.
Kami juga mengekspos Gambar dari sumur gambar yang kami tambahkan ke UI kami di Xcode dengan properti berikut:
public ViewController ImageViewController {
get { return ContentViewController as ViewController; }
}
public NSImage Image {
get {
return ImageViewController.Image;
}
set {
ImageViewController.Image = value;
}
}
Ketika Jendela Utama dimuat dan ditampilkan, kami membuat instans kelas kami ImageDocument
dan melampirkan gambar UI dengan baik ke dalamnya dengan kode berikut:
public override void AwakeFromNib ()
{
base.AwakeFromNib ();
// Create a new document instance
Document = new ImageDocument ();
// Attach to image view
Document.ImageView = ImageViewController.ContentView;
}
Terakhir, sebagai respons terhadap pengguna yang mengklik item toolbar salin dan tempel, kami memanggil instans ImageDocument
kelas untuk melakukan pekerjaan aktual:
partial void CopyImage (NSObject sender) {
Document.CopyImage(sender);
}
partial void PasteImage (Foundation.NSObject sender) {
Document.PasteImage(sender);
}
Mengaktifkan menu File dan Edit
Hal terakhir yang perlu kita lakukan adalah mengaktifkan item menu Baru dari menu File (untuk membuat instans baru dari jendela utama kita) dan untuk mengaktifkan item menu Potong, Salin, dan Tempel dari menu Edit.
Untuk mengaktifkan item menu Baru , edit file AppDelegate.cs dan tambahkan kode berikut:
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);
}
Untuk informasi selengkapnya, silakan lihat bagian Bekerja dengan Beberapa Windows dari dokumentasi Windows kami.
Untuk mengaktifkan item menu Potong, Salin, dan Tempel, edit file AppDelegate.cs dan tambahkan kode berikut:
[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);
}
Untuk setiap item menu, kita mendapatkan jendela kunci saat ini, paling atas, dan melemparkannya ke kelas kita ImageWindow
:
var window = NSApplication.SharedApplication.KeyWindow as ImageWindow;
Dari sana kita memanggil ImageDocument
instans kelas jendela tersebut untuk menangani tindakan salin dan tempel. Contohnya:
window.Document.CopyImage (sender);
Kami hanya ingin item menu Potong, Salin, dan Tempel dapat diakses jika ada data gambar di papan tempel default atau di sumur gambar jendela aktif saat ini.
Mari kita tambahkan file EditMenuDelegate.cs ke proyek Xamarin.Mac dan membuatnya terlihat seperti berikut:
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
}
}
Sekali lagi, kita mendapatkan jendela paling atas saat ini dan menggunakan instans kelasnya ImageDocument
untuk melihat apakah data gambar yang diperlukan ada. Kemudian kita menggunakan MenuWillHighlightItem
metode untuk mengaktifkan atau menonaktifkan setiap item berdasarkan status ini.
Edit file AppDelegate.cs dan buat DidFinishLaunching
metode terlihat seperti berikut ini:
public override void DidFinishLaunching (NSNotification notification)
{
// Disable automatic item enabling on the Edit menu
EditMenu.AutoEnablesItems = false;
EditMenu.Delegate = new EditMenuDelegate ();
}
Pertama, kami menonaktifkan pengaktifan dan penonaktifan item menu secara otomatis di menu Edit. Selanjutnya, kami melampirkan EditMenuDelegate
instans kelas yang kami buat di atas.
Untuk informasi selengkapnya, silakan lihat dokumentasi Menu kami.
Menguji aplikasi
Dengan segala sesuatu di tempat, kami siap untuk menguji aplikasi. Bangun dan jalankan aplikasi dan antarmuka utama ditampilkan:
Jika Anda membuka menu Edit, perhatikan bahwa Potong, Salin, dan Tempel dinonaktifkan karena tidak ada gambar di gambar dengan baik atau di papan tempel default:
Jika Anda menambahkan gambar ke gambar dengan baik dan membuka kembali menu Edit, item sekarang akan diaktifkan:
Jika Anda menyalin gambar dan memilih Baru dari menu file, Anda dapat menempelkan gambar tersebut ke jendela baru:
Di bagian berikut, kita akan melihat secara rinci bekerja dengan pasteboard di aplikasi Xamarin.Mac.
Tentang papan tempel
Di macOS (sebelumnya dikenal sebagai OS X) papan tempel (NSPasteboard
) menyediakan dukungan untuk beberapa proses server seperti Salin & Tempel, Seret & Letakkan dan Layanan Aplikasi. Di bagian berikut, kita akan melihat lebih dekat beberapa konsep papan tempel kunci.
Apa itu papan tempel?
Kelas ini NSPasteboard
menyediakan mekanisme standar untuk bertukar informasi antara aplikasi atau dalam aplikasi tertentu. Fungsi utama pasteboard adalah untuk menangani operasi salin dan tempel:
- Saat pengguna memilih item di aplikasi dan menggunakan item menu Potong atau Salin , satu atau beberapa representasi item yang dipilih ditempatkan di papan tempel.
- Saat pengguna menggunakan item menu Tempel (dalam aplikasi yang sama atau yang berbeda), versi data yang dapat ditanganinya disalin dari pasteboard dan ditambahkan ke aplikasi.
Penggunaan papan tempel yang kurang jelas termasuk operasi temukan, seret, seret dan letakkan, dan layanan aplikasi:
- Saat pengguna memulai operasi seret, data seret disalin ke papan tempel. Jika operasi seret berakhir dengan penurunan ke aplikasi lain, aplikasi tersebut menyalin data dari papan tempel.
- Untuk layanan terjemahan, data yang akan diterjemahkan disalin ke pasteboard oleh aplikasi yang meminta. Layanan aplikasi, mengambil data dari pasteboard, melakukan terjemahan, lalu menempelkan data kembali di papan tempel.
Dalam bentuk paling sederhana, pasteboard digunakan untuk memindahkan data di dalam aplikasi tertentu atau di antara aplikasi dan ada di area memori global khusus di luar proses aplikasi. Meskipun konsep papan tempel mudah dipahami, ada beberapa detail yang lebih kompleks yang harus dipertimbangkan. Ini akan dibahas secara rinci di bawah ini.
Papan tempel bernama
Papan tempel dapat bersifat publik atau privat dan dapat digunakan untuk berbagai tujuan dalam aplikasi atau di antara beberapa aplikasi. macOS menyediakan beberapa pasteboard standar, masing-masing dengan penggunaan tertentu yang terdefinisi dengan baik:
NSGeneralPboard
- Pasteboard default untuk operasi Potong, Salin, dan Tempel.NSRulerPboard
- Mendukung operasi Potong, Salin, dan Tempel pada Penggaris.NSFontPboard
- Mendukung operasi Potong, Salin, dan Tempel padaNSFont
objek.NSFindPboard
- Mendukung panel temuan khusus aplikasi yang dapat berbagi teks pencarian.NSDragPboard
- Mendukung operasi Seret & Lepas .
Untuk sebagian besar situasi, Anda akan menggunakan salah satu papan tempel yang ditentukan sistem. Tetapi mungkin ada situasi yang mengharuskan Anda membuat papan tempel Anda sendiri. Dalam situasi ini, Anda dapat menggunakan FromName (string name)
metode NSPasteboard
kelas untuk membuat papan tempel kustom dengan nama yang diberikan.
Secara opsional, Anda dapat memanggil CreateWithUniqueName
metode NSPasteboard
kelas untuk membuat papan tempel bernama unik.
Item papan tempel
Setiap bagian data yang ditulis aplikasi ke papan tempel dianggap sebagai Item Papan Tempel dan papan tempel dapat menyimpan beberapa item secara bersamaan. Dengan cara ini, aplikasi dapat menulis beberapa versi data yang disalin ke papan tempel (misalnya, teks biasa dan teks yang diformat) dan aplikasi pengambilan hanya dapat membaca data yang dapat diproses (seperti teks biasa saja).
Representasi data dan pengidentifikasi jenis seragam
Operasi pasteboard biasanya berlangsung antara dua aplikasi (atau lebih) yang tidak memiliki pengetahuan satu sama lain atau jenis data yang dapat ditangani masing-masing. Seperti yang dinyatakan dalam bagian di atas, untuk memaksimalkan potensi berbagi informasi, papan tempel dapat menyimpan beberapa representasi data yang disalin dan ditempelkan.
Setiap representasi diidentifikasi melalui Pengidentifikasi Jenis Seragam (UTI), yang tidak lebih dari string sederhana yang secara unik mengidentifikasi jenis tanggal yang disajikan (untuk informasi selengkapnya, silakan lihat dokumentasi Gambaran Umum Pengidentifikasi Jenis Seragam Apple).
Jika Anda membuat jenis data kustom (misalnya, objek gambar di aplikasi gambar vektor), Anda dapat membuat UTI Anda sendiri untuk mengidentifikasinya secara unik dalam operasi salin dan tempel.
Saat aplikasi bersiap untuk menempelkan data yang disalin dari papan tempel, aplikasi harus menemukan representasi yang paling sesuai dengan kemampuannya (jika ada). Biasanya ini akan menjadi jenis terkaya yang tersedia (misalnya teks yang diformat untuk aplikasi pemrosesan kata), kembali ke formulir paling sederhana yang tersedia sesuai kebutuhan (teks biasa untuk editor teks sederhana).
Data yang dijanjikan
Secara umum, Anda harus memberikan sebanyak mungkin representasi data yang disalin untuk memaksimalkan berbagi antar aplikasi. Namun, karena kendala waktu atau memori, mungkin tidak praktis untuk benar-benar menulis setiap jenis data ke dalam papan tempel.
Dalam situasi ini, Anda dapat menempatkan representasi data pertama di papan tempel dan aplikasi penerima dapat meminta representasi yang berbeda, yang dapat dihasilkan secara on-the-fly tepat sebelum operasi tempel.
Saat Anda menempatkan item awal di papan tempel, Anda akan menentukan bahwa satu atau beberapa representasi lain yang tersedia disediakan oleh objek yang sesuai NSPasteboardItemDataProvider
dengan antarmuka. Objek-objek ini akan memberikan representasi tambahan sesuai permintaan, seperti yang diminta oleh aplikasi penerima.
Ubah jumlah
Setiap papan tempel mempertahankan Jumlah Perubahan yang bertambah setiap kali pemilik baru dideklarasikan. Aplikasi dapat menentukan apakah konten papan tempel telah berubah sejak terakhir kali memeriksanya dengan memeriksa nilai Jumlah Perubahan.
ChangeCount
Gunakan metode NSPasteboard
dan ClearContents
kelas untuk memodifikasi Jumlah Perubahan papan tempel tertentu.
Menyalin data ke papan tempel
Anda melakukan operasi salin dengan terlebih dahulu mengakses pasteboard, menghapus konten yang ada dan menulis representasi data sebanyak yang diperlukan ke pasteboard.
Contohnya:
// 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});
Biasanya, Anda hanya akan menulis ke papan tempel umum, seperti yang telah kami lakukan dalam contoh di atas. Objek apa pun yang Anda kirim ke WriteObjects
metode harus sesuai dengan INSPasteboardWriting
antarmuka. Beberapa kelas bawaan (seperti NSString
, , NSImage
, NSURL
NSColor
, NSAttributedString
, dan NSPasteboardItem
) secara otomatis sesuai dengan antarmuka ini.
Jika Anda menulis kelas data kustom ke papan tempel, kelas tersebut harus sesuai dengan INSPasteboardWriting
antarmuka atau dibungkus dalam instans NSPasteboardItem
kelas (lihat bagian Jenis Data Kustom di bawah).
Membaca data dari papan tempel
Seperti yang dinyatakan di atas, untuk memaksimalkan potensi berbagi data antar aplikasi, beberapa representasi data yang disalin dapat ditulis ke pasteboard. Terserah aplikasi penerima untuk memilih versi terkaya yang mungkin untuk kemampuannya (jika ada).
Operasi tempel sederhana
Anda membaca data dari papan tempel menggunakan ReadObjectsForClasses
metode . Ini akan memerlukan dua parameter:
- Array jenis
NSObject
kelas berbasis yang ingin Anda baca dari papan tempel. Anda harus memesan ini dengan jenis data yang paling diinginkan terlebih dahulu, dengan jenis yang tersisa dalam preferensi yang menurun. - Kamus yang berisi batasan tambahan (seperti membatasi jenis konten URL tertentu) atau kamus kosong jika tidak ada batasan lebih lanjut yang diperlukan.
Metode mengembalikan array item yang memenuhi kriteria yang kami lewati dan oleh karena itu berisi paling banyak jumlah jenis data yang sama yang diminta. ada juga kemungkinan bahwa tidak ada jenis yang diminta dan array kosong akan dikembalikan.
Misalnya, kode berikut memeriksa untuk melihat apakah NSImage
ada di papan tempel umum dan menampilkannya dalam gambar dengan baik jika ada:
[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];
}
}
Meminta beberapa jenis data
Berdasarkan jenis aplikasi Xamarin.Mac yang sedang dibuat, aplikasi mungkin dapat menangani beberapa representasi data yang ditempelkan. Dalam situasi ini, ada dua skenario untuk mengambil data dari papan tempel:
- Lakukan satu panggilan ke
ReadObjectsForClasses
metode dan berikan array dari semua representasi yang Anda inginkan (dalam urutan yang disukai). - Lakukan beberapa panggilan ke
ReadObjectsForClasses
metode yang meminta array jenis yang berbeda setiap kali.
Lihat bagian Operasi Tempel Sederhana di atas untuk detail selengkapnya tentang mengambil data dari papan tempel.
Memeriksa jenis data yang sudah ada
Ada kalanya Anda mungkin ingin memeriksa apakah papan tempel berisi representasi data tertentu tanpa benar-benar membaca data dari papan tempel (seperti mengaktifkan item menu Tempel hanya ketika data yang valid ada).
CanReadObjectForClasses
Panggil metode papan tempel untuk melihat apakah berisi jenis tertentu.
Misalnya, kode berikut menentukan apakah pasteboard umum berisi NSImage
instans:
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);
}
}
Membaca url dari pasteboard
Berdasarkan fungsi aplikasi Xamarin.Mac tertentu, mungkin diperlukan untuk membaca URL dari pasteboard, tetapi hanya jika memenuhi serangkaian kriteria tertentu (seperti menunjuk ke file atau URL dari jenis data tertentu). Dalam situasi ini, Anda dapat menentukan kriteria pencarian tambahan menggunakan parameter kedua dari CanReadObjectForClasses
metode atau ReadObjectsForClasses
.
Jenis data kustom
Ada kalanya Anda perlu menyimpan jenis kustom Anda sendiri ke papan tempel dari aplikasi Xamarin.Mac. Misalnya, aplikasi gambar vektor yang memungkinkan pengguna menyalin dan menempelkan objek gambar.
Dalam situasi ini, Anda harus merancang kelas kustom data Anda sehingga mewarisi dan NSObject
sesuai dengan beberapa antarmuka (INSCoding
, INSPasteboardWriting
dan INSPasteboardReading
). Secara opsional, Anda dapat menggunakan NSPasteboardItem
untuk merangkum data yang akan disalin atau ditempelkan.
Kedua opsi ini akan dibahas secara rinci di bawah ini.
Menggunakan kelas kustom
Di bagian ini kita akan memperluas aplikasi contoh sederhana yang kita buat di awal dokumen ini dan menambahkan kelas kustom untuk melacak informasi tentang gambar yang kita salin dan tempel di antara jendela.
Tambahkan kelas baru ke proyek dan sebut saja ImageInfo.cs. Edit file dan buat terlihat seperti berikut ini:
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
}
}
Di bagian berikut, kita akan melihat kelas ini secara terperinci.
Pewarisan dan antarmuka
Sebelum kelas data kustom dapat ditulis atau dibaca dari papan tempel, kelas tersebut harus sesuai dengan INSPastebaordWriting
antarmuka dan INSPasteboardReading
. Selain itu, ia harus mewarisi dari NSObject
dan juga sesuai dengan INSCoding
antarmuka:
[Register("ImageInfo")]
public class ImageInfo : NSObject, INSCoding, INSPasteboardWriting, INSPasteboardReading
...
Kelas juga harus diekspos untuk Objective-C menggunakan direktif Register
dan harus mengekspos properti atau metode yang diperlukan menggunakan Export
. Contohnya:
[Export("name")]
public string Name { get; set; }
[Export("imageType")]
public string ImageType { get; set; }
Kami mengekspos dua bidang data yang akan dimuat kelas ini - nama gambar dan jenisnya (jpg, png, dll.).
Untuk informasi selengkapnya, lihat bagian Mengekspos kelas /metode C# ke Objective-Cbagian dokumentasi Xamarin.Mac Internals, ini menjelaskan Register
atribut dan Export
yang digunakan untuk menghubungkan kelas C# Anda ke Objective-C objek dan elemen UI.
Konstruktor
Dua konstruktor (diekspos dengan benar ke Objective-C) akan diperlukan untuk kelas data kustom kami sehingga dapat dibaca dari papan tempel:
[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 ();
}
Pertama, kami mengekspos konstruktor kosong di bawah metode init
default Objective-C .
Selanjutnya, kami mengekspos NSCoding
konstruktor yang sesuai yang akan digunakan untuk membuat instans baru objek dari pasteboard saat menempelkan dengan nama yang diekspor .initWithCoder
Konstruktor ini mengambil (seperti yang NSCoder
NSKeyedArchiver
dibuat oleh ketika ditulis ke pasteboard), mengekstrak data berpasangan kunci/nilai dan menyimpannya ke bidang properti kelas data.
Menulis ke papan tempel
Dengan menyesuaikan dengan INSPasteboardWriting
antarmuka, kita perlu mengekspos dua metode, dan secara opsional metode ketiga, sehingga kelas dapat ditulis ke pasteboard.
Pertama, kita perlu memberi tahu pasteboard representasi jenis data apa yang dapat ditulis oleh kelas kustom:
[Export ("writableTypesForPasteboard:")]
public virtual string[] GetWritableTypesForPasteboard (NSPasteboard pasteboard) {
string[] writableTypes = {"com.xamarin.image-info", "public.text"};
return writableTypes;
}
Setiap representasi diidentifikasi melalui Pengidentifikasi Jenis Seragam (UTI), yang tidak lebih dari string sederhana yang secara unik mengidentifikasi jenis data yang disajikan (untuk informasi selengkapnya, silakan lihat dokumentasi Gambaran Umum Pengidentifikasi Jenis Seragam Apple).
Untuk format kustom kami, kami membuat UTI kami sendiri: "com.xamarin.image-info" (perhatikan bahwa dalam notasi terbalik sama seperti Pengidentifikasi Aplikasi). Kelas kami juga mampu menulis string standar ke pasteboard (public.text
).
Selanjutnya, kita perlu membuat objek dalam format yang diminta yang benar-benar ditulis ke pasteboard:
[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;
}
Untuk jenisnya public.text
, kita mengembalikan objek sederhana yang diformat NSString
. Untuk jenis kustom com.xamarin.image-info
, kami menggunakan antarmuka NSKeyedArchiver
dan NSCoder
untuk mengodekan kelas data kustom ke arsip berpasangan kunci/nilai. Kita perlu menerapkan metode berikut untuk benar-benar menangani pengodean:
[Export ("encodeWithCoder:")]
public void EncodeTo (NSCoder encoder) {
// Encode data
encoder.Encode(new NSString(Name),"name");
encoder.Encode(new NSString(ImageType),"imageType");
}
Pasangan kunci/nilai individual ditulis ke encoder dan akan didekodekan menggunakan konstruktor kedua yang kami tambahkan di atas.
Secara opsional, kita dapat menyertakan metode berikut untuk menentukan opsi apa pun saat menulis data ke pasteboard:
[Export ("writingOptionsForType:pasteboard:"), CompilerGenerated]
public virtual NSPasteboardWritingOptions GetWritingOptionsForType (string type, NSPasteboard pasteboard) {
return NSPasteboardWritingOptions.WritingPromised;
}
Saat ini hanya opsi yang WritingPromised
tersedia dan harus digunakan ketika jenis tertentu hanya dijanjikan dan tidak benar-benar ditulis ke pasteboard. Untuk informasi selengkapnya, silakan lihat bagian Data yang Dijanjikan di atas.
Dengan metode ini di tempat, kode berikut dapat digunakan untuk menulis kelas kustom kami ke pasteboard:
// Get the standard pasteboard
var pasteboard = NSPasteboard.GeneralPasteboard;
// Empty the current contents
pasteboard.ClearContents();
// Add info to the pasteboard
pasteboard.WriteObjects (new ImageInfo[] { Info });
Membaca dari papan tempel
Dengan sesuai dengan INSPasteboardReading
antarmuka, kita perlu mengekspos tiga metode sehingga kelas data kustom dapat dibaca dari pasteboard.
Pertama, kita perlu memberi tahu papan tempel representasi jenis data apa yang dapat dibaca kelas kustom dari clipboard:
[Export ("readableTypesForPasteboard:")]
public static string[] GetReadableTypesForPasteboard (NSPasteboard pasteboard){
string[] readableTypes = {"com.xamarin.image-info", "public.text"};
return readableTypes;
}
Sekali lagi, ini didefinisikan sebagai UTI sederhana dan merupakan jenis yang sama yang kami tentukan dalam bagian Menulis ke Papan Tempel di atas.
Selanjutnya, kita perlu memberi tahu papan tempel bagaimana masing-masing jenis UTI akan dibaca menggunakan metode berikut:
[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;
}
Untuk jenisnya com.xamarin.image-info
, kami memberi tahu papan tempel untuk mendekode pasangan kunci/nilai yang kami buat dengan NSKeyedArchiver
saat menulis kelas ke papan tempel dengan memanggil initWithCoder:
konstruktor yang kami tambahkan ke kelas .
Terakhir, kita perlu menambahkan metode berikut untuk membaca representasi data UTI lainnya dari pasteboard:
[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;
}
Dengan semua metode ini di tempat, kelas data kustom dapat dibaca dari papan tempel menggunakan kode berikut:
// 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];
}
Menggunakan NSPasteboardItem
Mungkin ada kalanya Anda perlu menulis item kustom ke papan tempel yang tidak menjamin pembuatan kelas kustom atau Anda ingin memberikan data dalam format umum, hanya jika diperlukan. Untuk situasi ini, Anda dapat menggunakan NSPasteboardItem
.
Memberikan NSPasteboardItem
kontrol terperincah atas data yang ditulis ke papan tempel dan dirancang untuk akses sementara - harus dibuang setelah ditulis ke papan tempel.
Data tulis
Untuk menulis data kustom Anda ke, NSPasteboardItem
Anda harus menyediakan kustom NSPasteboardItemDataProvider
. Tambahkan kelas baru ke proyek dan sebut saja ImageInfoDataProvider.cs. Edit file dan buat terlihat seperti berikut ini:
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
}
}
Seperti yang kami lakukan dengan kelas data kustom, kita perlu menggunakan arahan Register
dan Export
untuk mengeksposnya ke Objective-C. Kelas harus mewarisi dari NSPasteboardItemDataProvider
dan harus menerapkan FinishedWithDataProvider
metode dan ProvideDataForType
.
ProvideDataForType
Gunakan metode untuk memberikan data yang akan dibungkus dalam NSPasteboardItem
sebagai berikut:
[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;
}
}
Dalam hal ini, kami menyimpan dua informasi tentang gambar kami (Nama dan ImageType) dan menulisnya ke string sederhana (public.text
).
Ketik tulis data ke pasteboard, gunakan kode berikut:
// 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 });
}
Membaca data
Untuk membaca kembali data dari pasteboard, gunakan kode berikut:
// 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
...
}
Ringkasan
Artikel ini telah melihat secara rinci tentang bekerja dengan papan tempel di aplikasi Xamarin.Mac untuk mendukung operasi salin dan tempel. Pertama, ini memperkenalkan contoh sederhana untuk membuat Anda terbiasa dengan operasi papan tempel standar. Selanjutnya, dibutuhkan tampilan terperinci pada papan tempel dan cara membaca dan menulis data darinya. Akhirnya, ia melihat menggunakan jenis data kustom untuk mendukung penyalinan dan penempelan jenis data kompleks dalam aplikasi.