2017-09-15 73 views
2

以下是我所嘗試的 - 它的工作原理,只要看到用戶界面刷新,但我不認爲它是最佳使用異步/等待。我該如何改進?在處理時更新WPF UI - 最佳使用異步等待

private async void btnQuickTest_Click(object sender, RoutedEventArgs e) 
{ 
    XmlReader reader; 
    int rowCount = 0; 
    using (reader = XmlReader.Create("someXmlFile.xml")) 
    { 
     while (reader.Read()) 
     { 
      rowCount++; 

      DoSomeProcessingOnTheUIThread(reader); 

      //only update UI every 100 loops 
      if (rowCount > 0 && rowCount % 100 == 0) 
      { 
       //yay! release the UI thread 
       await Task.Run(() => 
       { 
        Application.Current.Dispatcher.Invoke(
         () => 
         { 
          txtRowCount.Text = string.Format("{0}", rowCount); 
         }); 
       }); 
      } 

     } //end-while 
    }//end-using 
} 

什麼是更好的方法?

UPDATE: 我避免基於克萊門斯的答案Dispatcher.Invoke,通過發送處理後臺任務,並直接對UI更新進度。 我的代碼現在看起來像。

private async void btnQuickTest_Click(object sender, RoutedEventArgs e) 
{ 
    XmlReader reader; 
    int rowCount = 0; 
    using (reader = XmlReader.Create("someXmlFile.xml")) 
    { 
     while (reader.Read()) 
     { 
      rowCount++; 

      await DoSomeProcessing(reader); 

      //only update UI every 100 loops 
      if (rowCount % 100 == 0) 
      { 
       txtRowCount.Text = string.Format("{0}", rowCount); 
      } 

     } //end-while 
    }//end-using 
    MessageBox.Show("I am done!"); 
} 

private Task DoSomeProcessing(XmlReader reader) 
{ 
    Task t =Task.Run(() => 
    { 
     //Do my processing here. 
    }); 
    return t; 
} 

更新#2: 在反思,爲什麼我在每個循環創建一個新的任務? 在一個後臺任務中運行整個循環可能會更好。並定期提出回調以顯示進度;見下面我的其他答案。

+3

如果您的代碼正常工作,但您希望某人提出改進建議,您可能需要在代碼複審中提出此問題。 –

+0

不應該在非UI線程上進行「一些處理」? – Fredrik

+0

DoSomeProcessingOnTheUIThread做什麼? – Fredrik

回答

4

立即調用Dispatcher.Invoke的任務是毫無意義的,因爲除了調度Dispatcher操作的小部分代碼之外,實際上沒有任何操作在後臺線程上運行。

更好地直接設置Text屬性,並使用XmlReader.ReadAsync

private async void btnQuickTest_Click(object sender, RoutedEventArgs e) 
{ 
    using (var reader = XmlReader.Create("someXmlFile.xml")) 
    { 
     int rowCount = 0; 

     while (await reader.ReadAsync()) 
     { 
      rowCount++; 

      await Task.Run(() => 
      { 
       DoSomeWork(reader); 
      }); 

      if (rowCount > 0 && rowCount % 100 == 0) 
      { 
       txtRowCount.Text = string.Format("{0}", rowCount); 
      } 
     } 
    } 
} 

您也可以考慮讓你的DoSomeWork異步和直接調用它是這樣的:

await DoSomeWork(reader); 
+0

我沒有看到.ReadAsync()會如何幫助我;我想繼續閱讀文件並處理其中的記錄,並定期在UI上顯示進度而不會阻止它(或使行數從0到完成)。但是等待處理部分的第二個想法幫助了我。所以我改變了它,所以沒有調度員是必需的 - 更新我的問題,但你應該得到答案。 – joedotnot

+0

ReadAsync在後臺線程中處理XML流,而Read在UI線程中運行(此處)。儘管這裏的性能差異可能是可以接受的,但如果您已經處於等待方法中,那麼調用Async方法是一個好主意。 – Clemens

0

我最後的答案。

private async void btnQuickTest_Click(object sender, RoutedEventArgs e) 
{ 

    await Task.Run(() => DoSomeWorkOnBgThread((cnt) => //HAHA! this syntax is so confusing 
     { 
      Application.Current.Dispatcher.Invoke(() => 
       { 
        txtRowCount.Text = cnt.ToString(); 
       } 
      ); //end-invoke 
     } 
    )); 

    MessageBox.Show("i am done!"); 
} 


private void DoSomeWorkOnBgThread(Action<int> callbackFn) 
{ 

    XmlReader reader; 
    int rowCount = 0; 
    using (reader = XmlReader.Create("someXmlFile.xml")) 
    { 
     while (reader.Read()) 
     { 
      rowCount++; 

      DoMyProcessingHere(); 

      //only update UI every 100 loops 
      if (rowCount % 100 == 0) 
      { 
       callbackFn(count); 
      } 

     } //end-while 
    }//end-using 
} 
+0

最好使用'IProgress '和'Progress '而不是'Dispatcher.Invoke'。 –