Yet another question about updating from background threads.
To get to the point: In the application, background threads need to update UI. I’ve considered using an in-between collection to buffer messages and have a timer to display them. At the moment we are trying a simplest approach.
Code attempt #1:
void foo(string status)
{
if (this.InvokeRequired)
{
BeginInvoke(new MethodInvoker(delegate()
{
InsertStatusMessage(status);
}));
}
else
{
InsertStatusMessage(status);
}
}
This seems to have some flaws. Msdn states that InvokeRequired also returns false if the window handle hasn’t been created yet (not available, in my opinion). So the code should be:
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");
}
}
The code above somehow also throws (for an unknown and not replicated reason). We use DevExpress and this is the unhandled exception message (no information nor any clue on what/where the error happened):
System.NullReferenceException: object reference not set to an instance
of an object in
DevExpress.Utils.Text.FontsCache.GetFontCacheByFont(Graphics graphics,
Font font) in
DevExpress.Utils.Text.TextUtils.GetFontAscentHeight(Graphics g, Font
font) in
DevExpress.XtraEditors.ViewInfo.BaseEditViewInfo.GetTextAscentHeight()
in
DevExpress.XtraEditors.ViewInfo.TextEditViewInfo.CalcTextBaseline(Graphics
g) in
DevExpress.XtraEditors.ViewInfo.BaseControlViewInfo.ReCalcViewInfo(Graphics
g, MouseButtons buttons, Point mousePosition, Rectangle bounds) in
DevExpress.XtraGrid.Views.Grid.ViewInfo.GridViewInfo.UpdateCellEditViewInfo(GridCellInfo
cell, Point mousePos, Boolean canFastRecalculate, Boolean calc) in
DevExpress.XtraGrid.Views.Grid.ViewInfo.GridViewInfo.CreateCellEditViewInfo(GridCellInfo
cell, Boolean calc, Boolean allowCache) in
DevExpress.XtraGrid.Views.Grid.ViewInfo.GridViewInfo.RequestCellEditViewInfo(GridCellInfo
cell) in
DevExpress.XtraGrid.Views.Grid.Drawing.GridPainter.DrawRegularRowCell(GridViewDrawArgs
e, GridCellInfo ci) in
DevExpress.XtraGrid.Views.Grid.Drawing.GridPainter.DrawRegularRow(GridViewDrawArgs
e, GridDataRowInfo ri) in
DevExpress.XtraGrid.Views.Grid.Drawing.GridPainter.DrawRow(GridViewDrawArgs
e, GridRowInfo ri) in
DevExpress.XtraGrid.Views.Grid.Drawing.GridPainter.DrawRows(GridViewDrawArgs
e) in
DevExpress.XtraGrid.Views.Grid.Drawing.GridPainter.DrawContents(GridViewDrawArgs
e) in
DevExpress.XtraGrid.Views.Grid.Drawing.GridPainter.Draw(ViewDrawArgs
ee) in DevExpress.XtraGrid.Views.Base.BaseView.Draw(GraphicsCache
e) in DevExpress.XtraGrid.GridControl.OnPaint(PaintEventArgs e)
in System.Windows.Forms.Control.PaintWithErrorHandling(PaintEventArgs
e, Int16 layer, Boolean disposeEventArgs) in
System.Windows.Forms.Control.WmPaint(Message& m) in
System.Windows.Forms.Control.WndProc(Message& m) in
DevExpress.XtraEditors.Container.EditorContainer.WndProc(Message& m)
in DevExpress.XtraGrid.GridControl.WndProc(Message& m) in
System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message& m)
in System.Windows.Forms.NativeWindow.Callback(IntPtr hWnd, Int32 msg,
IntPtr wparam, IntPtr lparam)
I want to use Begin/End Invoke instead of Invoke because it requires less stuff (method delegates) and it is more readable.
What have I missed, how can I safely do thread invoking? I just want to add a message in a listbox. I really don’t care if the calling thread will waits for a few milliseconds.
You can call directly “Invoke” with “MethodInvoker”.
I used this also with DevExpress controls (especially to async update the data sources on several Xtragrids on one form).
For more information about MethodInvoker there is an excellent post.