在TextBox条目中暂停期间引发PropertyChanged事件?

我想知道当用户在TextBox键入文本时暂停时是否可以引发PropertyChanged事件? 或者更具体地说,我想在用户停止在TextBox中键入X秒后运行一个方法。

例如,我有一个带有TextBox的表单,没有别的。 用户在TextBox中键入1-9位Id值,相当资源密集的后台进程加载记录。

我不想使用UpdateSouceTrigger=PropertyChanged因为这会导致资源密集型后台进程在键入字符时运行,因此9个ID号从这些进程中的9个开始。

我也不想使用UpdateSourceTrigger=LostFocus因为表单上没有任何其他内容可以使TextBox失去焦点。

那么有没有办法让我的后台进程只有在用户在输入ID号时暂停后才会运行?

准备代码转储。

我用WPF假行为(一个附加的DP就像一个行为)这样做了。 此代码有效,但它不漂亮,可能会导致泄漏。 可能需要用弱引用等替换所有引用。

这是Behavior类:

 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Windows; using System.Windows.Threading; using System.Windows.Data; using System.ComponentModel; namespace BehaviorForDelayedTrigger { public static class DelayedUpdateBehavior { #region TargetProperty Attached DependencyProperty ///  /// An Attached  of type  defined on DependencyObject instances. ///  public static readonly DependencyProperty TargetPropertyProperty = DependencyProperty.RegisterAttached( TargetPropertyPropertyName, typeof(DependencyProperty), typeof(DelayedUpdateBehavior), new FrameworkPropertyMetadata(null, OnTargetPropertyChanged) ); ///  /// The name of the  Attached . ///  public const string TargetPropertyPropertyName = "TargetProperty"; ///  /// Sets the value of the  on the given . ///  /// The target element. public static void SetTargetProperty(DependencyObject element, DependencyProperty value) { element.SetValue(TargetPropertyProperty, value); } ///  /// Gets the value of the  as set on the given . ///  /// The target element. ///  public static DependencyProperty GetTargetProperty(DependencyObject element) { return (DependencyProperty)element.GetValue(TargetPropertyProperty); } ///  /// Called when  changes ///  /// The event source. /// event arguments private static void OnTargetPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { var prop = e.NewValue as DependencyProperty; if(prop == null) return; d.Dispatcher.BeginInvoke( (Action) ((target, p) => new PropertyChangeTimer(target, p)), DispatcherPriority.ApplicationIdle, d, prop); } #endregion #region Milliseconds Attached DependencyProperty ///  /// An Attached  of type  defined on DependencyObject instances. ///  public static readonly DependencyProperty MillisecondsProperty = DependencyProperty.RegisterAttached( MillisecondsPropertyName, typeof(int), typeof(DelayedUpdateBehavior), new FrameworkPropertyMetadata(1000) ); ///  /// The name of the  Attached . ///  public const string MillisecondsPropertyName = "Milliseconds"; ///  /// Sets the value of the  on the given . ///  /// The target element. public static void SetMilliseconds(DependencyObject element, int value) { element.SetValue(MillisecondsProperty, value); } ///  /// Gets the value of the  as set on the given . ///  /// The target element. ///  public static int GetMilliseconds(DependencyObject element) { return (int)element.GetValue(MillisecondsProperty); } #endregion private class PropertyChangeTimer { private DispatcherTimer _timer; private BindingExpression _expression; public PropertyChangeTimer(DependencyObject target, DependencyProperty property) { if (target == null) throw new ArgumentNullException("target"); if (property == null) throw new ArgumentNullException("property"); if (!BindingOperations.IsDataBound(target, property)) return; _expression = BindingOperations.GetBindingExpression(target, property); if (_expression == null) throw new InvalidOperationException("No binding was found on property "+ property.Name + " on object " + target.GetType().FullName); DependencyPropertyDescriptor.FromProperty(property, target.GetType()).AddValueChanged(target, OnPropertyChanged); } private void OnPropertyChanged(object sender, EventArgs e) { if (_timer == null) { _timer = new DispatcherTimer(); int ms = DelayedUpdateBehavior.GetMilliseconds(sender as DependencyObject); _timer.Interval = TimeSpan.FromMilliseconds(ms); _timer.Tick += OnTimerTick; _timer.Start(); return; } _timer.Stop(); _timer.Start(); } private void OnTimerTick(object sender, EventArgs e) { _expression.UpdateSource(); _expression.UpdateTarget(); _timer.Stop(); _timer = null; } } } } 

以下是如何使用它的示例:

             

这个要点是……

您在绑定的UIElement上设置附加属性,传入您希望延迟的DP。 在这一点上,我有附加属性的目标和要延迟的属性,所以我可以设置。 我必须等到绑定可用,所以我必须使用Dispatcher在设置数据绑定后实例化我的观察者类。 不能这样做,你不能抓住绑定表达式。

watcher类获取绑定并向DependencyProperty添加更新侦听器。 在监听器中,我设置了一个计时器(如果我们没有更新)或重置计时器。 一旦Timer滴答,我就会触发绑定表达式。

它再次起作用,但绝对需要清理。 此外,您可以通过其名称使用DP以及以下代码段:

 FieldInfo fieldInfo = instance.GetType() .GetField(name, BindingFlags.Public | BindingFlags.Static | BindingFlags.FlattenHierarchy); return (fieldInfo != null) ? (DependencyProperty)fieldInfo.GetValue(null) : null; 

您可能必须将“Property”添加到name ,但与使用x:Static相比,这很容易。

设置UpdateSourceTrigger=PropertyChanged ,然后每次属性更改时,启动计时器以获得您想要的延迟。 如果在计时器滴答之前再次更改属性,则取消旧计时器并启动新计时器。 如果计时器确实打勾,那么您知道该属性在X秒内没有改变,您可以启动后台进程。

我认为这正是您正在寻找的: WPF的DelayBinding

它是自定义绑定,完全符合上面的两个答案。 它最简单,如写或指定延迟间隔

如果您使用的是.NET 4.5或更高版本,则可以使用Binding的Delay属性。 这真的很容易:

  

为什么不使用UpdateSouceTrigger=PropertyChanged ,但不是直接触发后台进程,而是重置一个计时器,该计时器将在3秒之后触发该进程。 这样,如果他们在3秒之前键入其他内容,则计时器将重置,后台进程将从现在开始+3秒。