|
1 | | ---- |
2 | | -id: clipboard |
3 | | -title: Clipboard |
4 | | ---- |
5 | | - |
6 | | -The `Clipboard` class enables interacting with the system clipboard, providing functionality for setting and retrieving text, clearing the clipboard, handling data objects and working with different data formats. |
7 | | - |
8 | | -<GitHubSampleLink title="Clipboard" link="https://github.com/AvaloniaUI/AvaloniaUI.QuickGuides/tree/main/ClipboardOps"/> |
9 | | - |
10 | | - |
11 | | -The `Clipboard` can be access through an instance of `TopLevel` or `Window`, for more details on accessing `TopLevel` please visit [TopLevel](/docs/concepts/toplevel) page: |
12 | | -```cs |
13 | | -var clipboard = window.Clipboard; |
14 | | -``` |
15 | | - |
16 | | -## Methods |
17 | | - |
18 | | -### GetTextAsync() |
19 | | - |
20 | | -Retrieves text from the clipboard asynchronously. The resulting value of the task is the text from the clipboard. If the clipboard doesn't contain text or is empty, the method returns `null`. |
21 | | - |
22 | | -```cs |
23 | | -Task<string?> GetTextAsync() |
24 | | -``` |
25 | | - |
26 | | -:::note |
27 | | -Avalonia clipboard always operates with Unicode text. |
28 | | -::: |
29 | | - |
30 | | -### SetTextAsync(string? text) |
31 | | -Sets the clipboard text asynchronously and flushes it immediately. This method accepts a `string?` parameter for a text that needs to be copied. If the provided text is `null`, the clipboard will be cleared. |
32 | | - |
33 | | -```cs |
34 | | -Task SetTextAsync(string? text) |
35 | | -``` |
36 | | - |
37 | | -:::note |
38 | | -Unlikely different Win32 clipboard APIs, Avalonia clipboard always flushes data and is never delayed. |
39 | | -::: |
40 | | - |
41 | | -### ClearAsync() |
42 | | -Clears the clipboard asynchronously flushes it immediately. |
43 | | - |
44 | | -```cs |
45 | | -Task ClearAsync() |
46 | | -``` |
47 | | - |
48 | | -### SetDataObjectAsync(IDataObject data) |
49 | | -Sets the clipboard content to the specified data object asynchronously. This method accepts an `IDataObject` parameter. The data object can contain multiple data formats. |
50 | | - |
51 | | -```cs |
52 | | -Task SetDataObjectAsync(IDataObject data) |
53 | | -``` |
54 | | - |
55 | | -:::note |
56 | | -Unlikely different Win32 clipboard APIs, Avalonia clipboard always flushes data and is never delayed. |
57 | | -::: |
58 | | - |
59 | | -### GetFormatsAsync() |
60 | | -Retrieves the list of formats currently stored in the clipboard asynchronously. The resulting value of the task is an array of string format names. |
61 | | - |
62 | | -```cs |
63 | | -Task<string[]> GetFormatsAsync() |
64 | | -``` |
65 | | - |
66 | | -### GetDataAsync(string format) |
67 | | -Retrieves data in the specified format from the clipboard asynchronously. This method returns a `Task<object?>` that represents the operation. The resulting value of the task is the clipboard data in the specified format. If there's no data in the clipboard in the specified format, the method returns `null`. |
68 | | - |
69 | | -```cs |
70 | | -Task<object?> GetDataAsync(string format) |
71 | | -``` |
72 | | - |
73 | | -## Creating a DataObject to be sent to the clipboard |
74 | | - |
75 | | -You can store objects on the clipboard on some platforms with different formats. |
76 | | - |
77 | | -```csharp title='C#' |
78 | | -private async void CopyButton_OnClick(object? sender, RoutedEventArgs args) |
79 | | -{ |
80 | | - var clipboard = TopLevel.GetTopLevel(this)?.Clipboard; |
81 | | - var dataObject = new DataObject(); |
82 | | - dataObject.Set(DataFormats.Text, "Hello World"); |
83 | | - await clipboard.SetDataObjectAsync(dataObject); |
84 | | -} |
85 | | -``` |
86 | | - |
87 | | -## Platform compatibility: |
88 | | - |
89 | | -| Feature | Windows | macOS | Linux x11 | Browser | Android | iOS | |
90 | | -|---------------|-------|-------|-------|-------|-------|-------| |
91 | | -| `GetTextAsync` | ✔ | ✔ | ✔ | ✔** | ✔ | ✔ | |
92 | | -| `SetTextAsync` | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | |
93 | | -| `ClearAsync` | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | |
94 | | -| `GetFormatsAsync` | ✔ | ✔ | ✔ | ✖* | ✖* | ✖* | |
95 | | -| `SetDataObjectAsync` | ✔ | ✔ | ✔ | ✖* | ✖* | ✖* | |
96 | | -| `GetDataAsync` | ✔ | ✔ | ✔ | ✖* | ✖* | ✖* | |
97 | | - |
98 | | -\* Technically possible, but wasn't implemented yet. Contributions are welcome! |
99 | | - |
100 | | -** In the Mozilla browser, the GetTextAsync method only functions after the "Paste" gesture has been triggered, typically by using Ctrl+V. |
| 1 | +--- |
| 2 | +id: clipboard |
| 3 | +title: Clipboard |
| 4 | +--- |
| 5 | + |
| 6 | +## Data Formats |
| 7 | + |
| 8 | +Before accessing the clipboard, it's important to understand the data format, represented by the `DataFormat<T>` class. It represents the format of a data item (e.g., text, HTML, PNG, etc.) and is used by various clipboard and drag-and-drop APIs. |
| 9 | + |
| 10 | +A data format consists of a kind (`Universal`, `Platform`, or `Application`), an identifier, and a data type. |
| 11 | + |
| 12 | +Data formats are considered equal if they have the same kind and identifier. |
| 13 | + |
| 14 | +### Universal Formats |
| 15 | + |
| 16 | +Universal formats are cross-platform formats that Avalonia directly understands. |
| 17 | +There are currently three universal formats: |
| 18 | + |
| 19 | +| Format | Identifier | Type | Description | |
| 20 | +| --------------------|------------|----------------|---------------------| |
| 21 | +| `DataFormat.Text` | "Text" | `string` | Plain text data | |
| 22 | +| `DataFormat.File` | "File" | `IStorageItem` | A file or directory | |
| 23 | +| `DataFormat.Bitmap` | "Bitmap" | `Bitmap` | A bitmap image | |
| 24 | + |
| 25 | +### Platform Formats |
| 26 | + |
| 27 | +Platform format is compatible **only with the current platform** that the application is running on (e.g., Windows, Linux, iOS, etc.). Its identifier should be a name understood by the underlying platform. Use this format only when you need direct interoperability with the target platform and have precise knowledge of its encoding or serialization. |
| 28 | + |
| 29 | +:::warning |
| 30 | +Do **not** assume that a given identifier works for all platforms! |
| 31 | +For example, the HTML format is named `HTML format` on Windows, `text/html` on Linux and Android, and `public.html` on macOS and iOS. Always verify the intended operating system before using a platform-specific format. |
| 32 | +::: |
| 33 | + |
| 34 | +A platform format can be defined using `DataFormat.CreateBytesPlatformFormat` or `DataFormat.CreateStringPlatformFormat`, respectively using `byte[]` and `string` types. Avalonia does not perform automatic serialization. |
| 35 | + |
| 36 | +Example: |
| 37 | +```csharp |
| 38 | +if (OperatingSystem.IsMacOS()) |
| 39 | +{ |
| 40 | + var macOSHtmlFormat = DataFormat.CreateStringPlatformFormat("public.html"); |
| 41 | +} |
| 42 | +``` |
| 43 | + |
| 44 | +### Application Formats |
| 45 | + |
| 46 | +An application format is specific to your application and works with all platforms that allow custom data formats. Its identifier is restricted to ASCII letters, digits, plus dot and dash symbols (`A`-`Z`, `a`-`z`, `0`-`9`, `.`, `-`). This identifier will not be exposed directly to the underlying platform; instead, it will be internally prefixed to avoid conflicts with platform formats. |
| 47 | + |
| 48 | +Use this format when you need to place application-specific data on the clipboard, for example, to share it between multiple instances of the program. |
| 49 | + |
| 50 | +An application format can be defined using `DataFormat.CreateBytesApplicationFormat` or `DataFormat.CreateStringApplicationFormat`, respectively using `byte[]` and `string` types. Avalonia does not perform automatic serialization. |
| 51 | + |
| 52 | +```csharp |
| 53 | +var myFormat = DataFormat.CreateBytesApplicationFormat("mycompany-myapp-myformat"); |
| 54 | +``` |
| 55 | + |
| 56 | +## IClipboard |
| 57 | + |
| 58 | +The `IClipboard` interface enables interaction with the system clipboard to set and retrieve text, image, and custom data formats. |
| 59 | + |
| 60 | +An instance of `IClipboard` can be accessed through a [`TopLevel`](/docs/concepts/toplevel) object: |
| 61 | +```csharp |
| 62 | +var clipboard = window.Clipboard; |
| 63 | +``` |
| 64 | + |
| 65 | +### Reading |
| 66 | + |
| 67 | +#### TryGetDataAsync() |
| 68 | + |
| 69 | +The clipboard's content is read by retrieving an instance of `IAsyncDataTransfer`. This object is responsible for providing data items of various formats on demand (see the [`IAsyncDataTransfer`](#iasyncdatatransfer--iasyncdatatransferitem) section below). |
| 70 | + |
| 71 | +The `TryGetDataAsync` method asynchronously retrieves an `IAsyncDataTransfer` object representing the clipboard's contents. If the clipboard is empty, `null` is returned. |
| 72 | + |
| 73 | +```csharp |
| 74 | +using var data = await clipboard.TryGetDataAsync(); |
| 75 | +``` |
| 76 | + |
| 77 | +:::warning |
| 78 | +Since the clipboard's contents may change at any time, it is recommended to use the returned `IAsyncDataTransfer` instance as soon as possible; do not store it for later use. After all the necessary operations are done on the object, it must be disposed of by the caller; a `using` statement is therefore recommended. |
| 79 | +::: |
| 80 | + |
| 81 | +#### Extension Methods |
| 82 | + |
| 83 | +To retrieve data using a specific format, several extension methods are available. You can use the following for common formats, instead of `TryGetDataAsync()`: |
| 84 | + |
| 85 | +- `TryGetValueAsync(DataFormat<T>)` returns a single value of type `T` matching the specified data format from the clipboard, or `null` if none is present. |
| 86 | +- `TryGetValuesAsync(DataFormat<T>)` returns multiple values of type `T` matching the specified data format from the clipboard, or an empty array if none is present. |
| 87 | +- `TryGetTextAsync()` returns a single `string` value matching the `DataFormat.Text` format, or `null` if none is present. |
| 88 | +- `TryGetFileAsync()` returns a single `IStorageItem` object matching the `DataFormat.File` format, or `null` if none is present. |
| 89 | +- `TryGetFilesAsync()` returns multiple `IStorageItem` objects matching the `DataFormat.File` format, or an empty array if none is present. |
| 90 | +- `TryGetBitmapAsync()` returns a single `Bitmap` image matching the `DataFormat.Bitmap` format, or `null` if none is present. |
| 91 | + |
| 92 | +If several values are present on the clipboard when the methods returning a single value are called, the first one is used. |
| 93 | + |
| 94 | +Examples: |
| 95 | + |
| 96 | +```csharp |
| 97 | +var text = await clipboard.TryGetTextAsync(); |
| 98 | +Console.WriteLine($"Clipboard text: {text}"); |
| 99 | + |
| 100 | +var file = await clipboard.TryGetFileAsync(); |
| 101 | +Console.WriteLine($"Clipboard file: {file?.Path}"); |
| 102 | + |
| 103 | +var bitmap = await clipboard.TryGetBitmapAsync(); |
| 104 | +Console.WriteLine($"Clipboard image: {bitmap?.PixelSize}"); |
| 105 | +``` |
| 106 | + |
| 107 | +:::tip |
| 108 | +Each of these extension methods calls `TryGetDataAsync()` under the hood. If you plan to use several of these in a row, consider calling [`TryGetDataAsync()`](#trygetdataasync) once instead to obtain an `IAsyncDataTransfer`, then read the expected values from that object. |
| 109 | +::: |
| 110 | + |
| 111 | +#### TryGetInProcessDataAsync() |
| 112 | + |
| 113 | +This method retrieves the exact `IAsyncDataTransfer` instance previously placed on the clipboard by `SetDataAsync()` if it's still present. If the clipboard's content has changed, has been flushed, or if the platform does not support lazily provided values, this method returns `null`. |
| 114 | + |
| 115 | +Calling this method avoids accessing the underlying platform's clipboard, which can be useful when fine-grained control is needed. For most scenarios, using `TryGetDataAsync()` is the preferred option. |
| 116 | + |
| 117 | +This method is supported on Windows, macOS, and X11. |
| 118 | + |
| 119 | +### Writing |
| 120 | + |
| 121 | +#### SetDataAsync() |
| 122 | + |
| 123 | +To place data on the clipboard, call the `IClipboard.SetDataAsync(IAsyncDataTransfer)` method. It accepts an implementation of `IAsyncDataTransfer` that is responsible for providing data items of various formats on demand (see the [`IAsyncDataTransfer`](#iasyncdatatransfer--iasyncdatatransferitem) section below). |
| 124 | + |
| 125 | +Example: |
| 126 | +```csharp |
| 127 | +var data = new DataTransfer(); |
| 128 | +data.Add(DataTransferItem.CreateText("Copied from Avalonia!")); |
| 129 | +await clipboard.SetDataAsync(data); |
| 130 | +``` |
| 131 | + |
| 132 | +:::note |
| 133 | +Placing a new object on the clipboard always clears any previous data. |
| 134 | +::: |
| 135 | + |
| 136 | +:::warning |
| 137 | +Do **not** call `Dispose()` on the `IAsyncDataTransfer` object passed to `SetDataAsync()`! The instance must stay valid while it's on the clipboard. Avalonia takes ownership and automatically disposes of it once it's no longer in use. |
| 138 | +::: |
| 139 | + |
| 140 | +#### Extension Methods |
| 141 | + |
| 142 | +To write a single specific format, several extension methods are available for convenience. You can use the following for common formats: |
| 143 | + |
| 144 | +- `SetValueAsync(DataFormat<T>, T)` sets a single value of type `T` with the specified data format on the clipboard. |
| 145 | +- `SetValuesAsync(DataFormat<T>, IEnumerable<T>)` sets multiple values of type `T` with the specified data formats on the clipboard. |
| 146 | +- `SetTextAsync(string)` sets a single `string` value with the `DataFormat.Text` format on the clipboard. |
| 147 | +- `SetFileAsync(IStorageItem)` sets a single `IStorageItem` object with the `DataFormat.File` format on the clipboard. |
| 148 | +- `SetFilesAsync(IEnumerable<IStorageItem>)` sets multiple `IStorageItem` objects with the `DataFormat.File` format on the clipboard. |
| 149 | +- `SetBitmapAsync(Bitmap)` sets a single `Bitmap` image with the `DataFormat.Bitmap` format on the clipboard. |
| 150 | + |
| 151 | +#### ClearAsync() |
| 152 | + |
| 153 | +Calling the `ClearAsync()` method clears the clipboard of all contents. |
| 154 | + |
| 155 | +#### FlushAsync() |
| 156 | + |
| 157 | +On Windows, macOS, and X11, the data is retrieved lazily on demand from the `IAsyncDataTransfer` placed on the clipboard. |
| 158 | +If the Avalonia application terminates, the data becomes unavailable. |
| 159 | + |
| 160 | +On Windows, calling `FlushAsync()` allows the system to query all the data present on the clipboard and persist it. On other platforms, this method does nothing. |
| 161 | + |
| 162 | +## IAsyncDataTransfer & IAsyncDataTransferItem |
| 163 | + |
| 164 | +The `IAsyncDataTransfer` interface represents the contents of the clipboard and exposes the following properties: |
| 165 | +- `Formats`, returning a list of `DataFormat` instances representing all formats present inside the object. |
| 166 | +- `Items`, returning a list of `IAsyncDataTransferItem` instances representing all items present inside the object. |
| 167 | + |
| 168 | +The `IAsyncDataTransferItem` interface represents a single item inside an `IAsyncDataTransfer` and exposes the following members: |
| 169 | +- `Formats`, returning a list of `DataFormat` instances representing all formats present inside the object. |
| 170 | +- `TryGetRawAsync(DataFormat)`, used to retrieve a single value in a given data format asynchronously. |
| 171 | + |
| 172 | +:::info |
| 173 | +A single item can have multiple formats. |
| 174 | +For example, rich text might be represented in RTF, HTML, and plain text. |
| 175 | +::: |
| 176 | + |
| 177 | +### Getting values |
| 178 | + |
| 179 | +#### Raw |
| 180 | + |
| 181 | +To read values from an `IAsyncDataTransferItem` object, call the `TryGetRawAsync(DataFormat)` method by specifying the requested data format. |
| 182 | + |
| 183 | +:::tip |
| 184 | +The returned value is untyped (`object`). |
| 185 | +Consider using the extension methods described below to get typed results. |
| 186 | +::: |
| 187 | + |
| 188 | +#### Typed |
| 189 | + |
| 190 | +Several extension methods are provided to retrieve typed values from `IAsyncDataTransfer` and `IAsyncDataTransferItem`: |
| 191 | +- `TryGetValueAsync(DataFormat<T>)` returns a single value of type `T` matching the specified data format, or `null` if none is present. |
| 192 | +- `TryGetTextAsync()` returns a single `string` value matching the `DataFormat.Text` format, or `null` if none is present. |
| 193 | +- `TryGetFileAsync()` returns a single `IStorageItem` object matching the `DataFormat.File` format, or `null` if none is present. |
| 194 | +- `TryGetBitmapAsync()` returns a single `Bitmap` image matching the `DataFormat.Bitmap` format, or `null` if none is present. |
| 195 | + |
| 196 | +When called on `IAsyncDataTransfer`, if multiple items match the requested format, the first is used. |
| 197 | + |
| 198 | +In addition, the following extension methods exist only for `IAsyncDataTransfer`: |
| 199 | +- `TryGetValuesAsync(DataFormat<T>)` returns multiple values of type `T` matching the specified data format, or an empty array if none is present. |
| 200 | +- `TryGetFilesAsync()` returns multiple `IStorageItem` objects matching the `DataFormat.File` format, or an empty array if none is present. |
| 201 | + |
| 202 | +### Implementation |
| 203 | + |
| 204 | +#### DataTransfer & DataTransferItem |
| 205 | + |
| 206 | +To provide values to write to the clipboard, both `IAsyncDataTransfer` and `IAsyncDataTransferItem` must be implemented. |
| 207 | + |
| 208 | +Avalonia provides implementations of those interfaces with the `DataTransfer` and `DataTransferItem` types, respectively: |
| 209 | +- The `DataTransfer` class is a list of items. It provides an `Add(DataTransferItem)` method used to add new items. |
| 210 | +- The `DataTransferItem` class can be considered a dictionary of format and value pairs. It provides a `Set<T>(DataFormat, T)` method used to set the value for a given format. |
| 211 | + |
| 212 | +Example: |
| 213 | + |
| 214 | +```csharp |
| 215 | +// Creates an item with both text and HTML formats. |
| 216 | +var item = new DataTransferItem(); |
| 217 | +item.Set(DataFormat.Text, "From Avalonia!"); |
| 218 | +item.Set(DataFormat.CreateStringPlatformFormat("text/html"), "From <b>Avalonia</b>!"); |
| 219 | + |
| 220 | +// Adds the item to the DataTransfer. |
| 221 | +var data = new DataTransfer(); |
| 222 | +data.Add(item); |
| 223 | +``` |
| 224 | + |
| 225 | +The `Set<T>` method also has an overload `Set<T>(DataFormat, Func<T>)`, allowing the value to be provided lazily. |
| 226 | + |
| 227 | +For convenience, `DataTransferItem` provides methods to set the value for common formats: |
| 228 | +- `SetText(string)` sets a `string` value for the `DataFormat.Text` format. |
| 229 | +- `SetFile(IStorageItem)` sets an `IStorageItem` object for the `DataFormat.File` format. |
| 230 | +- `SetBitmap(Bitmap)` sets a `Bitmap` image for the `DataFormat.Bitmap` format. |
| 231 | + |
| 232 | +Additionally, static factories exist to create items with a single format: |
| 233 | +- `DataTransferItem.Create<T>(DataFormat<T>, T)` for a given format. |
| 234 | +- `DataTransferItem.CreateText(string)` for the `DataFormat.Text` format. |
| 235 | +- `DataTransferItem.CreateFile(IStorageItem)` for the `DataFormat.File` format. |
| 236 | +- `DataTransferItem.CreateBitmap(Bitmap)` for the `DataFormat.Bitmap` format. |
| 237 | + |
| 238 | +#### Custom |
| 239 | + |
| 240 | +For advanced scenarios, you can implement `IAsyncDataTransfer` and `IAsyncDataTransferItem` manually. One possible use is to dynamically provide a variety of formats for a given item. |
| 241 | + |
| 242 | +When writing the implementation, be certain that: |
| 243 | +- [`DataTransfer` and `DataTransferItem`](#datatransfer--datatransferitem) are not enough for your needs. |
| 244 | +- `IAsyncDataTransfer.Formats` contains the formats of all items, without duplicates. |
| 245 | +- You are aware `IAsyncDataTransferItem.TryGetRawAsync()` can be called from any thread and does not use the dispatcher. |
| 246 | +- You have considered implementing `IDataTransfer` and `IDataTransferItem`, as some platforms can only access the clipboard synchronously. |
| 247 | + |
| 248 | +:::warning |
| 249 | +`IAsyncDataTransferItem.TryGetRawAsync()` may or may not be called on the UI thread depending on the underlying platform. Do **not** call anything on the UI thread, including via `Dispatcher.Invoke/InvokeAsync`. Doing so will result in deadlocks! |
| 250 | +::: |
0 commit comments