2012-11-14 49 views
2

我有一個我寫的幫助器類,可用於在我的GUI上運行長時間運行的任務。它所做的是使用樣式來顯示「工作」動畫,並在任務運行時淡出內容,用戶可以看到正在進行的操作。WPF正在進行的動畫不會顯示數據綁定更新

我的問題是,當長時間運行的任務完成時,它會淡入內容並隱藏工作動畫 - 這是它應該做的事情,但是因爲我使用MVVM並主要是爲我的所有內容顯示數據綁定, GUI組件的更新單獨發生在長時間運行的任務中。即數據綁定OnPropertyChanged(「」)事件觸發,然後在長時間運行的任務完成之後由GUI線程拾取這些事件。但問題是當長時間運行的任務完成時,Worker Animation會關閉,但在數據綁定更新之前。

因此,最終結果是您可以在任務運行時按預期顯示工作動畫,但對於所有樹數據的大數據集,數據綁定更新需要4-5秒甚至更長時間,在此期間,該應用程序不處於「工作動畫模式」,只是凍結。

有沒有一種方法,我可以有我的工人動畫繼續,不僅運行方法從長遠來看,但對於相關的數據從OnPropertyChanged綁定更新呢?

+0

你嘗試調用NotifyPropertyChanged方法,當你長時間運行的方法完成的工作? – Legoless

+0

長時間運行的方法會經歷並更新很多ViewModel對象,很多屬性。在我調用OnPropertyChanged()的每個屬性的Setter中,這些都是在長時間運行的方法完成之前調用的,因爲它們是其中的一部分。這是你的意思嗎?我不確定INotifyPropertyChanged的內部工作原理,但是基於觀察到的行爲,我猜測INotifyPropertyChanged將調用發送到GUI調度程序線程,該線程隨後與所有後續的線程一起排隊,直到長時間運行的工作完成並且GUI線程釋放? – NZJames

+0

你正在執行長時間運行的任務嗎?在一個單獨的線程,後臺工作人員等?你可以發佈一些代碼嗎? –

回答

0

考慮使用BusyIndicator從擴展WPF工具。它應該提供你描述的功能。它具有IsBusy屬性,您可以將其綁定到ViewModel中的屬性,並在完成所有工作後將其設置爲False。 您可以隨時像使用其他控件一樣更改BusyIndi​​cator的樣式。在我的解決方案,我一直用這個控制與BackgroundWorker的類一起從System.ComponentModel我通常設置IsBusy =假年底RunWorkerCompleted

private void LongRunningMethod() 
    { 
     this.IsBusy = true; 
     var worker = new BackgroundWorker(); 
     worker.DoWork += this.LongMethodDoWork; 
     worker.RunWorkerCompleted += this.RunWorkerCompleted; 
     worker.RunWorkerAsync(); 
    } 

    private void LongMethodDoWork(object sender, DoWorkEventArgs doWorkEventArgs) 
    { 
     ... 
    } 

    private void RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs runWorkerCompletedEventArgs) 
    { 
     ... 
     this.IsBusy = false; 
    } 
+0

這仍然不能解決他的問題,因爲他希望只有在綁定完成後才能完成動畫,而不是在完成長時間任務後才能完成 – Blachshma

+0

這樣,我很抱歉錯誤的回答......我會等待某個人回答並解釋如何找出綁定已「完成」。而我從作者的評論中發現他的GUI線程被凍結了?這不是BackgroundWorker的問題。我又錯了嗎? –

0

感謝所有的答案。我實際上遇到了一個可能有點爭議的解決方案,因爲有些人會認爲這有點破解,但它確實是我想要的,而且似乎沒有其他辦法可以做到這一點,所以對我來說這是一個代碼解決方案,而不是黑客。我使用WPFBackgroundProgressIndicator開源項目,我從代碼項目(我認爲)下載,它可以選擇顯示主內容中的忙指示符有或沒有淡出,或作爲彈出窗口,它作爲後臺線程這是理想的,爲什麼我選擇它。

問題是,當您運行長時間運行的方法時,代碼執行會同步完成,但所有綁定OnPropertyChanged(「」)更新都會異步運行並在Dispatcher線程上排隊,因此您的工作方法在WPF控件之前完成有機會調用依賴屬性的Getters來檢索新值。你需要做的是有效地「阻止」,直到所有Dispatcher事件完成,這就是爲什麼不是每個人都會喜歡這個解決方案,因爲它「阻止」,但那正是我想要做的。我想阻止應用程序,直到完整更新完成爲止,因爲我不希望用戶能夠在數據仍在渲染時進行任何可視化操作,所以這是我的要求。清理阻塞比混亂的交互更可取。

因此,解決辦法,無論你相信與否,只是工作方法調用之後的一行代碼。它如下。

Application.Current.Dispatcher.Invoke(new Action(() => { }), DispatcherPriority.ContextIdle, null); 

正如你可以有效地看到排隊調度線程,直到它完成塊當前的代碼執行上一個新的任務,但是當你給它的優先級最低,這個調用會等到所有其他的調度執行完畢,即所有渲染完成。渲染完成後,該行將被執行,您將退出所有渲染完成。下面是我在上下文中使用的完整方法。我歡迎您對這種方法進行思考和討論。

public void LongRunningTaskWithFade(BusyDecorator busy, Action longTask) 
     { 
      if (loading) return; 
      loading = true; 

      busy.FadeTime = TimeSpan.Zero; 
      busy.IsBusyIndicatorShowing = true; 

      // in order for setting the opacity to take effect, you have to delay the task slightly to ensure WPF has time to process the updated visual 
      Application.Current.Dispatcher.BeginInvoke(new Action(() => 
      { 
       try 
       { 
        longTask(); 
        Application.Current.Dispatcher.Invoke(new Action(() => { }), DispatcherPriority.ContextIdle, null); 
       } 
       finally 
       { 
        HideBusyDisplay(busy);      
       } 
      }), DispatcherPriority.Background); 
     }