5

PropertyChangedユーザーがテキストを入力中に一時停止したときにイベントを発生させることができるかどうか疑問に思っていましたTextBox? Xより具体的には、ユーザーが TextBox への入力を停止してから数秒後にメソッドを実行したいと考えています。

たとえば、TextBox のみを含むフォームがあります。ユーザーが 1 ~ 9 桁の Id 値を TextBox に入力すると、かなりリソースを消費するバックグラウンド プロセスによってレコードが読み込まれます。

文字が入力されるたびにリソースを集中的に使用するバックグラウンド プロセスが実行されるため、使用したくありませんUpdateSouceTrigger=PropertyChanged。9 桁の ID 番号はこれらのプロセスの 9 つから始まります。

UpdateSourceTrigger=LostFocusTextBox がフォーカスを失うようにするフォームには他に何もないため、私も使用したくありません。

ID番号を入力するときにユーザーが一時停止した後にのみバックグラウンドプロセスを実行させる方法はありますか?

4

5 に答える 5

10

を設定UpdateSourceTrigger=PropertyChangedし、プロパティが変更されるたびに、希望する遅延のタイマーを開始します。タイマーが刻む前にプロパティが再度変更された場合は、古いタイマーをキャンセルして、新しいタイマーを開始します。タイマー作動する場合、プロパティが X 秒間変更されていないことがわかり、バックグラウンド プロセスを開始できます。

于 2011-07-15T18:01:25.690 に答える
8

コードダンプを準備します。

これは、WPF Fake Behavior (動作のように機能する添付の 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
        /// <summary>
        /// An Attached <see cref="DependencyProperty"/> of type <see cref="DependencyProperty"/> defined on <see cref="DependencyObject">DependencyObject instances</see>.
        /// </summary>
        public static readonly DependencyProperty TargetPropertyProperty = DependencyProperty.RegisterAttached(
          TargetPropertyPropertyName,
          typeof(DependencyProperty),
          typeof(DelayedUpdateBehavior),
          new FrameworkPropertyMetadata(null, OnTargetPropertyChanged)
        );

        /// <summary>
        /// The name of the <see cref="TargetPropertyProperty"/> Attached <see cref="DependencyProperty"/>.
        /// </summary>
        public const string TargetPropertyPropertyName = "TargetProperty";

        /// <summary>
        /// Sets the value of the <see cref="TargetPropertyProperty"/> on the given <paramref name="element"/>.
        /// </summary>
        /// <param name="element">The <see cref="DependencyObject">target element</see>.</param>
        public static void SetTargetProperty(DependencyObject element, DependencyProperty value)
        {
            element.SetValue(TargetPropertyProperty, value);
        }

        /// <summary>
        /// Gets the value of the <see cref="TargetPropertyProperty"/> as set on the given <paramref name="element"/>.
        /// </summary>
        /// <param name="element">The <see cref="DependencyObject">target element</see>.</param>
        /// <returns><see cref="DependencyProperty"/></returns>
        public static DependencyProperty GetTargetProperty(DependencyObject element)
        {
            return (DependencyProperty)element.GetValue(TargetPropertyProperty);
        }

        /// <summary>
        /// Called when <see cref="TargetPropertyProperty"/> changes
        /// </summary>
        /// <param name="d">The <see cref="DependencyObject">event source</see>.</param>
        /// <param name="e"><see cref="DependencyPropertyChangedEventArgs">event arguments</see></param>
        private static void OnTargetPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var prop = e.NewValue as DependencyProperty;
            if(prop == null)
                return;
            d.Dispatcher.BeginInvoke(
                (Action<DependencyObject, DependencyProperty>)
                    ((target, p) => new PropertyChangeTimer(target, p)), 
                DispatcherPriority.ApplicationIdle, 
                d, 
                prop);

        }
        #endregion
        #region Milliseconds Attached DependencyProperty
        /// <summary>
        /// An Attached <see cref="DependencyProperty"/> of type <see cref="int"/> defined on <see cref="DependencyObject">DependencyObject instances</see>.
        /// </summary>
        public static readonly DependencyProperty MillisecondsProperty = DependencyProperty.RegisterAttached(
          MillisecondsPropertyName,
          typeof(int),
          typeof(DelayedUpdateBehavior),
          new FrameworkPropertyMetadata(1000)
        );

        /// <summary>
        /// The name of the <see cref="MillisecondsProperty"/> Attached <see cref="DependencyProperty"/>.
        /// </summary>
        public const string MillisecondsPropertyName = "Milliseconds";

        /// <summary>
        /// Sets the value of the <see cref="MillisecondsProperty"/> on the given <paramref name="element"/>.
        /// </summary>
        /// <param name="element">The <see cref="DependencyObject">target element</see>.</param>
        public static void SetMilliseconds(DependencyObject element, int value)
        {
            element.SetValue(MillisecondsProperty, value);
        }

        /// <summary>
        /// Gets the value of the <see cref="MillisecondsProperty"/> as set on the given <paramref name="element"/>.
        /// </summary>
        /// <param name="element">The <see cref="DependencyObject">target element</see>.</param>
        /// <returns><see cref="int"/></returns>
        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;
            }
        }
    }
}

そして、これがどのように使用されるかの例です:

<Window
    x:Class="BehaviorForDelayedTrigger.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:t="clr-namespace:BehaviorForDelayedTrigger">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition />
            <RowDefinition
                Height="auto" />
        </Grid.RowDefinitions>
        <Viewbox>
            <TextBlock
                x:Name="TargetTextBlock"
                Background="Red" />
        </Viewbox>
        <TextBox
            t:DelayedUpdateBehavior.TargetProperty="{x:Static TextBox.TextProperty}"
            t:DelayedUpdateBehavior.Milliseconds="1000"
            Grid.Row="1"
            Text="{Binding Text, ElementName=TargetTextBlock, UpdateSourceTrigger=Explicit}" />
    </Grid>
</Window>

これの要点は…

バインドされた UIElement に添付プロパティを設定し、遅延させたい DP を渡します。この時点で、添付プロパティのターゲットと遅延するプロパティがあるので、設定できます。バインディングが利用可能になるまで待たなければならないので、データバインディングが設定された後、Dispatcher を使用してウォッチャー クラスをインスタンス化する必要があります。これを行わないと、バインディング式を取得できません。

ウォッチャー クラスはバインディングを取得し、更新リスナーを DependencyProperty に追加します。リスナーで、タイマーを設定するか (更新していない場合)、タイマーをリセットします。タイマーが作動したら、バインディング式を起動します。

繰り返しますが、動作しますが、間違いなくクリーンアップが必要です。また、次のコード スニペットで名前を介して 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です。

于 2011-07-15T19:06:24.757 に答える
4

これはまさにあなたが探しているものだと思います: DelayBinding for WPF

上記の2つの回答が示唆することを正確に行うのはカスタムバインディングです。書き込み<TextBox Text="{z:DelayBinding Path=SearchText}" />や遅延間隔を指定するのと同じくらい簡単に最大化します<TextBox Text="{z:DelayBinding Path=SearchText, Delay='00:00:03'}" />

于 2011-07-15T19:00:24.570 に答える
2

を使用しない理由はありませんUpdateSouceTrigger=PropertyChangedが、バックグラウンド プロセスを直接起動する代わりに、たとえば 3 秒後にそのプロセスを起動するタイマーをリセットします。そうすれば、3 秒が経過する前に別の何かを入力すると、タイマーがリセットされ、バックグラウンド プロセスがさらに 3 秒後に実行されます。

于 2011-07-15T18:02:22.007 に答える