2017-03-16 57 views
4

在我的項目中,我想顯示一個進度指示器,從web服務中檢索數據並隱藏進度指示器。如果我這樣做,我現在不得不等待,直到從Web服務檢索到數據,然後用戶界面的結果立即更新。但進度指標從未出現。所以看來,我在UI線程上工作或阻塞。但是哪裏?直到異步方法完成後UI纔會更新

這是我的一個簡單的代碼版本:

SomePage的

private async void OnItemSelected(object sender, SelectedItemChangedEventArgs e) 
{ 
    await ShowDetailView(); 
} 

private async Task ShowDetailView() 
{ 
    var view = new MyView(); 
    this.detailView = view; 
    await view.LoadContent(); 
} 

SomeView

public async Task LoadContent() 
{ 
    var success = await LoadData(); 
    if (success) 
    { 
     ShowInformation(); 
    } 
} 

public async Task<bool> LoadData() 
{ 
    try 
    { 
     ShowLoadingProcess(); 
     await Task.Delay(5000); 
     this.itemList = await WebService.Instance.GetData(); 

     return true; 
    } 
    catch (Exception ex) 
    { 
     System.Diagnostics.Debug.WriteLine(ex.Message); 
     return false; 
    } 
    finally 
    { 
     HideLoadingProcess(); 
    } 
} 

