OpenClipboard failed when copy pasting data from WPF DataGrid

81

I've got a WPF application using datagrid. The application worked fine until I installed Visual Studio 2012 and Blend+SketchFlow preview. Now, when I'm trying to copy the data from the grid into the clipboard with Ctrl + C (in any application), I'm getting the following exception:

System.Runtime.InteropServices.COMException (0x800401D0): OpenClipboard Failed (Exception from HRESULT: 0x800401D0 (CLIPBRD_E_CANT_OPEN))
   at System.Runtime.InteropServices.Marshal.ThrowExceptionForHRInternal(Int32 errorCode, IntPtr errorInfo)
   at System.Runtime.InteropServices.Marshal.ThrowExceptionForHR(Int32 errorCode, IntPtr errorInfo)
   at System.Windows.Clipboard.Flush()
   at System.Windows.Clipboard.CriticalSetDataObject(Object data, Boolean copy)
   at System.Windows.Controls.DataGrid.OnExecutedCopy(ExecutedRoutedEventArgs args)
   at System.Windows.Controls.DataGrid.OnExecutedCopy(Object target, ExecutedRoutedEventArgs args)
   at System.Windows.Input.CommandBinding.OnExecuted(Object sender, ExecutedRoutedEventArgs e)
   at System.Windows.Input.CommandManager.ExecuteCommandBinding(Object sender, ExecutedRoutedEventArgs e, CommandBinding commandBinding)
   at System.Windows.Input.CommandManager.FindCommandBinding(CommandBindingCollection commandBindings, Object sender, RoutedEventArgs e, ICommand command, Boolean execute)
   at System.Windows.Input.CommandManager.FindCommandBinding(Object sender, RoutedEventArgs e, ICommand command, Boolean execute)
   at System.Windows.Input.CommandManager.OnExecuted(Object sender, ExecutedRoutedEventArgs e)
   at System.Windows.UIElement.OnExecutedThunk(Object sender, ExecutedRoutedEventArgs e)
   at System.Windows.Input.ExecutedRoutedEventArgs.InvokeEventHandler(Delegate genericHandler, Object target)
   at System.Windows.RoutedEventArgs.InvokeHandler(Delegate handler, Object target)
   at System.Windows.RoutedEventHandlerInfo.InvokeHandler(Object target, RoutedEventArgs routedEventArgs)
   at System.Windows.EventRoute.InvokeHandlersImpl(Object source, RoutedEventArgs args, Boolean reRaised)
   at System.Windows.UIElement.RaiseEventImpl(DependencyObject sender, RoutedEventArgs args)
   at System.Windows.UIElement.RaiseTrustedEvent(RoutedEventArgs args)
   at System.Windows.UIElement.RaiseEvent(RoutedEventArgs args, Boolean trusted)
   at System.Windows.Input.RoutedCommand.ExecuteImpl(Object parameter, IInputElement target, Boolean userInitiated)
   at System.Windows.Input.RoutedCommand.ExecuteCore(Object parameter, IInputElement target, Boolean userInitiated)
   at System.Windows.Input.CommandManager.TranslateInput(IInputElement targetElement, InputEventArgs inputEventArgs)
   at System.Windows.UIElement.OnKeyDownThunk(Object sender, KeyEventArgs e)
   at System.Windows.Input.KeyEventArgs.InvokeEventHandler(Delegate genericHandler, Object genericTarget)
   at System.Windows.RoutedEventArgs.InvokeHandler(Delegate handler, Object target)
   at System.Windows.RoutedEventHandlerInfo.InvokeHandler(Object target, RoutedEventArgs routedEventArgs)
   at System.Windows.EventRoute.InvokeHandlersImpl(Object source, RoutedEventArgs args, Boolean reRaised)
   at System.Windows.UIElement.RaiseEventImpl(DependencyObject sender, RoutedEventArgs args)
   at System.Windows.UIElement.RaiseTrustedEvent(RoutedEventArgs args)
   at System.Windows.UIElement.RaiseEvent(RoutedEventArgs args, Boolean trusted)
   at System.Windows.Input.InputManager.ProcessStagingArea()
   at System.Windows.Input.InputManager.ProcessInput(InputEventArgs input)
   at System.Windows.Input.InputProviderSite.ReportInput(InputReport inputReport)
   at System.Windows.Interop.HwndKeyboardInputProvider.ReportInput(IntPtr hwnd, InputMode mode, Int32 timestamp, RawKeyboardActions actions, Int32 scanCode, Boolean isExtendedKey, Boolean isSystemKey, Int32 virtualKey)
   at System.Windows.Interop.HwndKeyboardInputProvider.ProcessKeyAction(MSG& msg, Boolean& handled)
   at System.Windows.Interop.HwndSource.CriticalTranslateAccelerator(MSG& msg, ModifierKeys modifiers)
   at System.Windows.Interop.HwndSource.OnPreprocessMessage(Object param)
   at System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Int32 numArgs)
   at MS.Internal.Threading.ExceptionFilterHelper.TryCatchWhen(Object source, Delegate method, Object args, Int32 numArgs, Delegate catchHandler)
   at System.Windows.Threading.Dispatcher.LegacyInvokeImpl(DispatcherPriority priority, TimeSpan timeout, Delegate method, Object args, Int32 numArgs)
   at System.Windows.Threading.Dispatcher.Invoke(DispatcherPriority priority, Delegate method, Object arg)
   at System.Windows.Interop.HwndSource.OnPreprocessMessageThunk(MSG& msg, Boolean& handled)
   at System.Windows.Interop.HwndSource.WeakEventPreprocessMessage.OnPreprocessMessage(MSG& msg, Boolean& handled)
   at System.Windows.Interop.ComponentDispatcherThread.RaiseThreadMessage(MSG& msg)
   at System.Windows.Threading.Dispatcher.PushFrameImpl(DispatcherFrame frame)
   at System.Windows.Threading.Dispatcher.PushFrame(DispatcherFrame frame)
   at System.Windows.Threading.Dispatcher.Run()
   at System.Windows.Application.RunDispatcher(Object ignore)
   at System.Windows.Application.RunInternal(Window window)
   at System.Windows.Application.Run(Window window)
   at System.Windows.Application.Run()

