I have an application that holds an image in a picture box. When you press ctrl+C it copies the image to clipboard. I use a thread to perform the actual clipboard operations.
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!";
}
}
}
Right now, if I repeatly send ctrl+C (eg: hold the keys down), a new thread would be spawned. How can I change the code so that I re-use the clipboard thread, and have it tell me whether it’s currently copying data or not so that I don’t try to execute another copy command while it’s still working.
UPDATE
Now it works
I’ve changed it to use a background worker
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();
}
But now an exception occurs on
Clipboard.Clear();
saying
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.
First off, you should be invoking when accessing UI controls from a background thread:
Then invoke that from a BackgroundWorker. Lots of examples of that are available on the web.