gpt4 book ai didi

.net - 如何每秒更新一次 WPF 绑定(bind)?

转载 作者:行者123 更新时间:2023-12-03 15:58:06 26 4
gpt4 key购买 nike

我想向用户显示自某些事件发生以来已经过去了多少秒。从概念上讲,我的 View 模型具有如下属性:

public DateTime OccurredAtUtc { get; set; }

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

如果我绑定(bind)一个 TextBlock.Text属性(property)给 SecondsSinceOccurrence ,该值出现但它是静态的。时间的流逝并不反射(reflect)这一事件的年龄越来越大。
<!-- static value won't update as time passes -->
<TextBlock Text="{Binding SecondsSinceOccurrence}" />

我可以在我的 View 模型中创建一个触发 PropertyChanged 的计时器每秒,但 UI 中可能有很多这样的元素(它是 ItemsControl 中项目的模板),我不想创建那么多计时器。

我对 Storyboard动画的了解不是很好。 WPF 动画框架可以在这种情况下提供帮助吗?

最佳答案

纯粹的 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 包下可用.如果您在 blend 中打开项目,它也会自动添加。

复制并粘贴代码
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();
}
}
}

关于.net - 如何每秒更新一次 WPF 绑定(bind)?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/949458/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com