WPFとかUWPのTriggerにはTimerTrgigerっていう一定間隔でActionを実行してくれる便利なTriggerがあります。
使い方はこっち
http://c-mitsuba.hatenablog.com/entry/20120228/1330420575
実行間隔はMillisecondsPerTickで設定できるんですが、こいつはDependencyPropertyのくせに、Bindingで途中で変更しても、最初に設定した値の間隔で実行しつづけます。
というわけで、MillisecondsPerTickをBindingで動的に変えたいっていうおはなし。
まずはじめにTimerTrigerの実装をみてみましょう。
と思ったけど、残念ながらTimerTrigerが含まれるMicrosoft.Expression.Interactivityはsource referenceには公開されていません。
http://referencesource.microsoft.com/
というわけで、ILSpyでのぞいちゃいます。
TimerTriggerはMicrosoft.Expression.InteractivityのMicrosoft.Expression.Interactivity.Coreの中にあります。
でてきたコードをまるっとコピーしちゃいます。
継承してるInterfaceも一緒に。
using System; using System.Windows; using System.Windows.Interactivity; using System.Windows.Threading; namespace Microsoft.Expression.Interactivity.Core { /// <summary> /// ソース上で発生する指定イベントによりトリガーされ、イベントが起動されたときに一定時間遅れて起動するトリガー。 /// </summary> public class TimerTrigger : System.Windows.Interactivity.EventTrigger { internal class DispatcherTickTimer : ITickTimer { private DispatcherTimer dispatcherTimer; public event EventHandler Tick { add { this.dispatcherTimer.Tick += value; } remove { this.dispatcherTimer.Tick -= value; } } public TimeSpan Interval { get { return this.dispatcherTimer.Interval; } set { this.dispatcherTimer.Interval = value; } } public DispatcherTickTimer() { this.dispatcherTimer = new DispatcherTimer(); } public void Start() { this.dispatcherTimer.Start(); } public void Stop() { this.dispatcherTimer.Stop(); } } public static readonly DependencyProperty MillisecondsPerTickProperty = DependencyProperty.Register("MillisecondsPerTick", typeof(double), typeof(TimerTrigger), new FrameworkPropertyMetadata(1000.0)); public static readonly DependencyProperty TotalTicksProperty = DependencyProperty.Register("TotalTicks", typeof(int), typeof(TimerTrigger), new FrameworkPropertyMetadata(-1)); private ITickTimer timer; private EventArgs eventArgs; private int tickCount; /// <summary> /// 目盛の間の待ち時間 (ミリ秒) を取得または設定します。これは依存関係プロパティです。 /// </summary> public double MillisecondsPerTick { get { return (double)base.GetValue(TimerTrigger.MillisecondsPerTickProperty); } set { base.SetValue(TimerTrigger.MillisecondsPerTickProperty, value); } } /// <summary> /// トリガーが終了する前に起動される目盛の合計数を取得または設定します。これは依存関係プロパティです。 /// </summary> public int TotalTicks { get { return (int)base.GetValue(TimerTrigger.TotalTicksProperty); } set { base.SetValue(TimerTrigger.TotalTicksProperty, value); } } /// <summary> /// <see cref="T:Microsoft.Expression.Interactivity.Core.TimerTrigger" /> クラスの新しいインスタンスを初期化します。 /// </summary> public TimerTrigger() : this(new TimerTrigger.DispatcherTickTimer()) { } internal TimerTrigger(ITickTimer timer) { this.timer = timer; } protected override void OnEvent(EventArgs eventArgs) { this.StopTimer(); this.eventArgs = eventArgs; this.tickCount = 0; this.StartTimer(); } protected override void OnDetaching() { this.StopTimer(); base.OnDetaching(); } internal void StartTimer() { if (this.timer != null) { this.timer.Interval = TimeSpan.FromMilliseconds(this.MillisecondsPerTick); this.timer.Tick += new EventHandler(this.OnTimerTick); this.timer.Start(); } } internal void StopTimer() { if (this.timer != null) { this.timer.Stop(); this.timer.Tick -= new EventHandler(this.OnTimerTick); } } private void OnTimerTick(object sender, EventArgs e) { if (this.TotalTicks > 0 && ++this.tickCount >= this.TotalTicks) { this.StopTimer(); } base.InvokeActions(this.eventArgs); } } internal interface ITickTimer { event EventHandler Tick; TimeSpan Interval { get; set; } void Start(); void Stop(); } }
こんなかんじ。
で、MillisecondPerTickのプロパティをみてみると。。
PropertyChangedCallBackがないんですよね。
public static readonly DependencyProperty MillisecondsPerTickProperty = DependencyProperty.Register("MillisecondsPerTick", typeof(double), typeof(TimerTrigger), new FrameworkPropertyMetadata(1000.0));
このやる気ない感じ。
なので、PropertyChangedCallBackを書き足して、変更処理を追記します。
public static readonly DependencyProperty MillisecondsPerTickProperty = DependencyProperty.Register("MillisecondsPerTick", typeof(double), typeof(TimerTrigger), new FrameworkPropertyMetadata(1000.0,PropertyChangedCallback)); private static void PropertyChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs) { var self = dependencyObject as TimerTrigger; self._timer.Stop(); self._timer.Interval = TimeSpan.FromMilliseconds(self.MillisecondsPerTick); self._timer.Start(); }
変更があったら一旦タイマーをStopして、Intervalを再設定して、もう一回Startするかんじで。
あとはnamespaceをあわせたり、クラス名を替えたりして、既存のと被らないようにします。
全文はこんなかんじ。
using System; using System.Windows; using System.Windows.Threading; namespace FetishSister { /// <summary> /// ソース上で発生する指定イベントによりトリガーされ、イベントが起動されたときに一定時間遅れて起動するトリガー。 /// </summary> public class TimerTrigger : System.Windows.Interactivity.EventTrigger { internal class DispatcherTickTimer : ITickTimer { private DispatcherTimer dispatcherTimer; public event EventHandler Tick { add { this.dispatcherTimer.Tick += value; } remove { this.dispatcherTimer.Tick -= value; } } public TimeSpan Interval { get { return this.dispatcherTimer.Interval; } set { this.dispatcherTimer.Interval = value; } } public DispatcherTickTimer() { this.dispatcherTimer = new DispatcherTimer(); } public void Start() { this.dispatcherTimer.Start(); } public void Stop() { this.dispatcherTimer.Stop(); } } public static readonly DependencyProperty MillisecondsPerTickProperty = DependencyProperty.Register("MillisecondsPerTick", typeof(double), typeof(TimerTrigger), new FrameworkPropertyMetadata(1000.0,PropertyChangedCallback)); private static void PropertyChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs) { var self = dependencyObject as TimerTrigger; self._timer.Stop(); self._timer.Interval = TimeSpan.FromMilliseconds(self.MillisecondsPerTick); self._timer.Start(); } public static readonly DependencyProperty TotalTicksProperty = DependencyProperty.Register("TotalTicks", typeof(int), typeof(TimerTrigger), new FrameworkPropertyMetadata(-1)); private ITickTimer _timer; private EventArgs _eventArgs; private int _tickCount; /// <summary> /// 目盛の間の待ち時間 (ミリ秒) を取得または設定します。これは依存関係プロパティです。 /// </summary> public double MillisecondsPerTick { get { return (double)base.GetValue(MillisecondsPerTickProperty); } set { base.SetValue(MillisecondsPerTickProperty, value); } } /// <summary> /// トリガーが終了する前に起動される目盛の合計数を取得または設定します。これは依存関係プロパティです。 /// </summary> public int TotalTicks { get { return (int)base.GetValue(TotalTicksProperty); } set { base.SetValue(TotalTicksProperty, value); } } /// <summary> /// <see cref="T:WpfApplication11.TimerTrigger" /> クラスの新しいインスタンスを初期化します。 /// </summary> public TimerTrigger(): this(new DispatcherTickTimer()) { } internal TimerTrigger(ITickTimer timer) { this._timer = timer; } protected override void OnEvent(EventArgs eventArgs) { this.StopTimer(); this._eventArgs = eventArgs; this._tickCount = 0; this.StartTimer(); } protected override void OnDetaching() { this.StopTimer(); base.OnDetaching(); } internal void StartTimer() { if (this._timer != null) { this._timer.Interval = TimeSpan.FromMilliseconds(this.MillisecondsPerTick); this._timer.Tick += this.OnTimerTick; this._timer.Start(); } } internal void StopTimer() { if (this._timer != null) { this._timer.Stop(); this._timer.Tick -= this.OnTimerTick; } } private void OnTimerTick(object sender, EventArgs e) { if (this.TotalTicks > 0 && ++this._tickCount >= this.TotalTicks) { this.StopTimer(); } base.InvokeActions(this._eventArgs); } } internal interface ITickTimer { event EventHandler Tick; TimeSpan Interval { get; set; } void Start(); void Stop(); } }
これでBindingで間隔を変更できるようになりましたとさ。
あ、でも0以下とか突っ込むと落ちる気もするから、そのへんはよしなに。