1

我想在Silverlight中使用MVVM,但我對它很陌生,所以我對某些事情不太確定。我有一個silverlight頁面,顯示一些服務器端操作的進度。當前的進度來自Web服務,應該每隔幾秒刷新一次(爲了爭論起見說10秒)。定期用MVVM更新silverlight視圖

實現此目的的最佳方法是什麼?我能想到的選項是:

  1. Initalize一個DispatcherTimer在我的視圖模型的Initalize方法並刷新從DispatcherTimer事件視圖(把定時器細節視圖模型)

  2. 創建包裝角落找尋DispatcherTimer(例如PeriodicCommandExecutor),這將是一個控制或資源類似於結合命令屬性WindowsForms我結合於刷新命令,在視圖模型(把定時器的詳細內容在View)的定時器控制

我認爲第二個選項是首選,因爲它使ViewModel更易於測試,而DispatcherTimer是我不想在ViewModel中提供的UI實現細節。你同意嗎?

如果是的話,你將如何創建這樣一個包裝。我開始做一個帶有附加屬性的DependencyObject,但我不確定如何將諸如Interval之類的屬性值轉發到內部DispatcherTimer。當依賴項屬性發生變化並且DispatcherTimer不是DependencyObject時,Silverlight似乎不會提供任何事件,因此我無法直接將其數據綁定到其屬性。

謝謝!

回答

0

最後我解決了我的dillema創建一個行爲,它可以定期執行ViewModel上的刷新命令,您可以指定它。

對動作的代碼是這樣的 (對不起,VB代碼):

Option Strict On 

Imports System.Windows.Threading 
Imports System.Windows.Interactivity 

Namespace View.Behaviors 

    Public Class RefreshBehavior 
     Inherits Behavior(Of FrameworkElement) 



     Public Property Command As ICommand 
      Get 
       Return DirectCast(GetValue(CommandProperty), ICommand) 
      End Get 

      Set(ByVal value As ICommand) 
       SetValue(CommandProperty, value) 
      End Set 
     End Property 

     Public Shared ReadOnly CommandProperty As DependencyProperty = _ 
            DependencyProperty.Register("Command", _ 
                   GetType(ICommand), GetType(RefreshBehavior), _ 
                   New PropertyMetadata(Nothing)) 


     Public Property CommandParameter As Object 
      Get 
       Return GetValue(CommandParameterProperty) 
      End Get 

      Set(ByVal value As Object) 
       SetValue(CommandParameterProperty, value) 
      End Set 
     End Property 

     Public Shared ReadOnly CommandParameterProperty As DependencyProperty = _ 
            DependencyProperty.Register("CommandParameter", _ 
                   GetType(Object), GetType(RefreshBehavior), _ 
                   New PropertyMetadata(Nothing)) 




     Public Property Interval As TimeSpan 
      Get 
       Return DirectCast(GetValue(IntervalProperty), TimeSpan) 
      End Get 

      Set(ByVal value As TimeSpan) 
       SetValue(IntervalProperty, value) 
      End Set 
     End Property 

     Public Shared ReadOnly IntervalProperty As DependencyProperty = _ 
            DependencyProperty.Register("Interval", _ 
                   GetType(TimeSpan), GetType(RefreshBehavior), _ 
                   New PropertyMetadata(TimeSpan.Zero, AddressOf OnIntervalUpdate)) 



     Public Property Enabled As Boolean 
      Get 
       Return DirectCast(GetValue(EnabledProperty), Boolean) 
      End Get 

      Set(ByVal value As Boolean) 
       SetValue(EnabledProperty, value) 
      End Set 
     End Property 

     Public Shared ReadOnly EnabledProperty As DependencyProperty = _ 
           DependencyProperty.Register("Enabled", _ 
           GetType(Boolean), GetType(RefreshBehavior), _ 
           New PropertyMetadata(False, AddressOf OnEnabledUpdate)) 




     Dim WithEvents timer As New DispatcherTimer() 

     Private Shared Sub OnEnabledUpdate(ByVal d As DependencyObject, ByVal e As DependencyPropertyChangedEventArgs) 
      Dim enable As Boolean = CType(e.NewValue, Boolean) 
      Dim executor As RefreshBehavior = CType(d, RefreshBehavior) 
      If Not executor.attached Then Return 

      Dim timer As DispatcherTimer = executor.timer 

      If enable AndAlso Not timer.IsEnabled Then 
       timer.Start() 
      ElseIf Not enable AndAlso Not timer.IsEnabled Then 
       timer.Stop() 
      End If 
     End Sub 

     Private Shared Sub OnIntervalUpdate(ByVal d As DependencyObject, ByVal e As DependencyPropertyChangedEventArgs) 
      Dim executor As RefreshBehavior = CType(d, RefreshBehavior) 

      Dim timer As DispatcherTimer = executor.timer 
      timer.Interval = CType(e.NewValue, TimeSpan) 
     End Sub 

     Private WithEvents attachedObject As FrameworkElement 

     Private Sub OnUnload(ByVal sender As Object, ByVal e As EventArgs) Handles attachedObject.Unloaded 
      timer.Stop() 
     End Sub 

     Private attached As Boolean = False 
     Protected Overrides Sub OnAttached() 
      attached = True 
      attachedObject = AssociatedObject 

      If Enabled Then timer.Start() 
      MyBase.OnAttached() 
     End Sub 

     Protected Overrides Sub OnDetaching() 
      timer.Stop() 
      attached = False 
      attachedObject = Nothing 
      MyBase.OnDetaching() 
     End Sub 

     Private Sub OnTick(ByVal sender As Object, ByVal e As EventArgs) Handles Timer.Tick 
      Dim cmd = Command 
      Dim parameter = CommandParameter 
      If Interval < TimeSpan.MaxValue AndAlso cmd IsNot Nothing AndAlso cmd.CanExecute(parameter) Then 
       cmd.Execute(parameter) 
      End If 
     End Sub 
    End Class 
End Namespace 

您可以使用它像這樣:

<i:Interaction.Behaviors> 
    <Behaviors:RefreshBehavior Enabled="True" Interval="0:0:10" Command="{Binding RefreshPageCommand}" /> 
</i:Interaction.Behaviors> 

我希望它可以幫助別人類似的問題。

0

爲什麼要使用DispatcherTimer?爲什麼不使用普通的System.Threading.Timer,它會在後臺線程上觸發它的回調?

如果你把你的UI進度更新到某處不可信(即不在UI的中心,也許在底角或狀態欄中),那麼當用戶繼續做他們正在做的事時,讓背景計時器突然消失。進度值可以填充到視圖模型中,並使用綁定在UI上顯示。這樣您就不必綁定調用Web服務調用的UI線程。

+0

這不是一個真正的問題。 Silverlight中的服務調用始終在後臺線程上運行,並且在操作實際完成時以及ViewModel中具有更新邏輯的位置有單獨的* Completed-event。我有你建議的數據綁定。所以無論如何View不會被阻止。 – aKzenT 2011-05-06 10:58:11