Retrieving Images from the Clipboard Reliably in WPF Revisited
In this post, Rick Strahl reviews the long-standing problems with image data retrieval from the Windows clipboard in WPF, and demonstrates robust solutions using intermediary bitmaps and pixel format fixes. He includes code samples and performance notes for developers working with UI image handling.
Retrieving Images from the Clipboard Reliably in WPF Revisited
Author: Rick Strahl
Published: February 21, 2025
Introduction
Working with images from the Windows clipboard in WPF applications has been notoriously unreliable, especially due to the behavior of the ImageSource
control. The standard Clipboard.GetImage()
often returns incomplete or unusable data, leading to crashes or rendering failures. In this post, Rick Strahl revisits this issue, shares new findings, and offers improved code solutions for more dependable clipboard image handling.
Image Retrieval Challenges in WPF
WPF’s clipboard API seems simple, but using Clipboard.GetImage()
often causes issues:
- Crashes on reading clipboard data.
- Returns ImageSource objects that fail silently—data appears present but won’t render.
Alternatives like Windows.Forms.Clipboard.GetImage()
are more reliable but do not support transparency, which is sometimes necessary (e.g., for PNG images).
Case Study: Markdown Monster
Rick notes image corruption problems occurring in real scenarios, such as when pasting images captured via TechSmith’s SnagIt into Markdown Monster. Instead of rendering correctly, images display broken, offset, or color-distorted previews. Inspection shows the actual bitmap data is intact—the trouble arises during the Bitmap to ImageSource conversion.
Example Visual
Figure 1: Corrupted image when rendering via ImageSource.
Diagnosing The Issue
The commonly used Stack Overflow code for Bitmap-to-ImageSource conversion hardcodes PixelFormats.Bgr24
. This fails intermittently when the clipboard provides images in different bit depths (e.g., 32bpp PNGs now more widely used).
Original Conversion Code
public static BitmapSource BitmapToBitmapSource(Bitmap bmp) {
var bitmapData = bmp.LockBits(
new Rectangle(0, 0, bmp.Width, bmp.Height),
ImageLockMode.ReadOnly, bmp.PixelFormat);
var bitmapSource = BitmapSource.Create(
bitmapData.Width, bitmapData.Height,
bmp.HorizontalResolution, bmp.VerticalResolution,
PixelFormats.Bgr24, null,
bitmapData.Scan0, bitmapData.Stride * bitmapData.Height, bitmapData.Stride);
bmp.UnlockBits(bitmapData);
return bitmapSource;
}
This fails with modern 32bpp clipboard images.
Fix: Dynamic Pixel Format Conversion
Rick provides a method to map System.Drawing and System.Windows pixel formats. The core fix is to avoid hardcoding and dynamically handle different formats:
public static System.Windows.Media.PixelFormat ConvertPixelFormat(System.Drawing.Imaging.PixelFormat systemDrawingFormat) {
switch (systemDrawingFormat) {
case PixelFormat.Format32bppArgb: return PixelFormats.Bgra32;
case PixelFormat.Format32bppRgb: return PixelFormats.Bgr32;
case PixelFormat.Format24bppRgb: return PixelFormats.Bgr24;
case PixelFormat.Format16bppRgb565: return PixelFormats.Bgr565;
case PixelFormat.Format16bppArgb1555: return PixelFormats.Bgr555;
case PixelFormat.Format8bppIndexed: return PixelFormats.Gray8;
case PixelFormat.Format1bppIndexed: return PixelFormats.BlackWhite;
case PixelFormat.Format16bppGrayScale: return PixelFormats.Gray16;
default: return PixelFormats.Bgr24;
}
}
public static BitmapSource BitmapToBitmapSource(Bitmap bmp) {
var bitmapData = bmp.LockBits(
new Rectangle(0, 0, bmp.Width, bmp.Height),
ImageLockMode.ReadOnly, bmp.PixelFormat);
var pf = ConvertPixelFormat(bmp.PixelFormat);
var bitmapSource = BitmapSource.Create(
bitmapData.Width, bitmapData.Height,
bmp.HorizontalResolution, bmp.VerticalResolution,
pf, null, bitmapData.Scan0, bitmapData.Stride * bitmapData.Height, bitmapData.Stride);
bmp.UnlockBits(bitmapData);
return bitmapSource;
}
Now, regardless of source bitness (24bpp, 32bpp, etc.), the image renders correctly.
Visualization
Figure 2: Correct image rendering after pixel format fix.
Safe vs. Unsafe Code Approaches
The fastest solution uses unsafe memory access (locking bitmap bits). For environments where unsafe code isn’t allowed, Rick provides a slower, safe-memory solution using MemoryStream:
public static BitmapSource BitmapToBitmapSourceSafe(Bitmap bmp) {
using var ms = new MemoryStream();
bmp.Save(ms, ImageFormat.Png); ms.Position = 0;
var bitmap = new BitmapImage();
bitmap.BeginInit();
bitmap.CacheOption = BitmapCacheOption.OnLoad;
bitmap.StreamSource = ms;
bitmap.EndInit();
bitmap.Freeze();
return bitmap;
}
- Unsafe version: 3-4 times faster, uses in-place memory.
- Safe (stream) version: More portable but slower and duplicates memory.
Performance differences become critical with lots of images, such as asynchronous image lists. Example:
Figure 3: Performance matters for large async image lists.
Robust Clipboard Image Retrieval Helper
Combining these learnings, Rick offers a resilient ClipboardHelper
class:
Main methods
GetImage()
retrieves a Bitmap via the best available format (PNG if possible, else Bitmap).GetImageSource()
wrapsGetImage()
and returns an ImageSource for WPF binding.
Key retrieval logic example
public static ImageSource GetImageSource() {
if (!Clipboard.ContainsImage()) return null;
using (var bmp = GetImage()) {
if (bmp == null) return null;
return WindowUtilities.BitmapToBitmapSource(bmp);
}
}
public static Bitmap GetImage() {
try {
var dataObject = Clipboard.GetDataObject();
var formats = dataObject.GetFormats(true);
if (formats == null || formats.Length == 0) return null;
// Priority: PNG (supports transparency)
if (formats.Contains("PNG")) {
using MemoryStream ms = (MemoryStream)dataObject.GetData("PNG");
ms.Position = 0;
return (Bitmap)new Bitmap(ms);
}
if (formats.Contains("System.Drawing.Bitmap")) {
return (Bitmap)dataObject.GetData("System.Drawing.Bitmap");
}
if (formats.Contains(DataFormats.Bitmap)) {
return (Bitmap)dataObject.GetData(DataFormats.Bitmap);
}
// Fallback to WPF/WinForms Clipboard for non-standard formats
BitmapSource src = null;
for (int i = 0; i < 5; i++) {
try {
src = Clipboard.GetImage(); break;
} catch { Thread.Sleep(10); }
}
if (src == null) {
try {
return System.Windows.Forms.Clipboard.GetImage() as Bitmap;
} catch { return null; }
}
return WindowUtilities.BitmapSourceToBitmap(src);
} catch { return null; }
}
Steps handled
- Prioritizes PNG data (for transparency).
- Tries direct Bitmap formats next.
- Falls back to WPF/WinForms API with multiple attempts due to known clipboard timing bugs.
- Final fallback ensures something (if not perfect) is always returned.
Figure 4: Successful clipboard image with transparency preserved.
Summary
WPF Clipboard image handling is error-prone, but by prioritizing bitmap captures, translating pixel formats appropriately, and providing fallback strategies (including WinForms API use when needed), developers can ensure reliable image retrieval and rendering, even for complex PNG and 32-bit images. These helpers can be packaged into any WPF application for maximum interoperability.
See Also
- Wrestling with Clipboard Images in WPF (2020)
- Programmatic HTML to PDF Generation using WebView2
- Using SQL Server on Windows ARM
If you found this post helpful, consider supporting Rick Strahl’s work!
This post appeared first on “Rick Strahl’s Blog”. Read the entire article here