Периодически обновлять представление Silverlight с MVVM - PullRequest
1 голос
/ 06 мая 2011

Я пытаюсь использовать MVVM в Silverlight, но я новичок в этом, поэтому я не совсем уверен в некоторых вещах.У меня есть страница Silverlight, которая отображает ход выполнения некоторых операций на стороне сервера.Текущий прогресс исходит от веб-службы и должен обновляться каждые несколько секунд (скажем, 10 секунд ради аргумента).

Каков наилучший способ реализовать это?Я мог придумать следующие варианты:

  1. Инициализировать DispatcherTimer в методе Initalize моей ViewModel и обновить представление из события DispatcherTimer (поместив сведения о таймере в ViewModel)

  2. Создайте оболочку для DispatcherTimer (например, PeriodicCommandExecutor), которая будет элементом управления или ресурсом, аналогичным элементу управления Timer в WindowsForms, со свойством команды, которое я связываю с командой «Обновить» во ViewModel (помещая таймерподробности в представлении)

Я думаю, что второй вариант предпочтительнее, потому что он облегчает тестирование ViewModel, а DispatcherTimer - это деталь реализации пользовательского интерфейса, которую я не хочу в моей ViewModel, вероятно,,Вы согласны?

Если да, как бы вы создали такую ​​обертку.Я начал делать DependencyObject с прикрепленными свойствами, но я не уверен, как перенаправить значения свойств, такие как Interval, во внутренний DispatcherTimer.Похоже, что Silverlight не предоставляет никаких событий, когда изменяются свойства зависимости, а DispatcherTimer не является объектом DependencyObject, поэтому я не могу напрямую связать его свойства с данными.

Спасибо!

Ответы [ 2 ]

0 голосов
/ 28 июня 2011

В конце я решил мою дилемму, создав поведение, которое периодически выполняет команду обновления в 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 голосов
/ 06 мая 2011

Зачем использовать DispatcherTimer? Почему бы не использовать обычный System.Threading.Timer , который вызовет свой обратный вызов в фоновом потоке?

Если вы поместили свое обновление прогресса пользовательского интерфейса где-то незаметно (то есть не в центре пользовательского интерфейса, может быть, в нижнем углу или в строке состояния), тогда заставьте таймер фонового таймера отключаться, пока пользователь продолжает выполнять свои действия. Значение хода выполнения можно ввести в модель представления и отобразить в пользовательском интерфейсе с помощью привязки. Таким образом, вам не нужно связывать поток пользовательского интерфейса, выполняющий вызовы веб-службы.

...