从WPF DataGrid复制粘贴数据时,OpenClipboard失败

时间:2012-10-07 13:29:51

标签: c# wpf datagrid clipboard copy-paste

我有一个使用datagrid的WPF应用程序。该应用程序运行正常,直到我安装Visual Studio 2012和Blend + SketchFlow预览。现在,当我尝试使用 Ctrl + C (在任何应用程序中)将数据从网格复制到剪贴板时,我遇到以下异常:< / p>

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()

这真烦人。

我已经看到了一些对此问题的引用here以及网络上的各个位置,没有真正的解决方案。

我可以验证在Visual Studio中引发此异常时剪贴板是否已锁定,因为我无法复制粘贴消息(必须将其写入文件)。此外,在复制过程开始之前,剪贴板未锁定。

如何解决这个问题?

12 个答案:

答案 0 :(得分:81)

我们正在使用.NET 4.0。我们遇到了同样的问题,但在注销系统之后,代码过去常常工作了一段时间。

最后我们找到了替代方案。

如果要将字符串复制到剪贴板,

string data = "Copy This"

直到现在我使用以下方法

Clipboard.SetText(data);

一次又一次失败。然后我查看了其他可用于在 Clipboard Class 中设置剪贴板文本的方法,并尝试了以下方法:

Clipboard.SetDataObject(data);

它工作:)。我再也没遇到过这个问题。

答案 1 :(得分:68)

这是WPF剪贴板处理程序中的一个错误。您需要在Application.DispatcherUnhandledException事件中处理未处理的异常。

将此属性添加到App.xaml中的Application元素

DispatcherUnhandledException="Application_DispatcherUnhandledException"

将此代码添加到App.xaml.cs文件

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;
}

答案 2 :(得分:6)

我也在应用程序中遇到问题,我将信息复制到剪贴板中,因为用户仔细阅读ListBox。复制的信息与所选项目相关,并允许他们将其(所述信息)粘贴到其他应用程序中以方便使用。偶尔我会在某些用户的系统上获得CLIPBRD_E_CANT_OPEN,但在其他用户的系统上却没有。

虽然我仍然无法修复争用,但我能够创建一些代码来查找导致争用的应用程序。我想至少分享这段代码,希望能帮到别人。我将添加 using 语句,属性和我创建的方法来查找罪魁祸首的 Process 对象。从 Process 项中,您可以获取进程的名称,PID,主窗口标题(如果有),以及其他可能有用的数据。这是我添加的代码行,没有调用它的代码。 (注意:在代码段下方,我还有一个要分享的内容):

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;
    }

其他注意:我改变了另一件事,简化了我的代码,使用 System.Windows.Clipboard 转换为 System.Windows.Forms .Clipboard (参见System.Windows.Forms.Clipboard Class)因为后者有一个4参数的 SetDataObject()方法,其中包括重试计数和以毫秒为单位的重试延迟。这至少从我的代码中删除了一些重试 noise

你的里程可能会有所不同......加上可能会有副作用,我还没有偶然发现,所以如果有人知道它们,请发表评论。无论如何,我希望这证明对某人有用。

答案 3 :(得分:5)

我在WPF 4.0和4.5中也遇到过这个问题,因为我安装了 TeraCopy (Windows 7,64位)。每个Clipboard.SetText()都失败了,发生了System.Runtime.InteropServices.COMException。

我的第一个解决方案是卸载TeraCopy - 它有效,但我喜欢这个应用程序,所以我不得不寻找另一个解决方案来解决这个问题。解决方案是替换

Clipboard.SetText("my string");

Clipboard.SetDataObject("my string");

答案 4 :(得分:2)

我遇到了与RichTextBox相同的问题。以下代码随机崩溃:

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

似乎首选使用 System.Windows.Controls.RichTextBox.Copy

答案 5 :(得分:2)

我在使用.NET 4.6.1从剪贴板中检索XAML数据时遇到了问题。

错误讯息:

  

OpenClipboard失败(HRESULT异常:0x800401D0(CLIPBRD_E_CANT_OPEN)))

我解决了它如下:

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 { }
}

答案 6 :(得分:1)

我在将Excel单元格复制到剪贴板并从剪贴板中将数据作为HTML字符串时遇到了同样的问题。

您可以使用(while-try-catch),如下面的代码所示。

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;
    }
}

此外,如果循环次数超过10次或更多,您可以在while中设置一个计数器,异常发生。我测试它的最大计数器是一次和一次循环剪贴板工作。

答案 7 :(得分:1)

我终于找到了使用DataGrid实现的默认复制模式的解决方案。

以前的答案对我不起作用:

  • 使用Clipboard.SetDataObject(data);委托Clipboard.SetText(data)->此解决方案不是我期望的,我不想自己实现复制功能。
  • 处理DispatcherUnhandledException:我不知道为什么,但是对我不起作用。未调用与此事件相关的方法。

我终于找到了解决这个问题的新方法。您只需要清除剪贴板,然后再按“ Ctrl + C”即可。

因此,我在MainWindows.xaml文件资源中创建了新样式:

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

此样式用于处理我的应用程序的所有数据网格中的“ previewKeyDown”。 调用的方法如下:

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

之后,问题解决了。

答案 8 :(得分:0)

为此目的CopyingRowClipboardContentobject senderDataGridRowClipboardEventArgs e)提供了一个DataGrid事件/方法签名,并且比Clipboard.SetDataObject(data)或{{1更可靠}}

以下是如何使用它。

设置&#34; FullRow&#34;在SelectionUnit模式下,dataGrid名为myDataGrid

Clipboard.SetText(data)

我们有一个方法<DataGrid x:Name="myDataGrid" SelectionUnit="FullRow"></DataGrid> ,可以在dataGrid中调用每个行,将其内容复制到剪贴板。例如,对于具有七行的数据网格,这被称为七次。

myDataGrid_CopyingRowClipboardContent

答案 9 :(得分:0)

代码app.xaml

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

代码文件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();
            }
        }
    }
}

我已经处理了这段代码。

答案 10 :(得分:0)

我为WPF Datagrid导出到Excel(CSV)写了一个扩展方法:

如果“ MyDatagrid”是您的数据网格的名称,请使用一行代码来调用自己的用户控件。

MyDatagrid.ExportToExcel(this);

并将此方法添加到扩展静态类中

#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

最后不要忘记

using Microsoft.Win32;

关于扩展类 并设置

ClipboardCopyMode="IncludeHeader"

针对您的数据网格。

答案 11 :(得分:-1)

这对我也有用。

从以下位置更改班级:

Clipboard.SetText("my string");

收件人:

Clipboard.SetDataObject("my string");