Avoid calling Invoke when the control is disposed

2019-01-09 05:15发布

I have the following code in my worker thread (ImageListView below is derived from Control):

if (mImageListView != null && 
    mImageListView.IsHandleCreated &&
    !mImageListView.IsDisposed)
{
    if (mImageListView.InvokeRequired)
        mImageListView.Invoke(
            new RefreshDelegateInternal(mImageListView.RefreshInternal));
    else
        mImageListView.RefreshInternal();
}

However, I get an ObjectDisposedException sometimes with the Invoke method above. It appears that the control can be disposed between the time I check IsDisposed and I call Invoke. How can I avoid that?

14条回答
beautiful°
2楼-- · 2019-01-09 05:47

If a BackGroundWorker is a possibility, there's a very simple way to circumvent this:

public partial class MyForm : Form
{
    private void InvokeViaBgw(Action action)
    {
        BGW.ReportProgress(0, action);
    }

    private void BGW_ProgressChanged(object sender, ProgressChangedEventArgs e)
    {
        if (this.IsDisposed) return; //You are on the UI thread now, so no race condition

        var action = (Action)e.UserState;
        action();
    }

    private private void BGW_DoWork(object sender, DoWorkEventArgs e)
    {
       //Sample usage:
       this.InvokeViaBgw(() => MyTextBox.Text = "Foo");
    }
}
查看更多
爷、活的狠高调
3楼-- · 2019-01-09 05:48

One way might be to call the method itself ones more instead of invoking the ImageListView-Method:

if (mImageListView != null && 
    mImageListView.IsHandleCreated &&
    !mImageListView.IsDisposed)
{
    if (mImageListView.InvokeRequired)
        mImageListView.Invoke(new YourDelegate(thisMethod));
    else
        mImageListView.RefreshInternal();
}

That way it would check one more time before finally calling RefreshInternal().

查看更多
Ridiculous、
4楼-- · 2019-01-09 05:48

This works for me

if (this.IsHandleCreated){
    Task.Delay(500).ContinueWith(_ =>{
        this.Invoke(fm2);
    });
} else {
  this.Refresh();
}
查看更多
做个烂人
5楼-- · 2019-01-09 05:50

There are implicit race conditions in your code. The control can be disposed between your IsDisposed test and the InvokeRequired test. There's another one between InvokeRequired and Invoke(). You can't fix this without ensuring the control outlives the life of the thread. Given that your thread is generating data for a list view, it ought to stop running before the list view disappears.

Do so by setting e.Cancel in the FormClosing event and signaling the thread to stop with a ManualResetEvent. When the thread completes, call Form.Close() again. Using BackgroundWorker makes it easy to implement the thread completion logic, find sample code in this post.

查看更多
我只想做你的唯一
6楼-- · 2019-01-09 05:51

Handle the Form closing event. Check to see if your off UI thread work is still happening, if so start to bring it down, cancel the closing event and then reschedule the close using BeginInvoke on the form control.

private void Form_FormClosing(object sender, FormClosingEventArgs e)
{
    if (service.IsRunning)
    {
        service.Exit();
        e.Cancel = true;
        this.BeginInvoke(new Action(() => { this.Close(); }));
    }
}
查看更多
啃猪蹄的小仙女
7楼-- · 2019-01-09 06:01

Try using

if(!myControl.Disposing)
    ; // invoke here

I had the exact same problem as you. Ever since I switched to checking .Disposing on the control, the ObjectDisposedException has gone away. Not saying this will fix it 100% of the time, just 99% ;) There is still a chance of a race condition between the check to Disposing and the call to invoke, but in the testing I've done I haven't ran into it (I use the ThreadPool and a worker thread).

Here's what I use before each call to invoke:

    private bool IsControlValid(Control myControl)
    {
        if (myControl == null) return false;
        if (myControl.IsDisposed) return false;
        if (myControl.Disposing) return false;
        if (!myControl.IsHandleCreated) return false;
        if (AbortThread) return false; // the signal to the thread to stop processing
        return true;
    }
查看更多
登录 后发表回答