This is really annoying.

I've seen some references to this problem here and on various locations on the web, with no real solution.

I can verify that the clipboard is locked when this exception is raised in Visual Studio, as I couldn't copy paste the message (had to write it to a file). Also, the clipboard wasn't locked before the copy process started.

How to solve this problem?

c#
wpf
datagrid
clipboard
copy-paste
asked on Stack Overflow Oct 7, 2012 by Arsen Zahray • edited Jan 17, 2018 by akjoshi

11 Answers

104

We are using .NET 4.0. We had the same problem, but after logging off the system, code used to work fine for some time.

Finally we found the alternative.

If you want to copy a string to the clipboard,

string data = "Copy This"

Till now I was using the following method

Clipboard.SetText(data);

It was failing again and again. Then I looked at other methods available to set text in the clipboard in Clipboard Class and tried the following:

Clipboard.SetDataObject(data);

And it worked :). I never had the issue again.

answered on Stack Overflow Jul 16, 2013 by kushdilip • edited Oct 15, 2017 by Peter Mortensen
81

It is a bug in the WPF Clipboard handler. You need to handle the unhandled exception in the Application.DispatcherUnhandledException event.

Add this attribute to the Application element in your App.xaml

DispatcherUnhandledException="Application_DispatcherUnhandledException"

Add this code to your App.xaml.cs file

void Application_DispatcherUnhandledException(object sender, DispatcherUnhandledExceptionEventArgs e)
{
    var comException = e.Exception as System.Runtime.InteropServices.COMException;

    if (comException != null && comException.ErrorCode == -2147221040)
         e.Handled = true;
}
answered on Stack Overflow Nov 23, 2012 by Alex Wiese • edited May 27, 2013 by Alex Wiese
7

I, too, have been having a problem in an application where I copy information into the clipboard as users peruse a ListBox. The information that's copied is related to the selected item, and it permits them to paste it (said info) into other applications for convenience. Occasionally I get the CLIPBRD_E_CANT_OPEN on some user's systems, but not on others.

While I still haven't been able to fix the contention, I was able to create some code to find the application causing the contention. I'd like to at least share this code in the hope it helps someone. I will add the using statement, attributes, and method I created to find the Process object of the culprit. From the Process item you can obtain the process' name, PID, main window title (if it has one), and other potentially useful data. Here's the lines of code I added without the code that calls it. (NOTE: Below the code snippet I have one more tidbit to share):

using System.Diagnostics;               // For Process class
using System.Runtime.InteropServices;   // For DllImport's

...

[System.Runtime.InteropServices.DllImport("user32.dll")]
static extern IntPtr GetOpenClipboardWindow();

[DllImport("user32.dll", SetLastError = true)]
static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);

...

    ///-----------------------------------------------------------------------------
    /// <summary>
    /// Gets the Process that's holding the clipboard
    /// </summary>
    /// <returns>A Process object holding the clipboard, or null</returns>
    ///-----------------------------------------------------------------------------
    public Process ProcessHoldingClipboard()
    {
        Process theProc = null;

        IntPtr hwnd = GetOpenClipboardWindow();

        if (hwnd != IntPtr.Zero)
        {
            uint processId;
            uint threadId = GetWindowThreadProcessId(hwnd, out processId);

            Process[] procs = Process.GetProcesses();
            foreach (Process proc in procs)
            {
                IntPtr handle = proc.MainWindowHandle;

                if (handle == hwnd)
                {
                    theProc = proc;
                }
                else if (processId == proc.Id)
                {
                    theProc = proc;
                }
            }
        }

        return theProc;
    }

