2012-04-12 4 views
1

Еще один вопрос об обновлении из фоновых потоков.Обновление пользовательского интерфейса из фоновых потоков

Чтобы понять: В приложении фоновым потокам необходимо обновить интерфейс. Я рассмотрел возможность использования промежуточной коллекции для буферизации сообщений и таймера для их отображения. На данный момент мы пробовали простейший подход.

Код попытка # 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 (Графика g) в DevExpress.XtraEditors.ViewInfo.BaseControlViewInfo.ReCalcViewInfo (график г, кнопки MouseButtons, точка mousePosition, Прямоугольный) в DevExpress.XtraGrid.Views.Grid.ViewInfo.GridViewInfo.UpdateCellEditViewInfo (GridCellInfo клетки, точка mousePos, Boolean 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 (GridViewDrawArgs e, GridCellInfo ci) в DevExpress .XtraGrid.Views.Grid.Drawing.GridPainter.DrawRegularRow (GridViewDrawArgs е, GridDataRowInfo ри) в DevExpress.XtraGrid.Views.Grid.Drawing.GridPainter.DrawRow (GridViewDrawArgs е, GridRowInfo ри) в DevExpress.XtraGrid.Views .Grid.Drawing.GridPainter.DrawRows (GridViewDrawArgs е) в DevExpress.XtraGrid.Views.Grid.Drawing.GridPainter.DrawContents (GridViewDrawArgs е) в DevExpress.XtraGrid.Views.Grid.Drawing.GridPainter.Draw (ViewDrawArgs й) в DevExpress.XtraGrid.Views.Base.BaseView.Draw (GraphicsCache е) в DevExpress.XtraGrid.GridControl.OnPaint (PaintEventArgs е)
в System.Windows.Forms.Control.PaintWithErrorHandlin г (PaintEventArgs е, слой Int16, Boolean disposeEventArgs) в System.Windows.Forms.Control.WmPaint (Message & м) в System.Windows.Forms.Control.WndProc (Message & м) в DevExpress.XtraEditors. Container.EditorContainer.WndProc (Сообщение & m)
в DevExpress.XtraGrid.GridControl.WndProc (Message & м) в System.Windows.Forms.Control.ControlNativeWindow.WndProc (Message & м)
в System.Windows.Forms.NativeWindow.Callback (IntPtr HWND, Int32 тзд, IntPtr WPARAM, IntPtr LPARAM)

Я хочу использовать Begin/End Invoke вместо Invoke, поскольку он требует меньше материала (метод делегирует) и более читаемым.

Что я пропустил, как я могу безопасно выполнять вызов потока? Я просто хочу добавить сообщение в список. Мне действительно все равно, будет ли вызывающий поток ждать несколько миллисекунд.

+0

Почему метод триггера до создания дескриптора? Мне это кажется немного очевидным –

+0

@BoasEnkler Я не говорил, что это так. Я не уверен, что не удалось – Odys

+0

Есть некоторые элементы управления DevExpress, которые просто НЕ МОГУТ быть обновлены из фонового вызова с использованием Invoke. Они могут быть заняты тем, что будет нарушено с помощью Invoke. Используйте другой подход, чтобы исправить это и включить способ изменения структуры данных/источник данных, в который вы ставите вопрос, чтобы дать нам шанс помочь вам. – CodingBarfield

ответ

2

Вы можете позвонить непосредственно «Invoke» с помощью «MethodInvoker».

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

Я использовал это также со средствами управления DevExpress (особенно ASync обновлять источники данных по нескольким Xtragrids на одной форме).

Для получения дополнительной информации о MethodInvoker есть excellent post.

+0

Это похоже на то, что я делаю. Вы уверены, что это на 100% правильно? Я думаю, вы должны проверить, все ли настроено и может быть вызвано. – Odys

+1

Этот однострочный кадр работает без проблем. Просто продолжайте и попробуйте :). Обновлено мое сообщение со ссылкой на статью. –

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. объекта не задана ссылкой к экземпляру объекта.
  2. Объект в настоящее время используется в другом месте.

Ошибка введение

DevExpress.Utils.Text.FontsCache.GetFontCacheByFont(Graphics graphics, Font font) 

Это акции класса окна шрифтов между потоками, и это СТРОГО ЗАПРЕЩЕНО. Использование Окна GDI ресурсов, во время красочного обработки сообщений, но и такие вещи, как

DevExpress.Utils.Text.TextUtils.GetStringSize(Graphics g, String text, 
    Font font, StringFormat stringFormat, Int32 maxWidth, Int32 maxHeight, 
    IWordBreakProvider wordBreakProvider, Boolean& isCropped) 

ограничивается потоком, который создал ресурс GDI. Другими словами:

Только те темы, которые создали шрифт или окно, могут использовать его, все остальные потоки не могут отправлять сообщения о красках окон в эти окна.

Измерение размера шрифта в окне также является одним из видов действия краски (хотя и невидимым, потому что возвращается только размер окрашенного текста).

Solution (только один из обоих должны быть реализованы с помощью DevExpress):

  1. Класс Text.FontsCache должен добавить текущий-нить-ID идентификатору, который используется для хранения созданного шрифта в кэше.
  2. Каждый поток должен иметь собственный экземпляр класса Text.FontsCache.
Смежные вопросы