1

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

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

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

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

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

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

谢谢!

4

2 回答 2

0

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

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

于 2011-05-06T10:54:55.647 回答
0

最后,我解决了我的困境,创建了一种行为,该行为会定期在 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-06-27T23:09:04.500 回答