使用线程将图像复制到剪贴板并管理线程

时间:2012-07-31 19:22:34

标签: c# winforms

我有一个应用程序,用于在图片框中保存图像。按ctrl + C时,它会将图像复制到剪贴板。我使用一个线程来执行实际的剪贴板操作。

protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
{
    if (keyData == (Keys.Control | Keys.C))
    {
        clipboardThread = new Thread(copy_to_clipboard);
        clipboardThread.SetApartmentState(ApartmentState.STA);
        clipboardThread.Start();
        return true;
    }
    return base.ProcessCmdKey(ref msg, keyData);
}

private void copy_to_clipboard()
{
    if (pic_display.Image != null)
    {
        using (MemoryStream stream = new MemoryStream())
        {
            clipboardStatus.Text = "Copying image to clipboard...";
            pic_display.Image.Save(stream, ImageFormat.Png);
            var data = new DataObject("PNG", stream);
            Clipboard.Clear();
            Clipboard.SetDataObject(data, true);
            clipboardStatus.Text = "Copied successfully!";
        }
    }
}

现在,如果我重复发送ctrl + C(例如:按住键),将产生一个新线程。如何更改代码以便重新使用剪贴板线程,并让它告诉我当前是否正在复制数据,这样我就不会在它仍在工作时尝试执行另一个复制命令。

更新

现在可行了

我已将其更改为使用后台工作程序

protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
{
    if (keyData == (Keys.Control | Keys.C))
    {
        if (!backgroundWorker1.IsBusy)
            backgroundWorker1.RunWorkerAsync();
        return true;
    }
    return base.ProcessCmdKey(ref msg, keyData);
}

private void copy_to_clipboard()
{
    using (var stream = new MemoryStream())
    {
        clipboardStatus.Text = "Copying image to clipboard...";
        pic_display.Invoke((Action)(() => {
            if (pic_display.Image != null)
              pic_display.Image.Save(stream, ImageFormat.Png); 
        }));
        if (stream.Position == 0) return; // No image was saved
        var data = new DataObject("PNG", stream);
        BeginInvoke ( (Action) ( ()=> {
            Clipboard.Clear();
            Clipboard.SetDataObject(data, true);
        }
        clipboardStatus.Text = "Copied successfully!";
    }
}

private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
    copy_to_clipboard();
}

但是现在

发生了异常

Clipboard.Clear();

Current thread must be set to single thread apartment (STA) mode before OLE calls can be made. Ensure that your Main function has STAThreadAttribute marked on it.

4 个答案:

答案 0 :(得分:1)

首先,您应该在从后台线程访问UI控件时调用:

private void copy_to_clipboard()
{
    using (var stream = new MemoryStream())
    {
        clipboardStatus.Text = "Copying image to clipboard...";
        pic_display.Invoke((Action)()=> { 
            if (pic_display.Image != null)
              pic_display.Image.Save(stream, ImageFormat.Png); 
        });
        if (stream.Position == 0) return; // No image was saved
        var data = new DataObject("PNG", stream);
        Clipboard.Clear();
        Clipboard.SetDataObject(data, true);
        clipboardStatus.Text = "Copied successfully!";
    }
}

然后从BackgroundWorker调用它。网上有很多例子。

答案 1 :(得分:1)

我建议在点击期间进行保存 - 无论如何你应该在UI线程上进行保存。你可以使用InvokeRequired / Invoke;但是你无法控制Save何时接管UI线程。点击后可能会花费相当多的时间并让用户感到不安。或者,在用户更改图像后,它可能是。它很糟糕,它将需要任何时间远离UI线程;但从可用性的角度来看,越接近点击越好。在这种情况下,可能会像:

protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
{
    if (keyData == (Keys.Control | Keys.C))
    {
        if (pic_display.Image != null)
        {
            MemoryStream stream = new MemoryStream())
            clipboardStatus.Text = "Copying image to clipboard...";
            pic_display.Image.Save(stream, ImageFormat.Png);

            clipboardThread = new Thread(copy_to_clipboard);
            clipboardThread.SetApartmentState(ApartmentState.STA);
            clipboardThread.Start(stream);
        }
        return true;
    }
    return base.ProcessCmdKey(ref msg, keyData);
}

private void copy_to_clipboard(object state)
{
    var stream = (Stream) state;
    try
    {
        var data = new DataObject("PNG", stream);
        Clipboard.Clear();
        Clipboard.SetDataObject(data, true);
        BeginInvoke((MethodInvoker) (() => clipboardStatus.Text = "Copied successfully!"));
    }
    finally
    {
        stream.Dispose();
    }
}

答案 2 :(得分:0)

尝试类似的东西:

object lockObj = new object();

        private void copy_to_clipboard()
        {
            lock (lockObj)
            {
                if (pictureBox1.Image != null)
                    {
                        using (MemoryStream stream = new MemoryStream())
                        {
                            clipboardStatus.Text = "Copying image to clipboard...";                            
                            pictureBox1.Image.Save(stream, ImageFormat.Png);
                            var data = new DataObject("PNG", stream);
                            Clipboard.Clear();
                            Clipboard.SetDataObject(data, true);                            
                            clipboardStatus.Text = "Copied successfully!";
                        }
                    } 
            }
        }

答案 3 :(得分:0)

您不能只将该主题存储为成员吗?除非我误解了这个问题,否则Thread.IsAlive会做你想要的。但是,如果你正在修改UI控件作为上面的海报说你应该使用Invoke来确保在UI线程上发生这种情况。

protected Thread clipboardThread;

protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
{

if (keyData == (Keys.Control | Keys.C))
{
    if (clipBoardThread == null)
    {
       clipboardThread = new Thread(copy_to_clipboard);
       clipboardThread.SetApartmentState(ApartmentState.STA);
       clipboardThread.IsBackGround = false;
    }
    if (!clipboardThread.IsAlive)
    {
       clipboardThread.Start();
    }
    return true;
}
return base.ProcessCmdKey(ref msg, keyData);
}

private void copy_to_clipboard()
{
if (pic_display.Image != null)
{
    using (MemoryStream stream = new MemoryStream())
    {
        clipboardStatus.Text = "Copying image to clipboard...";
        pic_display.Image.Save(stream, ImageFormat.Png);
        var data = new DataObject("PNG", stream);
        Clipboard.Clear();
        Clipboard.SetDataObject(data, true);
        clipboardStatus.Text = "Copied successfully!";
    }
}
}