OTHER NOTE: One other thing I changed which simplified my code a bit was to convert from using System.Windows.Clipboard to System.Windows.Forms.Clipboard (see System.Windows.Forms.Clipboard Class)because the latter has a 4-parameter SetDataObject() method which includes a retry count and a retry delay in milliseconds. This at least removed some of the retry noise from my code.

Your mileage may vary...plus there may be side effects in this which I've not yet stumbled upon, so if anyone knows of them please comment. In any event, I hope this proves useful to someone.

answered on Stack Overflow Jan 23, 2014 by John
7

I also had this issue in WPF 4.0 and 4.5 since I installed TeraCopy (Windows 7, 64-bit). Every Clipboard.SetText() failed with a System.Runtime.InteropServices.COMException.

My first solution was to uninstall TeraCopy - it worked, but I love this application, so I had to search for another solution to resolve that issue. The solution was to replace

Clipboard.SetText("my string");

with

Clipboard.SetDataObject("my string");
answered on Stack Overflow Nov 12, 2014 by pr0gg3r • edited Oct 15, 2017 by Peter Mortensen
2

I had the same problem with RichTextBox. The following code crashed randomly:

TextRange tr = new TextRange(rich.Document.ContentStart, rich.Document.ContentEnd);
System.Windows.Clipboard.SetDataObject(tr.Text);

It seems it's preferred to use System.Windows.Controls.RichTextBox.Copy

answered on Stack Overflow Jul 5, 2014 by AVEbrahimi • edited Oct 15, 2017 by Peter Mortensen
2

I had a problem to retrieve XAML data from the clipboard with .NET 4.6.1.

Error message:

OpenClipboard Failed (Exception from HRESULT: 0x800401D0 (CLIPBRD_E_CANT_OPEN)))

I solved it as follows:

int counter = 0;
object xamlClipData = null;

while (xamlClipData == null)
{
    try
    {
        if (counter > 10)
        {
            System.Windows.MessageBox.Show("No access to clipboard xaml data.");
            break;
        }

        counter++;

        if (System.Windows.Clipboard.GetDataObject().GetDataPresent(DataFormats.Xaml))
        {
            xamlClipData = System.Windows.Clipboard.GetData(DataFormats.Xaml);
        }
    }
    catch { }
}
answered on Stack Overflow May 4, 2016 by Pollitzer • edited Oct 15, 2017 by Peter Mortensen
1

I had the same problem in copying Excel cells to the clipboard and getting data from the clipboard as an HTML string.

You can use (while-try-catch) like in the below code.

Excel.Application exap = new Microsoft.Office.Interop.Excel.Application();
Excel.Workbook wb = exap.Workbooks.Open(
                      sourceFileNameTextBox.Text,
                      Type.Missing, Type.Missing, Type.Missing, Type.Missing,
                      Type.Missing, Type.Missing, Type.Missing, Type.Missing,
                      Type.Missing, Type.Missing, Type.Missing, Type.Missing,
                      Type.Missing, Type.Missing);
Excel.Sheets sh = wb.Worksheets;

bool clip = false;

// Copy Excel cells to clipboard
while (!clip)
{
    try
    {
        ws.Cells.get_Range(cells[0], cells[1]).Copy(Type.Missing);
        clip = true;
    }
    catch
    {
        clip = false;
    }
}

string b = "";

// Get Excel cells data from the clipboard as HTML

clip = false;
while(!clip)
{
    try
    {
        b = Clipboard.GetData(DataFormats.Html) as string;
        clip = true;
    }
    catch
    {
        clip = false;
    }
}

Also, you can have a counter in the while if the loop is more than 10 times or more, exception occur. I test its maximum counter is one and in one time loop clipboard work.

answered on Stack Overflow Nov 12, 2014 by Majid gharaei • edited Oct 15, 2017 by Peter Mortensen
1

I have finaly found a solution to use the default copy mode implemented by DataGrid.

The previous answers didn't work for me:

  • Using Clipboard.SetDataObject(data); insteed of Clipboard.SetText(data) --> This solution was not what i expected, i didn't want to implement myself the copy feature.
  • Handling DispatcherUnhandledException : i do not know why but it didn't work for me. The method attached to this event was not called.

I finally found a new way to handle this problem. You just need to clear the clipboard before you press "Ctrl + C".

So, i made a new style in the MainWindows.xaml file resources:

<Window.Resources>
    <Style TargetType="DataGrid">
        <EventSetter Event="PreviewKeyDown" Handler="DataGrid_PreviewKeyDown"/>
    </Style>
</Window.Resources>

