Невозможно обновить интерфейс Silverlight из асинхронного метода, завершившего обратный вызов - PullRequest
0 голосов
/ 17 февраля 2012

Я унаследовал некоторый код, который запрашивает БД через службу WCF, а затем использует обратный вызов, когда это сделано. Я пытаюсь добавить код к этому обратному вызову, чтобы обновить пользовательский интерфейс при обработке данных. Я обнаружил, что не могу обновить интерфейс во время этого обратного вызова:

client.GetDataAsync();
client.GetDataCompleted += new EventHandler<GetDataCompletedEventArgs>(GetDataCompleted);

void GetDataCompleted(object sender, GetDataCompletedEventArgs e)
{
  // Loop through the data
  // ...
  textBlock1.Text= "test1";
  Dispatcher.BeginInvoke(() => textBlock1.Text= "test2" );
  var thread = new Thread(() =>
  {
     // textBlock1.Text= "test3"; (this throws a cross-thread access exception)
     Dispatcher.BeginInvoke(() =>
     {
       textBlock1.Text= "test4";
     });
  }
  thread.Start();
  // ...
  Debug.WriteLine("done");
}

Ничто из этого не обновляет интерфейс, пока (по-видимому) весь обратный вызов не будет завершен. Этот пост:

Какой поток вызывает обработчик завершенных событий при вызовах WCF silverlight?

предполагает, что обратный вызов выполняется в главном потоке пользовательского интерфейса, поэтому вызов BeginInvoke не требуется. Даже если я добавлю различные задержки в приведенный выше код, он все равно не работает. Это возможно? Есть ли лучший способ сделать это?

(Это следующий вопрос: Несколько обновлений асинхронного интерфейса в Silverlight )

1 Ответ

1 голос
/ 18 февраля 2012

degorolls правильно, предлагая TPL, ваш код будет выглядеть следующим образом (кроме как без комментариев) (Кроме того, исключения ДОЛЖНЫ обрабатываться в TPL, так что это может сделать его не стоящим, но я не думаю, что это должно),Первые методы остались бы прежними, и да, в асинхронном программировании, основанном на событиях, заботится о безопасности потоков (т. Е. Вы всегда возвращаетесь в тот же поток, из которого вы вызывали). Я также заметил, что весь текстовый вывод выполняется вместо =из + =, но это, вероятно, больше проблема ввода в переполнение Итак, test1 и test2 будут распечатываться одновременно, однако все, что выплевывается из кода TPL, должно печататься по мере поступления. Код пользовательского интерфейса не долженделать все, что требует слишком много времени, но ... только обновление интерфейса.Итак, думаете ли вы об этом как о точке рефакторинга?Дайте мне знать, если это поможет или я пропустил то, что вы искали.

client.GetDataAsync();
client.GetDataCompleted += new EventHandler<GetDataCompletedEventArgs>(GetDataCompleted);

void GetDataCompleted(object sender, GetDataCompletedEventArgs e)
{
  // Loop through the data
  // ...
  textBlock1.Text= "test1";
  //////Dispatcher should not be needed here as this IS on the main UI thread
  Dispatcher.BeginInvoke(() => textBlock1.Text= "test2" );
  //////Everything that happens here should NOT be on the main UI thread, thus the cross-thread access exception
  //////You can do Dispatcher.CheckAccess to determine if you need to invoke or not
  //////Notice the newCopyOfDataToBeWritten. This is a closure, 
  //////so using the same referenced object will result in errant data as it loops
  //////Also, doing it this way does not guarantee any order that this will be written out
  //////This will utilize the parallel fully, but there are ways to force the order
  var task = Task.Factory.StartNew(()=>
    {
      Dispatcher.BeginInvoke(()=>textBlock1.Text += newCopyOfDataToBeWritten)
    }
  );
  // ...
  ///////I assume this is the end of the loop?
  Debug.WriteLine("done");
}

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

 var outsideThread = new Thread(()=>
 {         
     for(int i = 0; i < 20; i++)
          {
              //This code will show all at once since it is on the main thread, 
              //which is still running
              //If you want this to display one at a time also, then you need
              //to use threads and callbacks like below, also
              Dispatcher.BeginInvoke(()=>{textBlock1.Text += "outer" + i;});
              int newI = i;
              var thread = new Thread(() =>
              {
                  System.Threading.Thread.Sleep(1000 * newI);
                  Dispatcher.BeginInvoke(() =>
                  {
                      //This will display as it comes in

                      textBlock1.Text += "inner" + newI;
                  });
              });
              thread.Start();
          }
});
outsideThread.Start();
...