using System; using System.ComponentModel; using System.Threading; using System.Threading.Tasks; using System.Windows; using System.Windows.Data; using System.Windows.Interactivity; using Chernobyl.Timing; using Chernobyl.Utility; namespace Chernobyl.App.Core { /// /// A that periodically triggers an update to the target or source /// of a binding. This is useful if you want a displayed property value or the property itself to /// be periodically refreshed/updated but the associated object or view doesn't trigger the /// updated as often as you would like (i.e. event for example). /// /// /// /// ]]> public class BindingUpdater : Behavior { // This code is a modified version of the code found here: https://stackoverflow.com/a/44253691. /// /// The time between updates of the binding. This property is one second by default. /// public TimeSpan Interval { get; set; } = TimeSpan.FromSeconds(1); /// /// The property that is bound to. /// public DependencyProperty Property { get; set; } /// /// Determines whether the property should be updated with the value on screen or the value /// on screen should be updated with the property. /// public BindingUpdateMode Mode { get; set; } = BindingUpdateMode.UpdateTarget; /// protected override void OnAttached() { Property.ThrowIfNull(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 BindingUpdateMode.UpdateTarget: Dispatcher.Invoke(() => BindingOperations.GetBindingExpression(AssociatedObject, Property)?.UpdateTarget()); break; case BindingUpdateMode.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(); } // Determines when the target/source of the property should be updated. WeakTimer _timer; // The method invoked to perform the update. TimerCallback _timerCallback; } /// /// Determines the direction of the data transfer. /// public enum BindingUpdateMode { /// /// Forces a data transfer from the binding source property to the binding target property. /// UpdateTarget, /// /// Forces a data transfer from the binding target property to the binding source property. /// UpdateSource } }