C# 使用线程将图像复制到剪贴板并管理线程
我有一个将图像保存在图片框中的应用程序。按ctrl+C时,它会将图像复制到剪贴板。我使用一个线程来执行实际的剪贴板操作C# 使用线程将图像复制到剪贴板并管理线程,c#,winforms,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); clip
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.
试试这样:
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!";
}
}
}
}
首先,您应该在从后台线程访问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!";
}
}
然后从后台工作人员那里调用它。网络上有很多这样的例子。你不能将线程作为成员存储吗?除非我误解了这个问题,否则Thread.IsAlive会做你想做的事。但是,如果您正在修改UI控件,正如上面的海报所说的,您应该真正使用Invoke来确保这在UI线程上发生
我建议在单击过程中执行保存操作——无论如何,您都应该在UI线程上执行保存操作。您可以使用invokererequired/Invoke;但是,您无法控制保存何时接管UI线程。这可能是一个明显的时间后,点击量和令人不安的用户。或者,可能是在用户更改图像之后。很糟糕的是,它需要离开UI线程的任何时间;但是从可用性的角度来看,点击越近越好。在这种情况下,可能类似于:
我想象你可以把它构造成一个无限循环,反复尝试锁定一个可数信号量来阻止它自己。然后,您可以减少控制线程的信号量,使其运行一次循环,然后再次阻塞。这可能需要多长时间?除非图像是巨大的,否则它几乎肯定不是应该放在背景线上的东西。此外,如果
picu display
是PictureBox,那么您已经违反了规则。您不应该从后台线程与UI控件交互。@ChrisShain因为我正在将其转换为png,所以可能需要一秒钟的时间。我应该使用backgroundworker吗?是的,在调用Image.Save时应该使用invoke。我将添加作为答案。我切换到线程
,因为我收到一条消息,说线程单元状态必须是STA。尽管在这段代码中,VS(2008)说它不能将lambda表达式转换为委托,因为它不是委托类型。是的,你不能使用BackgroundWorker,因为它使用线程池线程,你没有任何权限将其更改为STA。有关lambda问题,请参阅我的编辑。STA问题无论如何都是一个危险的话题。您不需要它是STA,因为您不应该从后台线程触摸UI。你应该调用UI线程,这是STA。奇怪的是,它看起来应该可以工作,但现在我得到了一个语法错误。我尝试将整个lambda函数包装在Action(…)
中,但是调用方法说它无法将void转换为delegateoh,nvm它是(Action)(()=>{…})代码>
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!";
}
}
}
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();
}
}