가는 하나의 방법입니다. 그러나 ContentControl
에 많은 항목이 있고 업데이트하려는 속성이 인 경우 ContentControl
일 때 불필요하게 100 개 이상의 타이머를 만들고 동시에 PropertyChanged
을 모두 발생시키는 것을 의미합니다. 그러나 ItemsControl
(ListBox
)과 같이 사용하면 모든 항목에 대해이 동작이 계속 만들어집니다.
이 이유 때문에 템플릿에 바인딩마다 한 번만 생성되는이 동작을 만들었습니다. 순수한 MVVM이기도합니다.
사용
<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>
종속성 http://schemas.microsoft.com/expression/2010/interactivity 네임 스페이스 System.Windows.Interactivity.WPF
라는 NuGet 패키지에서 볼 수하는 것으로
. 또한 프로젝트를 블렌드로 열면 자동으로 추가됩니다.
복사 오히려 기본 데이터 소스 통지를하는 것보다, 주기적으로 당겨 것이다 나는 요소를 가질 수 있었을 것이다 희망 (또는 결합) 된
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();
}
}
}
환상적입니다. 감사. –
감사합니다. 이 문제를 해결할 수있는 확실한 방법을 찾고있었습니다. 내가 오래된 C# 버전을 사용하고 있는지, 아니면'if (weakCallback.TryGetTarget (out TimerCallback callback))'를'TimerCallback 콜백으로 변경해야만했는지 모르겠다. if (weakCallback.TryGetTarget (out 콜백))'을 사용하십시오. – monoceres
@monoceres 오, 그래! C# 7입니다. 한번 시도해 본 후에는 그것을 사용할 수 없습니다. – fjch1997