2009-06-04 6 views
6

Bazı olay meydana beri kaç saniye geçti nasıl kullanıcıyı göstermek istiyorum. Ben SecondsSinceOccurrence bir TextBlock.Text özelliğini bağlarsanız,Her saniye WPF bağlama güncelleştirmesi nasıl yapılır?

public DateTime OccurredAtUtc { get; set; } 

public int SecondsSinceOccurrence 
{ 
    get { return (int)(DateTime.UtcNow - OccurredAtUtc).TotalSeconds; } 
} 

değeri görünür ama statikse: Kavramsal olarak, benim bakış modeli böyle özelliklere sahiptir. Zamanın geçişi, bu olayın artan yaşını yansıtmamaktadır.

<!-- static value won't update as time passes --> 
<TextBlock Text="{Binding SecondsSinceOccurrence}" /> 

Ben PropertyChanged her saniye ateşler benim bakış modelinde bir zamanlayıcı oluşturmak, ancak kullanıcı arayüzünde birçok tür elementler (bir ItemsControl öğeler için onun bir şablon) olması muhtemeldir vardır ve ben istemiyorum olabilir o çok zamanlayıcılar oluşturmak için. storyboard'una animasyon

bilgilerim büyük değil. WPF animasyon çerçevesi bu durumda yardımcı olabilir mi?

cevap

4

gitmek için bir yoldur. Ama eğer bir ContentControl öğelerin çok var ve güncelleştirmek istediğiniz özellik o gereksiz yere 100+ zamanlayıcılar oluşturma ve bunları aynı anda PropertyChanged tüm yükseltmek sahip demektir, yani ContentControl arasında ItemTemplate ise. ListBox gibi bir ItemsControl kullanıldığında Ancak bu davranış hala her kalem için oluşturulacaktır. Bu nedenle

Ben sadece Şablonu'ndaki bağlayıcı bir kez her için oluşturulacak bu Davranışı yarattı. Aynı zamanda tamamen MVVM.

Kullanımı

<Label xmlns:b="clr-namespace:Lloyd.Shared.Behaviors" 
     xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity" 
     Content="{Binding MyContent}" Width="80" Foreground="{Binding MyColor}"> 
    <i:Interaction.Behaviors> 
     <b:PeriodicBindingUpdateBehavior Interval="0:00:01" Property="{x:Static ContentControl.ContentProperty}" Mode="UpdateTarget" /> 
     <b:PeriodicBindingUpdateBehavior Interval="0:00:01" Property="{x:Static Control.ForegroundProperty}" Mode="UpdateTarget" /> 
    </i:Interaction.Behaviors> 
</Label> 

bağımlılıklar http://schemas.microsoft.com/expression/2010/interactivity ad System.Windows.Interactivity.WPF adında bir Nuget paketi altında kullanılabilir Not olduğunu

. Projeyi karışım halinde açarsanız, otomatik olarak eklenecektir.

Kopya ve oldukça temel veri kaynağı bildiren olmasındansa periyodik olarak bu çekeceği Ben bir öğe olması mümkün olurdu umuduyla (veya bağlanma) oldu

using System; 
using System.Threading; 
using System.Threading.Tasks; 
using System.Windows; 
using System.Windows.Data; 
using System.Windows.Interactivity; 

namespace Lloyd.Shared.Behaviors 
{ 
    public class PeriodicBindingUpdateBehavior : Behavior<DependencyObject> 
    { 
     public TimeSpan Interval { get; set; } 
     public DependencyProperty Property { get; set; } 
     public PeriodicBindingUpdateMode Mode { get; set; } = PeriodicBindingUpdateMode.UpdateTarget; 
     private WeakTimer timer; 
     private TimerCallback timerCallback; 
     protected override void OnAttached() 
     { 
      if (Interval == null) throw new ArgumentNullException(nameof(Interval)); 
      if (Property == null) throw new ArgumentNullException(nameof(Property)); 
      //Save a reference to the callback of the timer so this object will keep the timer alive but not vice versa. 
      timerCallback = s => 
      { 
       try 
       { 
        switch (Mode) 
        { 
         case PeriodicBindingUpdateMode.UpdateTarget: 
          Dispatcher.Invoke(() => BindingOperations.GetBindingExpression(AssociatedObject, Property)?.UpdateTarget()); 
          break; 
         case PeriodicBindingUpdateMode.UpdateSource: 
          Dispatcher.Invoke(() => BindingOperations.GetBindingExpression(AssociatedObject, Property)?.UpdateSource()); 
          break; 
        } 
       } 
       catch (TaskCanceledException) { }//This exception will be thrown when application is shutting down. 
      }; 
      timer = new WeakTimer(timerCallback, null, Interval, Interval); 

      base.OnAttached(); 
     } 

