• Articles
  • Api Documentation
Show / Hide Table of Contents
  • Introduction
  • Window Management
  • Clipboard Usage
  • DPI Awareness
  • Input Handling
  • Common Scenarios

Clipboard Usage Guide

This guide covers clipboard monitoring and manipulation using the Dapplo.Windows.Clipboard package.

Overview

The Clipboard package provides a reactive API for monitoring and manipulating the Windows clipboard. It uses Reactive Extensions (System.Reactive) for event handling.

Installation

Install-Package Dapplo.Windows.Clipboard

Monitoring Clipboard Changes

Basic Monitoring

using Dapplo.Windows.Clipboard;
using System;

// Subscribe to all clipboard changes
var subscription = ClipboardNative.OnUpdate.Subscribe(info =>
{
    Console.WriteLine($"Clipboard changed!");
    Console.WriteLine($"Available formats: {string.Join(", ", info.Formats)}");
    Console.WriteLine($"Sequence ID: {info.Id}");
});

// Dispose when done
subscription.Dispose();

Filter by Format

using Dapplo.Windows.Clipboard;
using System.Reactive.Linq;

// Only react to text changes
var textSubscription = ClipboardNative.OnUpdate
    .Where(info => info.Formats.Contains("Text") || info.Formats.Contains("UnicodeText"))
    .Subscribe(info =>
    {
        Console.WriteLine("Text copied to clipboard");
    });

// Only react to image changes
var imageSubscription = ClipboardNative.OnUpdate
    .Where(info => info.Formats.Contains("PNG") || info.Formats.Contains("Bitmap"))
    .Subscribe(info =>
    {
        Console.WriteLine("Image copied to clipboard");
    });

// Only react to files
var fileSubscription = ClipboardNative.OnUpdate
    .Where(info => info.Formats.Contains("FileDrop"))
    .Subscribe(info =>
    {
        Console.WriteLine("Files copied to clipboard");
    });

Thread Synchronization

Ensure clipboard operations run on the correct thread:

using Dapplo.Windows.Clipboard;
using System.Reactive.Linq;
using System.Reactive.Concurrency;

// In a WPF or Windows Forms application
var subscription = ClipboardNative.OnUpdate
    .ObserveOn(SynchronizationContext.Current) // Run on UI thread
    .Subscribe(info =>
    {
        // Safe to update UI here
        UpdateClipboardStatus(info.Formats);
    });

Reading Clipboard Content

Access Clipboard

Always use the Access() method to safely access the clipboard:

using Dapplo.Windows.Clipboard;

using (var clipboard = ClipboardNative.Access())
{
    // Clipboard is now locked for your exclusive use
    var formats = clipboard.AvailableFormats();
    Console.WriteLine($"Available formats: {string.Join(", ", formats)}");
}
// Clipboard is automatically released

Read Text

using Dapplo.Windows.Clipboard;

using (var clipboard = ClipboardNative.Access())
{
    if (clipboard.AvailableFormats().Contains("Text"))
    {
        string text = clipboard.GetAsString();
        Console.WriteLine($"Clipboard text: {text}");
    }
}

Read Files

using Dapplo.Windows.Clipboard;
using System.Collections.Generic;

using (var clipboard = ClipboardNative.Access())
{
    if (clipboard.AvailableFormats().Contains("FileDrop"))
    {
        IEnumerable<string> files = clipboard.GetAsFileNames();
        foreach (var file in files)
        {
            Console.WriteLine($"File: {file}");
        }
    }
}

Read Images

using Dapplo.Windows.Clipboard;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;

using (var clipboard = ClipboardNative.Access())
{
    // Read as stream (PNG format)
    if (clipboard.AvailableFormats().Contains("PNG"))
    {
        using var stream = clipboard.GetAsStream("PNG");
        using var fileStream = File.Create("clipboard_image.png");
        stream.CopyTo(fileStream);
    }

    // Read as bitmap
    if (clipboard.AvailableFormats().Contains("Bitmap"))
    {
        // Note: Direct bitmap reading may require additional code
        using var stream = clipboard.GetAsStream("Bitmap");
        // Process bitmap stream
    }
}

Read Custom Formats

using Dapplo.Windows.Clipboard;
using System.IO;

using (var clipboard = ClipboardNative.Access())
{
    string customFormat = "MyApplication.CustomFormat";

    if (clipboard.AvailableFormats().Contains(customFormat))
    {
        // Read as stream
        using var stream = clipboard.GetAsStream(customFormat);

        // Read as bytes
        byte[] data = clipboard.GetAsBytes(customFormat);

        Console.WriteLine($"Custom data size: {data.Length} bytes");
    }
}

Writing to Clipboard

Set Text

using Dapplo.Windows.Clipboard;

using (var clipboard = ClipboardNative.Access())
{
    clipboard.SetAsUnicodeString("Hello, World!");
}

