我有一个在图片框中保存图像的应用程序。当您按下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.
首先,您应该在从后台线程访问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调用它。网上有很多这样的例子。
我建议在单击时执行Save操作——无论如何都应该在UI线程上执行。您可以使用InvokeRequired/Invoke;但是您无法控制Save何时接管UI线程。点击后可能会有相当长的时间,这会让用户感到不安。或者,它可能是之后用户更改图像。它会占用用户界面线程的任何时间,这太糟糕了;但从可用性的角度来看,离Click越近越好。在这种情况下,可能类似于:
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();
}
}
试试这样的东西:
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!";
}
}
}
}
难道不能将线程存储为成员吗?除非我误解了这个问题,否则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!";
}
}
}