我正在尝试在Silverlight中使用MVVM,但我对它很新,所以我对某些事情不太确定。我有一个silverlight页面,显示一些服务器端操作的进度。当前的进展来自一个Web服务,应该每隔几秒刷新一次(为了争论,可以说10秒)。
实现此目的的最佳方法是什么?我能想到的选择是:
在我的ViewModel的Initalize方法中初始化DispatcherTimer并从DispatcherTimer事件刷新视图(将计时器详细信息放在ViewModel中)
创建一个包装器arround DispatcherTimer(例如PeriodicCommandExecutor),它是一个Control或资源,类似于WindowsForms中的Timer控件,带有一个命令属性,我将其绑定到ViewModel中的Refresh命令(将计时器详细信息放入视图)
我认为第二个选项是首选,因为它使ViewModel更容易测试,DispatcherTimer是一个UI实现细节,我不希望在我的ViewModel中。你同意吗?
如果是,您将如何创建这样的包装器。我开始使用附加属性执行DependencyObject,但我不确定如何将Interval等属性值转发到内部DispatcherTimer。当依赖项属性更改且DispatcherTimer不是DependencyObject时,Silverlight似乎不提供任何事件,因此我无法直接对其属性进行数据绑定。
谢谢!
答案 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>
我希望它可以帮助有类似问题的人。