所以我正在写一个小的Twitter客户端供我使用。我正在使用一个大面板的组合,较小的面板代表各个推文。在每个较小的面板中,我有一个PictureBox和一个RichTextBox。
现在,我的问题是加载超过10条推文导致速度减慢,因为我正在动态生成面板。所以我决定使用BackgroundWorker执行此操作,然后将这些面板添加到主面板。
我已经多次这样做了,将文本写入来自不同thead的文本框(甚至在其上编写了教程)。然而,我无法让它发挥作用。我收到错误消息:
Cross-thread operation not valid: Control '' accessed from a thread other than the thread it was created on.
代码:
List<Panel> panelList = new List<Panel>();
foreach (UserStatus friendStatus in list)
{
PictureBox pbTweet = new PictureBox();
// ...
// code to set numerous properties
// ...
RichTextBox rtbTweet = new RichTextBox();
// ...
// code to set numerous properties
// ...
Panel panelTweet = new Panel();
// ...
// code to set numerous properties
// ...
panelTweet.Controls.Add(pbTweet);
panelTweet.Controls.Add(rtbTweet);
panelList.Add(panelTweet);
}
if (panelMain.InvokeRequired)
panelMain.BeginInvoke((MethodInvoker)delegate { foreach (Panel p in panelList) { panelMain.Controls.Add(p); } });
有人注意到任何问题吗?
答案 0 :(得分:4)
panelTweet
是在BackgroundWorker
的主题上创建的,可以从代理中的主线程(panelMain.Controls.Add(p);// p = panelTweet
)进行访问。
您必须在主线程中调用所有该功能,而不仅仅是最后一部分。
你可以像这样重写这个函数:
private void AddControls()
{
if(panelMain.InvokeRequired)
{
panelMain.BeginInvoke(new MethodInvoker(AddControls));
return;
}
foreach (UserStatus friendStatus in list)
{
PictureBox pbTweet = new PictureBox();
// ...
// code to set numerous properties
// ...
RichTextBox rtbTweet = new RichTextBox();
// ...
// code to set numerous properties
// ...
Panel panelTweet = new Panel();
// ...
// code to set numerous properties
// ...
panelTweet.Controls.Add(pbTweet);
panelTweet.Controls.Add(rtbTweet);
panelMain.Controls.Add(panelTweet)
}
}
答案 1 :(得分:1)
使用后台线程时,必须完全分离从修改表单控件的部分检索数据的部分。必须在UI线程上调用修改表单控件的所有代码,即使这需要一些时间。没有办法解决这个问题。
这通常是一个好策略,因为通常情况下,将数据存入内存是缓慢的部分,更新UI是快速部分(相对于彼此)。
在您的代码示例中,所有代码都是UI修改部分,因此它必须全部进入UI线程。
编辑:要优化用户界面部分,您可以尝试在要修改的面板上调用SuspendLayout
和ResumeLayout
。
答案 2 :(得分:1)
您可以尝试在ProgressChanged处理程序中创建控件。这样你就可以在后台线程中进行一些初始化(用户图片检索等),并在GUI线程中进行可视化。
请注意,很可能是您的性能问题是由于创建RichTextEdit和PictureBox所需的大量资源。考虑创建自定义控件,该控件将仅包含在Paint事件上呈现的用户和文本的图像,例如
答案 3 :(得分:1)
您无法在后台线程中创建任何WinForms UI控件。
有几种方法可以解决这个问题 - 我从以下开始:
Control getPanelForUser( UserStatus friendStatus ) {
PictureBox pbTweet = new PictureBox { /* set props */ };
RichTextBox rtbTweet = new RichTextBox { /* set props */ };
Panel panelTweet = new Panel { /* set props */ };
panelTweet.Controls.Add(pbTweet);
panelTweet.Controls.Add(rtbTweet);
return panelTweet;
}
然后在你的后台工作人员中:
foreach (UserStatus friendStatus in list)
panelMain.BeginInvoke(
delegate ( object o ) {
panelMain.Controls.Add(getPanelForUser( o as UserStatus ));
},
friendStatus );
虽然可能仍然很慢 - 可能值得加载一个子集,然后滴下进一步输入。你也可以只加载可见列表 - 隐藏更多的列表直到它们滚动。然后你一次只加载一个页面。
答案 4 :(得分:0)
您尝试将在外部线程X上创建的Panel p添加回winform线程Y.
将整个创建放在BeginInvoke处理程序中。这样,所有控件都在winform线程Y中创建。
答案 5 :(得分:0)
好的,所以看看答案,看起来我只是SOL。我需要在UI线程中进行所有处理,从而导致它无响应。