从后台线程更新UI
关于从后台线程更新的另一个问题。从后台线程更新UI
为了说明问题:在应用程序中,后台线程需要更新UI。我考虑过使用中间集合来缓冲消息,并有一个计时器来显示它们。目前我们正在尝试最简单的方法。
代码尝试#1:
void foo(string status)
{
if (this.InvokeRequired)
{
BeginInvoke(new MethodInvoker(delegate()
{
InsertStatusMessage(status);
}));
}
else
{
InsertStatusMessage(status);
}
}
这似乎有一些瑕疵。 Msdn指出,如果尚未创建窗口句柄(在我看来,不可用),InvokeRequired
也返回false
。所以代码应该是:
void foo(string status)
{
if (this.InvokeRequired)
{
BeginInvoke(new MethodInvoker(delegate()
{
InsertStatusMessage(status);
}));
// wait until status is set
EndInvoke(result);
}
else if(this.IsHandleCreated)
{
InsertStatusMessage(status);
}
else
{
_logger.Error("Could not update status");
}
}
上面的代码也以某种方式抛出(对于未知和未复制的原因)。我们使用的DevExpress,这是未处理的异常消息(没有信息,也没有什么/错误发生的地点任何线索):
System.NullReferenceException:未设置为在 DevExpress的一个对象实例 对象引用。 Utils.Text.FontsCache.GetFontCacheByFont(图形图像, 字体的字体)的 DevExpress.Utils.Text.TextUtils.GetFontAscentHeight(图形克,字体 字体)在 DevExpress.XtraEditors.ViewInfo.BaseEditViewInfo.GetTextAscentHeight() 在 DevExpress.XtraEditors.ViewInfo.TextEditViewInfo.CalcTextBaseline(Graphics g)in DevExpress.XtraEditors.ViewInfo.BaseControlViewInfo.ReCalcViewInfo(图形 克,MouseButtons按钮,点mousePosition,矩形范围)在 DevExpress.XtraGrid.Views.Grid.ViewInfo.GridViewInfo.UpdateCellEditViewInfo(GridCellInfo 细胞,点mousePos结构,布尔canFastRecalculate,布尔计算值)在 DevExpress.XtraGrid.Views.Grid.ViewInfo.GridViewInfo.CreateCellEditViewInfo(GridCellInfo 细胞,布尔计算值,布尔allowCache)在 DevExpress.XtraGrid.Views.Grid.ViewInfo.GridViewInfo.RequestCellEditViewInfo(GridCellInfo 细胞)中 DevExpress.XtraGrid.Views.Grid.Drawing.GridPainter.DrawRegularRowCell在 的DevExpress(GridViewDrawArgs E,GridCellInfo CI)在 DevExpress.XtraGrid.Views.Grid.Drawing.GridPainter.DrawRow .XtraGrid.Views.Grid.Drawing.GridPainter.DrawRegularRow(GridViewDrawArgs E,GridDataRowInfo RI)(GridViewDrawArgs E,GridRowInfo RI)在 DevExpress.XtraGrid.Views .Grid.Drawing.GridPainter.DrawRows在 DevExpress.XtraGrid.Views.Grid.Drawing.GridPainter.Draw(ViewDrawArgs(GridViewDrawArgs e)在 DevExpress.XtraGrid.Views.Grid.Drawing.GridPainter.DrawContents(GridViewDrawArgs E) (在eX)中的DevExpress.XtraGrid.Views.Base.BaseView.Draw(GraphicsCache e)在System.Windows.Forms.Control.PaintWithErrorHandlin克(PaintEventArgs的 E,Int16的层,布尔disposeEventArgs)在 System.Windows.Forms.Control.WmPaint(消息&米) System.Windows.Forms.Control.WndProc(消息&米) DevExpress.XtraEditors。 DevExpress.XtraGrid.GridControl中的Container.EditorContainer.WndProc(消息& m)
。的WndProc(消息&米) System.Windows.Forms.Control.ControlNativeWindow.WndProc(消息&米)
在System.Windows.Forms.NativeWindow.Callback(IntPtr的的HWND,的Int32味精, IntPtr的WPARAM,IntPtr的LPARAM)
我想用Begin/End Invoke
,而不是Invoke
,因为它需要较少的东西(方法委托),它是更具可读性。
有什么我错过了,我怎么能放心地做线程调用?我只想在列表框中添加一条消息。我真的不在乎调用线程是否等待几毫秒。
您可以直接使用“MethodInvoker”调用“Invoke”。
void foo(string status)
{
Invoke(new MethodInvoker(() => {InsertStatusMessage(status);}));
}
我用这也与对照的DevExpress(尤其是异步更新的数据源的几个Xtragrids一种形式)。
有关MethodInvoker更多信息有一个excellent post。
这与我所做的相似。你确定这是100%正确吗?我认为你应该检查一切是否设置好,并且可以调用 – Odys 2012-04-12 09:55:05
这一行代码运行没有任何问题。只要继续尝试吧:)。文章链接更新了我的文章。 – 2012-04-12 10:00:59
class Test : Form
{
delegate void FooCallback(string status);
public Test()
{
}
private void foo(string status)
{
if (this.InvokeRequired == true)
{
FooCallback = new FooCallback(foo);
this.Invoke
(d, status);
}
else
{
//Do Things
}
}
}
使用MethodInvoker成本多少性能
我已反向工程所施加的逻辑和在所有情况下,是是错误的原因1个共同的因素:
- 对象引用不设置到一个对象的一个实例。
- 对象目前正在其他地方使用。
的错误是线程之间引入
DevExpress.Utils.Text.FontsCache.GetFontCacheByFont(Graphics graphics, Font font)
该类股Windows字体,这是严格禁止的。 GDI资源的的Windows使用油漆消息处理过程中也这样的东西
DevExpress.Utils.Text.TextUtils.GetStringSize(Graphics g, String text,
Font font, StringFormat stringFormat, Int32 maxWidth, Int32 maxHeight,
IWordBreakProvider wordBreakProvider, Boolean& isCropped)
仅限于已创建的GDI资源的线程。换句话说:
只有创建了字体或窗口的线程才可以使用它,所有其他线程都不允许向这些窗口发送windows画图消息。
测量窗口中字体的大小也是某种类型的绘画动作(虽然由于仅返回了绘制文本的大小而不可见)。
溶液(仅二者之一应该由DevExpress的实现):
- 类
Text.FontsCache
必须在当前线程ID添加到用于存储在高速缓存中创建的字体的ID。 - 每个线程必须自己创建类
Text.FontsCache
的实例。
为什么该方法在创建句柄之前触发?这看起来有点明显我 – 2012-04-12 09:37:37
@BoasEnkler我没有说它这样做。我不确定什么失败 – Odys 2012-04-12 09:38:21
有一些DevExpress控件根本无法使用Invoke从后台调用中更新。他们可能正忙着做一些将通过使用Invoke而被破坏的东西。使用另一种方法解决这个问题,并将数据结构更改/数据源设置为您的问题,以便我们有机会为您提供帮助。 – CodingBarfield 2012-04-12 09:44:30