2016-07-28 48 views
-1

剛剛創建了一個WPF項目.NET 4.6如何更新GUI連續異步

而且已經把這段代碼裏面

lbl1是在GUI

的標籤,但標籤永遠不會更新或while循環繼續只有1次

 private void Button_Click_1(object sender, RoutedEventArgs e) 
    { 
     var t = Task.Run(
     async() => 
     { 
      await AsyncLoop(); 
     }); 

    } 

    async Task AsyncLoop() 
    { 
     while (true) 
     { 
      string result = await LoadNextItem(); 
      lbl1.Content = result; 
     } 
    } 

    private static int ir11 = 0; 
    async Task<string> LoadNextItem() 
    { 
     ir11++; 
     return "aa " + ir11; 
    } 
+2

可能的[如何從C#中的另一個線程更新GUI]重複(http://stackoverflow.com/questions/661561/how-to-update-the-gui-from-another-thread-in- C);具體請參見[本答案](http://stackoverflow.com/a/18033198/3283203)。 – Kilazur

回答

0

請用戶Application.Current.Dispatcher.Invoke訪問UI線程

async Task AsyncLoop() 
    { 
     while (true) 
     { 
      string result = await LoadNextItem(); 
Application.Current.Dispatcher.Invoke(new Action(() => { lbl1.Content = result; })); 

     } 
    } 
+0

我明白了。所以它實際上是拋出一個無聲的錯誤,從而停止。任何方式快速鍵入這個或複製粘貼? Application.Current.Dispatcher.Invoke(new Action(()=> { – MonsterMMORPG

+0

是的,你可以從wpf中直接從另一個線程訪問ui線程, – suulisin

+0

是否等到ui更新?或者只是調度程序立即調用並返回? – MonsterMMORPG

1

C#編譯器給你一個警告,告訴你問題是什麼。

具體而言,這種方法是不是異步:

async Task<string> LoadNextItem() 
{ 
    ir11++; 
    return "aa " + ir11; 
} 

編譯器消息會通知您此async方法沒有await語句,從而將運行同步。您應該只使用async在有意義,通常是基於I/O操作,如:

async Task<string> LoadNextItemAsync() 
{ 
    await Task.Delay(100); // Placeholder for actual asynchronous work. 
    ir11++; 
    return "aa " + ir11; 
} 

另外,如果你有異步操作,而是你有一些緊張的CPU限制循環,那麼你可以通過Task.Run推那些開了一個後臺線程:

string LoadNextItem() 
{ 
    ir11++; 
    return "aa " + ir11; 
} 

while (true) 
{ 
    string result = await Task.Run(() => LoadNextItem()); 
    lbl1.Content = result; 
} 
2

通過調用Task.Run你打破了GUI線程(或WPF調度程序)的關聯(的SynchronizationContext),並失去了大部分的異步電動機/等待'善良'。

爲什麼不使用異步無效事件處理程序,並且只是回到每個步驟的SynchronizationContext(GUI Thread/Dispatcher)?

private async void Button_Click_1(object sender, RoutedEventArgs e) 
{ 
    while (true) 
    { 
     string result = await LoadNextItem(); 
     lbl1.Content = result; 
    } 
} 

private static int ir11 = 0; 
Task<string> LoadNextItem() 
{ 
    await Task.Delay(1000); // placeholder for actual async work 
    ir11++; 
    return "aa " + ir11; 
} 

或者,如果你真的想要的狀態機的「正在進行」操作分開,嘗試傳遞一個IProgress<T>(默認implement執行。Progress<T>或專門Progress<string>應該工作在這種情況下大)。見this article by @Stephen Cleary

他的例子非常接近您在問題中陳述的內容。我在這裏複製它爲了獨立。

public async void StartProcessingButton_Click(object sender, EventArgs e) 
{ 
    // The Progress<T> constructor captures our UI context, 
    // so the lambda will be run on the UI thread. 
    var progress = new Progress<int>(percent => 
    { 
    textBox1.Text = percent + "%"; 
    }); 

    // DoProcessing is run on the thread pool. 
    await Task.Run(() => DoProcessing(progress)); 
    textBox1.Text = "Done!"; 
} 

public void DoProcessing(IProgress<int> progress) 
{ 
    for (int i = 0; i != 100; ++i) 
    { 
    Thread.Sleep(100); // CPU-bound work 
    if (progress != null) 
     progress.Report(i); 
    } 
} 

編輯:我必須承認,當Progress<T>是一個很好的抽象,在這種情況下,它只是要掉下來Dispatcher.Invoke爲@Pamparanpa建議。