使用MVVM定期更新silverlight视图

时间:2011-05-06 10:24:02

标签: silverlight mvvm dependency-properties dispatcher dispatchertimer

我正在尝试在Silverlight中使用MVVM,但我对它很新,所以我对某些事情不太确定。我有一个silverlight页面,显示一些服务器端操作的进度。当前的进展来自一个Web服务,应该每隔几秒刷新一次(为了争论,可以说10秒)。

实现此目的的最佳方法是什么?我能想到的选择是:

  1. 在我的ViewModel的Initalize方法中初始化DispatcherTimer并从DispatcherTimer事件刷新视图(将计时器详细信息放在ViewModel中)

  2. 创建一个包装器arround DispatcherTimer(例如PeriodicCommandExecutor),它是一个Control或资源,类似于WindowsForms中的Timer控件,带有一个命令属性,我将其绑定到ViewModel中的Refresh命令(将计时器详细信息放入视图)

  3. 我认为第二个选项是首选,因为它使ViewModel更容易测试,DispatcherTimer是一个UI实现细节,我不希望在我的ViewModel中。你同意吗?

    如果是,您将如何创建这样的包装器。我开始使用附加属性执行DependencyObject,但我不确定如何将Interval等属性值转发到内部DispatcherTimer。当依赖项属性更改且DispatcherTimer不是DependencyObject时,Silverlight似乎不提供任何事件,因此我无法直接对其属性进行数据绑定。

    谢谢!

2 个答案:

答案 0 :(得分:0)

为什么要使用DispatcherTimer?为什么不使用普通的System.Threading.Timer,它会在后台线程上激活它的回调?

如果您将UI进度更新放在某处不可靠(即不在UI的中心,可能在底部角落或状态栏中),那么当用户继续他们正在做的事情时,让后台计时器匆匆离去。可以将进度值填充到viewmodel中,并使用绑定在UI上显示。这样您就不必占用Web线程进行Web服务调用。

答案 1 :(得分: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>

我希望它可以帮助有类似问题的人。