我是C#的后台工作者。 这是一个类,在它下面,你会找到它的实例,在那里我将为你定义我的问题:
我有类绘图:
class Drawing
{
BackgroundWorker bgWorker;
ProgressBar progressBar;
Panel panelHolder;
public Drawing(ref ProgressBar pgbar, ref Panel panelBig) // Progressbar and panelBig as reference
{
this.panelHolder = panelBig;
this.progressBar = pgbar;
bgWorker = new BackgroundWorker();
bgWorker.WorkerReportsProgress = true;
bgWorker.WorkerSupportsCancellation = true;
bgWorker.DoWork += new OpenNETCF.ComponentModel.DoWorkEventHandler(this.bgWorker_DoWork);
bgWorker.RunWorkerCompleted += new OpenNETCF.ComponentModel.RunWorkerCompletedEventHandler(this.bgWorker_RunWorkerCompleted);
bgWorker.ProgressChanged += new OpenNETCF.ComponentModel.ProgressChangedEventHandler(this.bgWorker_ProgressChanged);
}
public void createDrawing()
{
bgWorker.RunWorkerAsync();
}
private void bgWorker_DoWork(object sender, DoWorkEventArgs e)
{
Panel panelContainer = new Panel();
// Adding panels to the panelContainer
for(i=0; i<100; i++)
{
Panel panelSubpanel = new Panel();
// Setting size, color, name etc....
panelContainer.Controls.Add(panelSubpanel); // Adding the subpanel to the panelContainer
//Report the progress
bgWorker.ReportProgress(0, i); // Reporting number of panels loaded
}
e.Result = panelContainer; // Send the result(a panel with lots of subpanels) as an argument
}
private void bgWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
this.progressBar.Value = (int)e.UserState;
this.progressBar.Update();
}
private void bgWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Error == null)
{
this.panelHolder = (Panel)e.Result;
}
else
{
MessageBox.Show("An error occured, please try again");
}
}
}
设置此类的对象:
public partial class Draw: Form
{
public Draw()
{
ProgressBar progressBarLoading = new ProgressBar();
// Set lots of properties on progressBarLoading
Panel panelBigPanelContainer = new Panel();
Drawing drawer = new Drawing(ref progressBarLoading, ref panelBigPanelContainer);
drawer.createDrawing(); // this makes the object start a new thread, loading all the panels into a panel container, while also sending the progress to this progressbar.
}
}
这是我的问题: 在private void bgWorker_RunWorkerCompleted(对象发送者,RunWorkerCompletedEventArgs e)
我没有得到应有的e.Result。 当我调试并查看e.Result时,面板的属性有以下异常消息:
'((System.Windows.Forms.Control)(e.Result)).ClientSize' threw an exception of type 'System.ObjectDisposedException'
所以对象被处理了,但“为什么”是我的问题,我该如何解决这个问题呢?
我希望有人能回答我,这让我发疯。 我有另一个问题:是否允许使用带有参数的“ref”?编程错了吗?
提前致谢。
我还写了下面我对以下背景工作者的理解:
我认为这是背景工作者的“规则”:
bgWorker.RunWorkerAsync(); => starts a new thread.
bgWorker_DoWork cannot reach the main thread without delegates
-
private void bgWorker_DoWork(object sender, DoWorkEventArgs e)
{
// The work happens here, this is a thread that is not reachable by
the main thread
e.Result => This is an argument which can be reached by
bgWorker_RunWorkerCompleted()
bgWorker.ReportProgress(progressVar); => Reports the progress to the
bgWorker_ProgressChanged()
}
-
private void bgWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
// I get the progress here, and can do stuff to the main thread from here
(e.g update a control)
this.ProgressBar.Value = e.ProgressPercentage;
}
-
private void bgWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
// This is where the thread is completed.
// Here i can get e.Result from the bgWorker thread
// From here i can reach controls in my main thread, and use e.Result in my main thread
if (e.Error == null)
{
this.panelTileHolder = (Panel)e.Result;
}
else
{
MessageBox.Show("There was an error");
}
}
答案 0 :(得分:1)
我不能按照你的代码,“imagePanel”似乎从天而降,没有任何想法它是如何创建的。但是,您所做的非常违法,Windows要求控件的父级(由您的Controls.Add()调用设置)是在与子级相同的线程中创建的窗口。 .NET 2.0通常会检查这一点,并在违反该规则时生成IllegalOperationException,很难猜出为什么他们会将其从CF中删除。如果他们确实这样做了。
ObjectDisposedException与BackgroundWorker的RunWorkerCompleted或ProgressChanged事件运行且窗体关闭时很常见。在允许表单消失之前,您必须确保取消BGW。这有点无关紧要,无论如何你必须完全重新设计。
答案 1 :(得分:0)
您正在另一个线程上创建UI控件(Panel)并将容器面板返回到主线程。 UI控件具有线程关联性。当后台工作者完成时,它使用的线程被释放回线程池,并且在该过程中,与该线程相关联的UI控件显然被丢弃。稍后您尝试在主线程中的RunWorkerCompleted事件处理程序中使用已处置的面板对象时,将获得ObjectDisposedException。
您需要在UI所在的主线程中创建这些面板。您可以在主线程中运行的ProgressChanged事件处理程序中创建它们,也可以调用另一个检查InvokeRequired的方法,如果是,则通过调用Invoke方法调用主线程上的操作。您可以隐藏这些面板,直到创建所有面板,并在RunWorkerCompleted事件处理程序中显示它们。
我建议你看看下面的博文。
WinForms UI Thread Invokes: An In-Depth Review of Invoke/BeginInvoke/InvokeRequred