private void ShowInformation() 
{ 
    Device.BeginInvokeOnMainThread(() => 
    { 
     this.grid.Clear(); 

     foreach(var item in this.itemList) 
     { 
      GridItem contentItem = new GridItem(item); 
      this.grid.Children.Add(contentItem); 
     } 
    } 
} 

private void ShowLoadingProcess() 
{ 
    Device.BeginInvokeOnMainThread(() => BringProgressIndicatorToFront()); 
} 

private void HideLoadingProcess() 
{ 
    Device.BeginInvokeOnMainThread(() => BringProgressIndicatorToBack()); 
} 

我嘗試不同的東西,在這裏我之間有不同步UI和後臺線程。例如。在LoadData()完成之前調用ShowInformation()

有人可以給我提示這裏有什麼問題嗎?

+0

您正在查找[BackgroundWorker](https://developer.xamarin.com/api/type/System.ComponentModel.BackgroundWorker/)。這使您可以運行異步任務並從中報告進度。 –

+3

@LukeSamuel如果您正確使用TPL,則不需要BGW。 – Servy

回答

0

非常愚蠢的錯誤。我有這樣的事情:

private async Task ShowDetailView() 
{ 
    var view = new MyView(); 
    this.detailView = view; 
    await view.LoadContent(); 
    this.mainGrid.Children.Add(this.detailView); 
} 

當然,他等待,直到操作完成,然後顯示結果。這是正確的方法:

private async Task ShowDetailView() 
{ 
    var view = new MyView(); 
    this.detailView = view; 
    this.mainGrid.Children.Add(this.detailView); 
    await view.LoadContent(); 
} 

我應該刪除我的問題嗎?也許,因爲有時你錯過了樹林。

1

我不完全確定你的代碼爲什麼會這樣做,但你應該考慮到Device.BeginInvokeOnMainThread()沒有給出關於何時執行動作的任何承諾。它在內部執行以下操作:

// Simplified 
s_handler = new Handler(Looper.MainLooper); 
s_handler.Post(action); 

它將您的操作傳遞給消息隊列,然後在未來的未知時間點執行操作。

但是,我建議你的是重新考慮你的應用程序的體系結構。通過利用MVVM模式和數據綁定,您將能夠非常輕鬆地控制進度指示器的可見性。

在視圖模型,你將有以下:

private bool isBusy; 

public bool IsBusy() { 
    get { return isBusy; } 
    set { SetProperty(ref isBusy, value); } 
} 

在具有上述視圖模型作爲其BindingContext中查看,你會像下面這樣:

<ActivityIndicator IsRunning="{Binding IsBusy}" IsVisible="{Binding IsBusy}" /> 

現在,每當您將開始一個長時間運行的操作,您只需將isBusy更改爲true,並且由於綁定,View將自動顯示正在運行的ActivityIndi​​cator。

+0

我檢查了[我的代碼是否在UI線程上運行](https://forums.xamarin.com/discussion/comment/200008/#Comment_200008),它的確如此。所以我學習了異步/等待不會生成新線程。不完全明白爲什麼有時候在等待方法結束之前調用了非等待方法...但是我發現我的問題,並且您使用MVVM的提示很有幫助。謝謝。 – testing

+0

@testing很高興聽到您發現問題。我認爲線程和他們的祕密生活是軟件開發中最大的謎團。 ;) – hankide

0

我不熟悉xamarin.forms,所以下面可能是完全垃圾。如果是這樣,請告訴我,我會刪除答案。

對我來說,似乎你有一些事件處理程序OnItemSelected應該顯示一個DetailView,這是一些其他形式的窗體。創建DetailView對象後,您要加載詳細視圖。此加載需要一些時間,同時您希望通知操作員表單正在加載。

我不確定您想要視覺反饋的位置:SomePage上或SomeView上。答案並不重要,除了顯示和隱藏視覺反饋的命令最好寫在顯示反饋的類中。

顯示上SomeView

反饋在這種情況下SomeView有事顯示和隱藏視覺反饋,就像一個進度條,或AJAX裝載機。代碼將如下所示:

由於我熟悉Forms,我會將它作爲Form來編寫。您的代碼將是非常相似

class SomeView : Form 
{ 
    void ShowVisualFeedBack() 
    { 
     ... 
    } 
    void HideVisualFeedBack() 
    { 
     ... 
    } 

    public async Task LoadDataAsync() 
    { 
     bool result = false; 
     this.ShowVisualFeedBack() 
     try 
     { 
      this.itemList = await WebService.Instance.GetData(); 
      result = true; 
     } 
     catch (Exception ex) 
     { 
      System.Diagnostics.Debug.WriteLine(ex.Message); 
     } 
     this.HideVisualFeedBack(); 
     return result; 
    } 
} 

或者,如果你真的需要等待5秒鐘,然後開始獲取數據之前:

try 
{ 
     await Task.Delay(TimeSpan.FromSeconds(5)); 
     this.itemList = await WebService.Instance.GetData(); 
     result = true; 
    } 

顯示上SomePage的

class SomePage : Form 
{ 
    void ShowVisualFeedBack() 
    { 
     ... 
    } 
    void HideVisualFeedBack() 
    { 
     ... 
    } 

    private async Task ShowDetailView() 
    { 
     this.ShowVisualFeedBack(); 
     this.DetailView = new MyView(); 
     await view.LoadContent(); 
     this.HideVisualFeedBack(); 
    } 
} 

反饋順便說一句,當你的線程正在等待GetData()時,它不能更新你的視覺反饋。像GIF這樣的一些視覺反饋方法不需要你的線程進行更新,但是如果你有類似進度條的東西,你的線程需要更新它。您可以通過等待最大時間來完成此操作,直到GetData完成,更新進度並再次等待。

var taskWaitASec = Task.Delay(TimeSpan.FromSeconds(1)); 
var taskGetData = WebService.Instance.GetData(); 

// note: you are not awaiting yet, so you program continues: 

while (!taskGetData.IsCompleted) 
{ 
    var myTasks = new Task[] {taskWaitASec, taskGetData} 
    var completedTask = await Task.WhenAny(myTasks); 
    if (completedTask == taskWaitASec) 
    { 
     UpdateProgress(); 
     taskWaitASec = Task.Delay(TimeSpan.FromSeconds(1)); 
    } 
} 
+0

我顯示一個微調,所以我不需要更新之間的用戶界面。目前,我在* SomePage *(main)和* SomeView *上有多個spinners,用於從webservice單獨加載的元素。我試圖在你的例子之後重構我的代碼,但行爲保持不變。現在我發現了這個問題。儘管如此,感謝您的幫助。 – testing

相關問題