2016-03-30 3 views
0

У меня есть приложение C# WinForms с элементом управления вкладкой и несколькими вкладками. Одна из вкладок содержит элемент управления сетью данных - в нем всего около 10 элементов, но данные заполняются запросом нескольких серверов и, следовательно, медленно загружаются.Ожидание сообщения при заполнении элемента управления datagrid

Когда я запускаю свое приложение и выбираю вкладку с элементом управления datagrid, приложение, похоже, зависает, пытаясь запросить все серверы и заполнить сетку.

Вместо того, чтобы подвешивать, я хотел бы, чтобы приложение было отзывчивым и для него отображалось сообщение «please wait ...», которое исчезнет после заполнения данных.

То, что я пытался сделать, это создать предпосылки работника как таковые:

if (tabctrl.SelectedTab == tabctrl.TabPages["tabServices"]) 
{ 

    this.dgrdServices.RowPrePaint += new DataGridViewRowPrePaintEventHandler(dgrdServices_RowPrePaint); 
    this.dgrdServices.CellContentClick += new DataGridViewCellEventHandler(dgrdServices_CellClick); 

    BackgroundWorker bw = new BackgroundWorker(); 
    lblLoading.Visible = true; 
    bw.RunWorkerAsync(); 
    bw.DoWork += new DoWorkEventHandler(bw_DoWork); 
    bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted); 


} 

private void bw_DoWork(object sender, DoWorkEventArgs e) 
{ 

    PopulateServicesDataGrid(); 
} 

private void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) 
{ 
    lblLoading.Visible = false; 
} 

private void PopulateServicesDataGrid() 
{ 
    int x = 0; 

    foreach (Service Service in Globals.Services) 
    { 
     // Add a row to the datagrid for each service 
     this.dgrdServices.Rows.Add(); 

     // Update the current service status 
     Service.Status = Service.Query(Service.Server, Service.Name); 
     if (Service.Status == "running") 
     { 
      this.dgrdServices.Rows[x].Cells[0].Value = Properties.Resources.green_dot; 
      this.dgrdServices.Rows[x].Cells[4].Value = Properties.Resources.stop_enabled; 
     } 
     else 
     { 
      this.dgrdServices.Rows[x].Cells[0].Value = Properties.Resources.grey_dot; 
      this.dgrdServices.Rows[x].Cells[4].Value = Properties.Resources.start_enabled; 
     } 

     this.dgrdServices.Rows[x].Cells[1].Value = Service.Server.ToUpper(); 
     this.dgrdServices.Rows[x].Cells[2].Value = Service.FreindlyName; 
     this.dgrdServices.Rows[x].Cells[3].Value = Service.Status; 
     this.dgrdServices.Rows[x].Cells[5].Value = "Uninstall"; 
     this.dgrdServices.Rows[x].Cells[6].Value = Service.Name; 
     x++; 
    } 
} 

PopulateServicesDataGrid() содержит код, который перебирает некоторые объекты и запросы несколько различных серверов для статуса службы.

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

Почему рабочий рабочий рабочий/datagrid не работает?

+1

Вы должны проверить наличие ошибок в событии RunWorkerCompleted в параметре e. Запуск RunWorkerAsync() перед подключением событий просто выглядит странно. Не публиковать ваш PopulateServicesDataGrid не помогает нам помочь вам. – LarsTech

+0

Фоновый работник - отдельный процесс и не отображается в сетке. Запросы к серверу должны быть в фоновом рабочем документе, где работник backgroundwork возвращает DataTable. Затем заполните DataGrid с помощью возвращаемого DataTable. – jdweng

+0

Опубликован PopulationServiceDataGrid. – Brad

ответ

1

В вашем PopulateServicesDataGrid Я предполагаю, что вы взаимодействуете с элементом управления пользовательского интерфейса, который не работает, потому что фоновый рабочий работает в другом потоке, чем ваш контекст пользовательского интерфейса. Вам нужно будет разработать механизм для работы таким образом, который возвращает информацию, которую вы хотите поместить в сетку, а затем обратно в контекст потока пользовательского интерфейса (RunWorkerCompleted), заполнить сетью информацию, которую вы получаете в Выполнять работу.

В любое время, когда вы используете фонового работника, вам нужно разделить свои взаимодействия с элементами управления пользовательского интерфейса и после того, как работник backgroundwork возобновит взаимодействие с вашим пользовательским интерфейсом.

Вы также подключаете события после вызова RunWorkerAsync, сначала подключите свои события, затем вызовите RunWorkerAsync.

Редактировать, чтобы отразить комментарий с примером:

Грубый пример того, как вы могли бы это сделать, на основе кода я вижу.

private void bw_DoWork(object sender, DoWorkEventArgs e) 
{ 
    QueryServices() 
} 

private void QueryServices() 
{ 
    foreach (Service Service in Globals.Services) 
    { 
     Service.Status = Service.Query(Service.Server, Service.Name); 
    } 
} 

    private void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) 
    { 
     PopulateServicesDataGrid(); 
     lblLoading.Visible = false; 
    } 

    private void PopulateServicesDataGrid() 
    { 
     //Do everything else you are doing originally in this method minus the Service.Query calls. 
    } 
+0

Перемещено RunWorkerAsync для завершения - спасибо! Любые предложения о том, как вернуть информацию обратно в сетку? Я не делал многопоточности до – Brad

+0

@Brad. Похоже, у вас уже есть коллекция в Globals.Services, это своего рода странный способ заполнения, но с этим вам нужно работать. Вы можете продолжить свой foreach и просто сделать запрос на каждом из них. В завершенном событии, заходите обратно на них и выполняйте логику на основе пользовательского интерфейса, которая уже существует в методе. Это предполагает, что Service.Status сохранит свое состояние после завершения работника, что я не на 100% от приведенного в настоящее время кода. – davidallyoung

+0

Извините, я прочитал ваш ответ примерно дюжину раз. Я не уверен, что следую тому, что вы предлагаете. Не могли бы вы уточнить или уточнить? Может быть, с примером? Сожалею! – Brad

1

Метод bw_DoWork работает в другом потоке от ThreadPool. Для доступа к объекту WinForms из других потоков требуется синхронизация. Лучший способ сделать это - использовать AsyncOperationManager. Вы должны создать AsyncOperation в потоке графического интерфейса и использовать его внутри PopulateServicesDataGrid для отправки или публикации результатов.

Другой способ - обновить DataGrid с помощью подготовленных данных внутри bw_RunWorkerComplete - он уже синхронизирован компонентом BackgroundWorker.

Более современный способ сделать то же самое - использовать async-задачи, но для этого требуется базовый уровень знаний TPL.

Смежные вопросы