2012-04-12 223 views
1

关于从后台线程更新的另一个问题。从后台线程更新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,因为它需要较少的东西(方法委托),它是更具可读性。

有什么我错过了,我怎么能放心地做线程调用?我只想在列表框中添加一条消息。我真的不在乎调用线程是否等待几毫秒。

+0

为什么该方法在创建句柄之前触发?这看起来有点明显我 – 2012-04-12 09:37:37

+0

@BoasEnkler我没有说它这样做。我不确定什么失败 – Odys 2012-04-12 09:38:21

+0

有一些DevExpress控件根本无法使用Invoke从后台调用中更新。他们可能正忙着做一些将通过使用Invoke而被破坏的东西。使用另一种方法解决这个问题,并将数据结构更改/数据源设置为您的问题,以便我们有机会为您提供帮助。 – CodingBarfield 2012-04-12 09:44:30

回答

2

您可以直接使用“MethodInvoker”调用“Invoke”。

void foo(string status) 
{ 
    Invoke(new MethodInvoker(() => {InsertStatusMessage(status);})); 
} 

我用这也与对照的DevExpress(尤其是异步更新的数据源的几个Xtragrids一种形式)。

有关MethodInvoker更多信息有一个excellent post

+0

这与我所做的相似。你确定这是100%正确吗?我认为你应该检查一切是否设置好,并且可以调用 – Odys 2012-04-12 09:55:05

+1

这一行代码运行没有任何问题。只要继续尝试吧:)。文章链接更新了我的文章。 – 2012-04-12 10:00:59

1
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成本多少性能

0

我已反向工程所施加的逻辑和在所有情况下,是是错误的原因1个共同的因素:

  1. 对象引用不设置到一个对象的一个​​实例。
  2. 对象目前正在其他地方使用。

的错误是线程之间引入

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的实现):

  1. Text.FontsCache必须在当前线程ID添加到用于存储在高速缓存中创建的字体的ID。
  2. 每个线程必须自己创建类Text.FontsCache的实例。