Set Files

using Dapplo.Windows.Clipboard;
using System.Collections.Generic;

var files = new List<string>
{
    @"C:\path\to\file1.txt",
    @"C:\path\to\file2.txt"
};

using (var clipboard = ClipboardNative.Access())
{
    clipboard.SetAsFileNames(files);
}

Set Custom Format

using Dapplo.Windows.Clipboard;
using System.Text;

using (var clipboard = ClipboardNative.Access())
{
    string customFormat = "MyApplication.CustomFormat";
    byte[] data = Encoding.UTF8.GetBytes("Custom clipboard data");

    clipboard.SetAsBytes(customFormat, data);
}

Set Multiple Formats

You can set multiple formats at once:

using Dapplo.Windows.Clipboard;
using System.Text;

using (var clipboard = ClipboardNative.Access())
{
    string text = "Hello, World!";

    // Set as both Unicode text and HTML
    clipboard.SetAsUnicodeString(text);

    string html = $"<html><body>{text}</body></html>";
    clipboard.SetAsBytes("HTML Format", Encoding.UTF8.GetBytes(html));
}

Advanced Scenarios

Delayed Rendering

Delayed rendering allows you to provide clipboard data only when it's actually requested:

using Dapplo.Windows.Clipboard;
using System;

// Subscribe to render requests
var renderSubscription = ClipboardNative.OnRenderFormat.Subscribe(request =>
{
    if (request.IsDestroyClipboard)
    {
        // Clean up any delayed rendering resources
        return;
    }

    if (request.RenderAllFormats)
    {
        // Render all delayed formats
        return;
    }

    // Render specific format on demand
    switch (request.RequestedFormat)
    {
        case "MyFormat":
            using (var clipboard = ClipboardNative.Access())
            {
                byte[] data = GenerateLargeData(); // Only generate when needed
                clipboard.SetAsBytes("MyFormat", data);
            }
            break;
    }
});

// Set clipboard with delayed rendering
using (var clipboard = ClipboardNative.Access())
{
    clipboard.SetAsDelayedRenderer("MyFormat");
}

Clear Clipboard

using Dapplo.Windows.Clipboard;

using (var clipboard = ClipboardNative.Access())
{
    clipboard.Clear();
}

Monitor Clipboard in Reactive Pipeline

using Dapplo.Windows.Clipboard;
using System;
using System.Reactive.Linq;

var subscription = ClipboardNative.OnUpdate
    .Where(info => info.Formats.Contains("Text"))
    .Throttle(TimeSpan.FromMilliseconds(500)) // Avoid rapid updates
    .Subscribe(info =>
    {
        using var clipboard = ClipboardNative.Access();
        var text = clipboard.GetAsString();
        ProcessClipboardText(text);
    });

Get Clipboard Owner

using Dapplo.Windows.Clipboard;

var subscription = ClipboardNative.OnUpdate.Subscribe(info =>
{
    Console.WriteLine($"Clipboard owner handle: {info.OwnerHandle}");
});

Error Handling

Handle Access Denied

using Dapplo.Windows.Clipboard;

try
{
    using var clipboard = ClipboardNative.Access();
    var text = clipboard.GetAsString();
}
catch (ClipboardAccessDeniedException ex)
{
    Console.WriteLine($"Clipboard access denied: {ex.Message}");
    // Another application is currently using the clipboard
}

Retry Logic

using Dapplo.Windows.Clipboard;
using System;
using System.Threading;

int maxRetries = 3;
int retryDelay = 100; // milliseconds

for (int i = 0; i < maxRetries; i++)
{
    try
    {
        using var clipboard = ClipboardNative.Access();
        clipboard.SetAsUnicodeString("Hello, World!");
        break; // Success
    }
    catch (ClipboardAccessDeniedException)
    {
        if (i == maxRetries - 1)
        {
            throw; // Give up after max retries
        }
        Thread.Sleep(retryDelay);
    }
}

Cloud Clipboard and Clipboard History

Windows 10 and later versions support cloud clipboard synchronization and clipboard history. You can control how your clipboard content interacts with these features using the cloud clipboard extensions.

What are Cloud Clipboard Options?

The cloud clipboard formats allow you to control three aspects of clipboard behavior:

  1. CanIncludeInClipboardHistory - Controls whether the content appears in Windows clipboard history (Win+V)
  2. CanUploadToCloudClipboard - Controls whether the content syncs across devices via cloud
  3. ExcludeClipboardContentFromMonitorProcessing - Controls whether clipboard monitoring apps can process the content

Setting Cloud Clipboard Options

The simplest way to control cloud clipboard behavior is using the SetCloudClipboardOptions method:

using Dapplo.Windows.Clipboard;

