【问题标题】:Refreshing PictureBox causes ArgumentException Parameter is not valid刷新 PictureBox 导致 ArgumentException 参数无效
【发布时间】:2015-06-25 15:58:47
【问题描述】:

我这辈子都想不通。我正在修改 PictureBox 指向的位图。基本上有人将图像拖过图片框,并且图像会更新,因此看起来像是拖过图片框。刷新图片框时会发生什么,我在 Application.Run 行上得到一个 ArgumentException Parameter not valid 错误,所以我花了一段时间才弄清楚究竟是什么原因造成的,因为异常中没有更多信息。

我创建了一个派生自图片框的类并覆盖了 onpaint 方法。它还有一个方法 RefreshImage 来调用自己并刷新自己

如果我不刷新图片框,我不会收到此错误,但图片框图像永远不会更新

我有一个更新逻辑的主循环在后面运行。逻辑是当鼠标被拖动时,图像偏移量被更新。

循环完成逻辑后,它会尝试刷新图片框。这是我发现当我注释掉刷新行时,我没有问题,但图片框永远不会更新。我已向名为 UpdateImage 的自定义图片框类添加了一个方法,该方法调用自身然后刷新。

重写的 onpaint 方法从自定义循环线程中获取逻辑,并根据后端循环线程更新的偏移量更新位图

主窗体是在主线程上创建的,而图片框也是在主线程上创建的,我已经检查过了

调用正常工作,刷新图片框时后端线程调用主线程

注释掉刷新行会使错误消失,但看不到图片框更新

那么对于更好的方法或错误的解决方案有什么建议吗?

这是派生的图片框类的一部分

    public class PictureBoxEX : PictureBox
    {
        public int m_xoffset = 0;
        private Bitmap m_picture;
        public PictureBoxEX()
        {
            m_picture = new Bitmap(690, 600);
            this.Image = m_picture;
        }

        protected override void OnPaint(PaintEventArgs pe)
        {
            base.OnPaint(pe);
            DrawBitmaps(); // updates the m_picture with another bitmap only in this class
            Graphics g = pe.Graphics;
            g.DrawImage(this.m_picture, new Point(0, 0));
        }
        public void RefreshImage()
        {
            if(this.InvokeRequired)
            {
                MethodInvoker d = new MethodInvoker(RefreshImage);
                this.Invoke(d);
            }
            else
            {
                this.Refresh();
            }
        }
    }

这是主循环的想法

    private MainForm m_MainWindow; // derrives from Form

    private void MainThread()
    {
        int currTime = System.Environment.TickCount;
        int lastTime = currTime;
        while (Running)
        {
            currTime = System.Environment.TickCount;
            m_MainWindow.Update(currTime, lastTime);
            lastTime = currTime;

            Thread.Sleep(10);
        }
    }

这是更新方法在主窗口中的样子

PictureBoxEx m_LeftPictureBox;

public void Update(int currTime, int lastTime)
    {
        // do logic (all the logic is only working with primitive types, nothing to do with actual bitmaps
        this.m_LeftPictureBox.RefreshImage(); // if i comment this out, i will not get the exception, but it will not update the picturebox
    }
}

还有一件事,为什么主窗体刷新时图片框不刷新?或者可能是主要形式不令人耳目一新?我确定如果是这种情况,我会看到光标没有被清除。我在自定义图片框类的onpaint方法中放了一个断点,它只在第一次创建时调用一次,之后就没有了,所以我必须手动刷新它。

编辑: 不确定我可以提供哪些更多代码来帮助解决这个问题,但是如果我从后台线程调用这个函数(这个函数在我制作的表单类中),我几乎立即得到相同的异常。异常发生在 this.Refresh() 和主线程上(所以在它被调用之后)。即使它在一个 try-catch 块中,异常仍然说它未被捕获并关闭我的应用程序

    public void InvokeRefresh()
    {
        if (this.InvokeRequired)
        {
            RefreshDelegate d = new RefreshDelegate(InvokeRefresh);
            this.Invoke(d, null);
        }
        else
        {
            try
            {
                this.Refresh();
            }
            catch { }
        }
    }

【问题讨论】:

  • 如果将 InvokeRequired 检查放入 Update 方法会发生什么?
  • 好问题,我最初尝试过,同样的问题,我只是将调用检查移至自定义类,但要清理更新方法,谢谢
  • 请发布完整的堆栈跟踪和可能的更多代码。您的问题在 Application.Run 行中提到了 ArgumentException Parameter not valid 错误。这不应该与多线程问题有关。
  • 对于堆栈跟踪来说并不多。它类似于 [外部代码] -> Program.Run() -> [外部代码]
  • 因此,如果我不在我的表单或图片框上调用 refresh,则覆盖的 onpaint 只会在首次创建时被调用一次。如果我从后台线程调用表单(调用它)刷新,我会得到相同的异常,但在主线程上的实际刷新调用

标签: c# winforms thread-safety


【解决方案1】:

答案就在cmets中

问题在于调用 刷新需要从当前线程调用

this.Invoke((MethodInvoker)delegate
{
     this.m_LeftPictureBox.RefreshImage();
}

【讨论】:

    猜你喜欢
    • 2012-12-04
    • 1970-01-01
    • 1970-01-01
    • 2012-05-01
    • 1970-01-01
    • 1970-01-01
    • 2019-02-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多