【问题标题】:Periodically update silverlight view with MVVM使用 MVVM 定期更新 silverlight 视图
【发布时间】:2011-05-06 10:24:02
【问题描述】:

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

实现这一点的最佳方法是什么?我能想到的选项是:

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

  2. 创建一个围绕 DispatcherTimer(例如 PeriodicCommandExecutor)的包装器,它是一个类似于 WindowsForms 中的 Timer 控件的控件或资源,具有我绑定到 ViewModel 中的 Refresh 命令的命令属性(将计时器详细信息放在查看)

我认为第二个选项是首选,因为它使 ViewModel 更易于测试,而 DispatcherTimer 是我不希望在我的 ViewModel 中适当地使用的 UI 实现细节。你同意吗?

如果是,您将如何创建这样的包装器。我开始做一个带有附加属性的 DependencyObject,但我不确定如何将 Interval 等属性值转发到内部 DispatcherTimer。当依赖属性发生更改并且 DispatcherTimer 不是 DependencyObject 时,Silverlight 似乎没有提供任何事件,因此我无法直接将数据绑定到它的属性。

谢谢!

【问题讨论】:

    标签: silverlight mvvm dependency-properties dispatcher dispatchertimer


    【解决方案1】:

    为什么要使用 DispatcherTimer?为什么不使用普通的System.Threading.Timer,它将在后台线程上触发其回调?

    如果您将 UI 进度更新放在不显眼的位置(即不在 UI 的中心,可能在底角或状态栏),那么在用户继续他们正在做的事情时让后台计时器开始运行。进度值可以填充到视图模型中,并使用绑定显示在 UI 上。这样您就不必占用 UI 线程来进行 Web 服务调用。

    【讨论】:

    • 这不是一个真正的问题。 Silverlight 中的服务调用始终在后台线程上运行,并且当操作实际完成并且我在 ViewModel 中有更新逻辑时,会有一个单独的 *Completed-event。我有你建议的数据绑定。所以在任何情况下视图都不会被阻塞。
    【解决方案2】:

    最后,我解决了我的困境,创建了一种行为,该行为会定期在 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>
    

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

    【讨论】:

      猜你喜欢
      • 2011-02-28
      • 2010-12-07
      • 1970-01-01
      • 1970-01-01
      • 2011-04-16
      • 1970-01-01
      • 2017-05-04
      • 1970-01-01
      • 2011-01-20
      相关资源
      最近更新 更多