This style is made to handle the "previewKeyDown" in all datagrids of my application. The method called is the following:

private void DataGrid_PreviewKeyDown(object sender, KeyEventArgs e)
{
    if (e.Key == Key.C && (Keyboard.Modifiers & ModifierKeys.Control) == ModifierKeys.Control)
    {
        System.Windows.Forms.Clipboard.Clear();
    }
}

After that, the problem was solved.

answered on Stack Overflow Aug 1, 2019 by zlink17
0

There's a DataGrid event/method signature for this exact purpose CopyingRowClipboardContent(object sender, DataGridRowClipboardEventArgs e) and is more reliable than Clipboard.SetDataObject(data) or Clipboard.SetText(data).

Here's how to use it.

Set "FullRow" at the SelectionUnit mode for dataGrid called myDataGrid

<DataGrid x:Name="myDataGrid" SelectionUnit="FullRow"></DataGrid>

We have a method, myDataGrid_CopyingRowClipboardContent, that gets called for each row in the dataGrid to copy its contents to the clipboard. For example,for a datagrid with seven rows this is called seven times.

public int clipboardcalledcnt { get; set; } // CopyingRowClipboardContent invoked count
private void myDataGrid_CopyingRowClipboardContent(object sender, DataGridRowClipboardEventArgs e)
{
    PathInfo cellpath = new PathInfo(); // A custom class to hold path information
    string path = string.Empty;

    DataGrid dgdataPaths = (DataGrid)sender;
    int rowcnt = dgdataPaths.SelectedItems.Count;

    cellpath = (PathInfo)e.Item;

    path = "Row #" + clipboardcalledcnt + " Len=" + cellpath.Length.ToString() + ", path=" + cellpath.Path;

    e.ClipboardRowContent.Clear();

    if (clipboardcalledcnt == 0) // Add header to clipboard paste
        e.ClipboardRowContent.Add(new DataGridClipboardCellContent("", null, "--- Clipboard Paste ---\t\t\n")); // \t cell divider, repeat (number of cells - 1)

    clipboardcalledcnt++;
    e.ClipboardRowContent.Add(new DataGridClipboardCellContent(path, null, path));

    if (clipboardcalledcnt == rowcnt)
        clipboardcalledcnt = 0;
}
answered on Stack Overflow Sep 16, 2017 by Markus • edited Apr 9, 2019 by akjoshi
0

Code app.xaml

<Application.Resources>
        <Style TargetType="DataGrid">
            <EventSetter Event="PreviewKeyDown" Handler="DataGrid_PreviewKeyDown"/>
        </Style>
    </Application.Resources>

code file app.xaml.cs

using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Input;

namespace WpfApp1
{
    /// <summary>
    /// Interaction logic for App.xaml
    /// </summary>
    public partial class App : Application
    {
        private void DataGrid_PreviewKeyDown(object sender, KeyEventArgs e)
        {
            if (e.Key == Key.C && (Keyboard.Modifiers & ModifierKeys.Control) == ModifierKeys.Control)
            {
                System.Windows.Forms.Clipboard.Clear();
            }
        }
    }
}

I have dealt with this code.

answered on Stack Overflow Sep 17, 2019 by user3115047
0

I write a extension method for WPF Datagrid Export to Excel(CSV):

if "MyDatagrid" is the name of your datagrid, use one line code to call on own user control.

MyDatagrid.ExportToExcel(this);

and add this method to your extension static class

#region DataGrid Extentions

public static void ExportToExcel(this DataGrid dg, UserControl owner, string filename = "")
{
    try
    {
        dg.SelectionMode = DataGridSelectionMode.Extended;
        dg.SelectAllCells();

        Clipboard.Clear();
        ApplicationCommands.Copy.Execute(null, dg);

        var saveFileDialog = new SaveFileDialog
        {
            FileName = filename != "" ? filename : "gpmfca-exportedDocument",
            DefaultExt = ".csv", 
            Filter = "Common Seprated Documents (.csv)|*.csv"
        };

        if (saveFileDialog.ShowDialog() == true)
        {
            var clip2 = Clipboard.GetText();
            File.WriteAllText(saveFileDialog.FileName, clip2.Replace('\t', ','), Encoding.UTF8);
            Process.Start(saveFileDialog.FileName);
        }    
   
        dg.UnselectAllCells();
        dg.SelectionMode = DataGridSelectionMode.Single;
    }
    catch (Exception ex)
    {
        owner.ShowMessageBox(ex.Message);
        Clipboard.Clear();
    }
}
#endregion

finally don't forget to

using Microsoft.Win32;

on extension class and set

ClipboardCopyMode="IncludeHeader"

for your datagrid.

answered on Stack Overflow Nov 29, 2020 by Jalal Maheri • edited Nov 30, 2020 by Jackdaw

User contributions licensed under CC BY-SA 3.0