using (var clipboard = ClipboardNative.Access())
{
    // Set your clipboard content
    clipboard.SetAsUnicodeString("Sensitive information");

    // Prevent from being stored in history or synced to cloud
    clipboard.SetCloudClipboardOptions(
        canIncludeInHistory: false,
        canUploadToCloud: false,
        excludeFromMonitoring: true
    );
}

Use Cases

Private/Sensitive Content

Prevent sensitive data from being stored in history or synced:

using Dapplo.Windows.Clipboard;

using (var clipboard = ClipboardNative.Access())
{
    clipboard.SetAsUnicodeString("MyPassword123!");

    // Disable history and cloud sync for sensitive data
    clipboard.SetCloudClipboardOptions(
        canIncludeInHistory: false,
        canUploadToCloud: false
    );
}

Temporary Content

For temporary clipboard content that shouldn't clutter history:

using Dapplo.Windows.Clipboard;

using (var clipboard = ClipboardNative.Access())
{
    clipboard.SetAsUnicodeString("Temporary data");

    // Don't include in history, but allow cloud sync
    clipboard.SetCloudClipboardOptions(
        canIncludeInHistory: false,
        canUploadToCloud: true
    );
}

Normal Content (Default)

For normal content that should be available in history and cloud:

using Dapplo.Windows.Clipboard;

using (var clipboard = ClipboardNative.Access())
{
    clipboard.SetAsUnicodeString("Normal clipboard content");

    // Use defaults - allows history and cloud
    clipboard.SetCloudClipboardOptions();
}

Individual Option Methods

You can also set each option individually:

using Dapplo.Windows.Clipboard;

using (var clipboard = ClipboardNative.Access())
{
    clipboard.SetAsUnicodeString("My content");

    // Set options individually
    clipboard.SetCanIncludeInClipboardHistory(false);
    clipboard.SetCanUploadToCloudClipboard(true);
    clipboard.SetExcludeClipboardContentFromMonitorProcessing(true);
}

Checking Cloud Clipboard Formats

You can check if cloud clipboard formats are present:

using Dapplo.Windows.Clipboard;
using System.Linq;

using (var clipboard = ClipboardNative.Access())
{
    var formats = clipboard.AvailableFormats().ToList();

    bool hasHistoryFormat = formats.Contains(
        ClipboardCloudExtensions.CanIncludeInClipboardHistoryFormat);
    bool hasCloudFormat = formats.Contains(
        ClipboardCloudExtensions.CanUploadToCloudClipboardFormat);
    bool hasMonitorFormat = formats.Contains(
        ClipboardCloudExtensions.ExcludeClipboardContentFromMonitorProcessingFormat);
}

Best Practices for Cloud Clipboard

  1. Always set cloud options after setting content - The cloud formats should be set in the same clipboard access session as your content
  2. Default to allowing - Unless you have a specific reason, use the defaults which allow history and cloud sync
  3. Be consistent - If you disable history, you probably want to disable cloud sync too
  4. Consider user privacy - For passwords and sensitive data, always disable history and cloud sync

Common Clipboard Formats

Standard Windows clipboard formats:

  • Text - ANSI text
  • UnicodeText - Unicode text
  • Bitmap - Bitmap image
  • PNG - PNG image format
  • FileDrop - List of file paths
  • HTML Format - HTML content
  • Rich Text Format - RTF content
  • Csv - Comma-separated values

You can also check available formats:

using Dapplo.Windows.Clipboard;

using (var clipboard = ClipboardNative.Access())
{
    var formats = clipboard.AvailableFormats();
    foreach (var format in formats)
    {
        Console.WriteLine($"Format: {format}");
    }
}

Best Practices

1. Always Use using Statements

This ensures the clipboard is properly released:

using (var clipboard = ClipboardNative.Access())
{
    // Use clipboard
} // Automatically released

2. Dispose Subscriptions

Clean up event subscriptions when done:

var subscription = ClipboardNative.OnUpdate.Subscribe(...);

// Later
subscription.Dispose();

3. Handle Concurrency

The clipboard is a shared resource. Use appropriate error handling:

try
{
    using var clipboard = ClipboardNative.Access();
    // Use clipboard
}
catch (ClipboardAccessDeniedException)
{
    // Handle gracefully
}

4. Use Reactive Operators

Leverage Rx operators for better control:

var subscription = ClipboardNative.OnUpdate
    .Throttle(TimeSpan.FromMilliseconds(500))  // Debounce rapid changes
    .DistinctUntilChanged(info => info.Id)      // Skip duplicates
    .ObserveOn(SynchronizationContext.Current)  // UI thread
    .Subscribe(info => { /* Handle */ });

See Also

  • API Reference
  • Getting Started
  • Common Scenarios
  • Reactive Extensions Documentation
  • Improve this Doc
In This Article
Back to top Copyright © 2017 Dapplo