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
}
}