PictureBox上的InvokeRequired为true。怎么处理这个?

时间:2010-09-13 06:00:18

标签: c# .net winforms picturebox invokerequired

我的PictureBox调用上有另外一个question给我3种错误,特别是来自Conrad Frix的一些很好的答案。所以它让我弄清楚我的问题在哪里,但现在要修复它我不是百分百肯定的。

基本上我有一个Windows窗体计时器,它检查某个事件是否为真,如果是,则告诉系统在所述事件(一个值)超过某个阈值后2秒发送一些数据。

我认为我所有的计时器都是用我的PictureBox创建一个讨厌的竞争条件,我在几个地方使用它来获取图像:

new Bitmap(myPicBox.Image); 
等等......

我在某个地方看到计时器的间隔应该至少为50.从33开始。我发现我可以做一个picCapture.InvokeRequired,看看它是否会基本消亡。我知道我需要使用一个委托,但只使用那些来设置一些东西......不是为了得到一个图像....不知道如何设置它......我知道究竟是什么导致它,这是这个代码组合:

private void timer1_Tick(object sender, EventArgs e)
    {
          if(someCOnditionTrue)
          {

                    TimerCallback tc = new TimerCallback(sendDataFast); //only 
                       //doing all this so i can have the method run two seconds after     
                       // the condition is detected to be true.
                    System.Threading.Timer t = new System.Threading.Timer(tc, null, 2000, Timeout.Infinite);
          }
   }



    void sendDataFast(Object stateObject)
    {

        //using this so the execution is not haulted while the sending of data takes place.
        EmergencyDelegate delEmergency =
                     new EmergencyDelegate(mic.sendEmergencyData);

        Image imgclone;

        if (picCapture.InvokeRequired)
        {                 
            Console.WriteLine("HFS Batman! its going to die ");
        }
        lock (lockObject2) //i admit no clue what im doing here and doesn't seem to help.
        {
            Image img = picCapture.Image;
            imgclone = (Image)img.Clone();
        }
        delEmergency.BeginInvoke(imgclone, null, null); //deep in the call to
        //sendEmergencyData i get the **ParameterNotValid** almost everytime.

        imgclone.Dispose(); //to free memory?


    }

根据我之前的问题,在timer1_tick事件中似乎不再出现内存问题或其他错误...(内存不足错误是一个)。

我认为最大的问题是当我需要图像数据时如何处理picCapture.InvokeRequired?我确定它在timer1_click中的线程计时器调用我这样做导致了这个....

3 个答案:

答案 0 :(得分:1)

顾名思义,InvokeRequired表示您在访问控件时需要致电Invoke(或BeginInvoke)。

请注意,这是Control.Invoke / Control.BeginInvoke代理中存在的Invoke/BeginInvoke ...虽然您需要一个代理人调用 Invoke / BeginInvoke,只是为混合添加更多混淆。

有关详细信息,请参阅Windows Forms section of my threading tutorial。整个教程可以做更新,但我相信这一点是可以的。在其他情况下,您可能还需要考虑使用BackgroundWorker,但我不认为在这种特殊情况下这可能与您相关。

答案 1 :(得分:1)

我认为您对InvokeRequired的理解有误。 InvokeRequired表示当前线程与UI线程不同,现在访问控制状态是不安全的。如果是这种情况,则必须使用Control.Invoke封送对UI线程的调用,然后访问控制状态。请阅读here on MSDN了解详情。

在您的情况下,除非PictureBox图像正在发生变化,否则我建议您先预先复制图像并使用它。否则,您需要使用Control.Invoke

答案 2 :(得分:1)

你有太多的线程要把它带到一个好的结局。 Timer和委托的BeginInvoke()方法都将使用线程池线程。问题是PictureBox.Image属性只是部分线程安全的。它一次只能由一个线程访问。当您的代码调用Clone()方法的同时,UI线程绘制图像时,您的代码将会出现异常。

您的锁定语句无法解决问题,PictureBox正在访问Image属性而不使用相同的锁定。我强烈建议首先删除线程,使用System.Windows.Forms.Timer而不是System.Threading.Timer。在UI线程上引发了Tick事件。然而,这将使UI线程在事件运行时无响应,这取决于用户是否注意到它需要多长时间。超过100毫秒就会成为一个问题。

唯一的另一种方法是尝试使PictureBox控件具有线程安全性。这在一定程度上是可能的。在项目中添加一个新类并粘贴下面显示的代码。编译。将新控件从工具箱顶部拖放到表单上,替换现有PB。请注意,这只是一个部分解决方案,显示动画GIF或使用ImageLocation属性仍然会炸弹。使用提供的Clone方法,而不是在Image属性上调用Clone。

using System;
using System.Drawing;
using System.Windows.Forms;

class MyPictureBox : PictureBox {
    private object locker = new object();
    public new Image Image {
        get { return base.Image; }
        set { lock (locker) { base.Image = value; } }
    }
    public Image Clone() {
        lock (locker) {
            return (this.Image != null) ? (Image)this.Image.Clone() : null;
        }
    }
    protected override void OnPaint(PaintEventArgs pe) {
        lock (locker) {
            base.OnPaint(pe);
        }
    }
}
相关问题