     protected override void OnDetaching() 
     { 
      timer.Dispose(); 
      timerCallback = null; 
      base.OnDetaching(); 
     } 
    } 

    public enum PeriodicBindingUpdateMode 
    { 
     UpdateTarget, UpdateSource 
    } 

    /// <summary> 
    /// Wraps up a <see cref="System.Threading.Timer"/> with only a <see cref="WeakReference"/> to the callback so that the timer does not prevent GC from collecting the object that uses this timer. 
    /// Your object must hold a reference to the callback passed into this timer. 
    /// </summary> 
    public class WeakTimer : IDisposable 
    { 
     private Timer timer; 
     private WeakReference<TimerCallback> weakCallback; 
     public WeakTimer(TimerCallback callback) 
     { 
      timer = new Timer(OnTimerCallback); 
      weakCallback = new WeakReference<TimerCallback>(callback); 
     } 

     public WeakTimer(TimerCallback callback, object state, int dueTime, int period) 
     { 
      timer = new Timer(OnTimerCallback, state, dueTime, period); 
      weakCallback = new WeakReference<TimerCallback>(callback); 
     } 

     public WeakTimer(TimerCallback callback, object state, TimeSpan dueTime, TimeSpan period) 
     { 
      timer = new Timer(OnTimerCallback, state, dueTime, period); 
      weakCallback = new WeakReference<TimerCallback>(callback); 
     } 

     public WeakTimer(TimerCallback callback, object state, uint dueTime, uint period) 
     { 
      timer = new Timer(OnTimerCallback, state, dueTime, period); 
      weakCallback = new WeakReference<TimerCallback>(callback); 
     } 

     public WeakTimer(TimerCallback callback, object state, long dueTime, long period) 
     { 
      timer = new Timer(OnTimerCallback, state, dueTime, period); 
      weakCallback = new WeakReference<TimerCallback>(callback); 
     } 

     private void OnTimerCallback(object state) 
     { 
      if (weakCallback.TryGetTarget(out TimerCallback callback)) 
       callback(state); 
      else 
       timer.Dispose(); 
     } 

     public bool Change(int dueTime, int period) 
     { 
      return timer.Change(dueTime, period); 
     } 
     public bool Change(TimeSpan dueTime, TimeSpan period) 
     { 
      return timer.Change(dueTime, period); 
     } 

     public bool Change(uint dueTime, uint period) 
     { 
      return timer.Change(dueTime, period); 
     } 

     public bool Change(long dueTime, long period) 
     { 
      return timer.Change(dueTime, period); 
     } 

     public bool Dispose(WaitHandle notifyObject) 
     { 
      return timer.Dispose(notifyObject); 
     } 
     public void Dispose() 
     { 
      timer.Dispose(); 
     } 
    } 
} 
+0

Bu harika görünüyor. Teşekkürler. –

+0

Teşekkürler bir demet! Bunu çözmek için temiz bir yol arıyordum. Eski bir C# sürümünü ya da ne kullanıyorsam biliyorum ama 'TimerCallback geri arama için'() weakCallback.TryGetTarget (TimerCallback dışarı geri arama) 'eğer değiştirmek zorunda etmeyin; eğer (zayıfBağlantı.AramaGetTarget (geri arama)) çalışmasını sağlarsa. – monoceres

+0

@monoceres Oh evet! Bu C# 7. Bunu denedikten sonra onsuz yaşayamazsınız – fjch1997

8

Görünüm modeliniz için statik olarak tek bir DispatcherTimer oluşturabilir ve ardından bu görünüm modelinin tüm örneklerini Tick olayını dinleyebilirsiniz. periyodik PropertyChanged olayı tetiklemek için bir zamanlayıcı olması

public class YourViewModel 
{ 
    private static readonly DispatcherTimer _timer; 

    static YourViewModel() 
    { 
     //create and configure timer here to tick every second 
    } 

    public YourViewModel() 
    { 
     _timer.Tick += (s, e) => OnPropertyChanged("SecondsSinceOccurence"); 
    } 
} 
+1

macun kodu. Özel bir ciltleme oluşturabilir ve bir 'RefreshPeriod' özelliği ekleyebilir mi? Öyleyse, DispatcherTimer örnekleri de havuzlanmış olabilir. –

+0

Gerçekten de, sadece XAML'den yapmakla ilgileniyorum. Ayrıca animasyon atm hakkında yeterli bilgiye sahip değilim. – buckley