C# WPF文本框搜索

C# WPF文本框搜索,c#,wpf,reactiveui,C#,Wpf,Reactiveui,我有一个快速查找文本框。我想筛选包含quickFind字符串的记录集合 我怎样才能延迟搜索,直到用户停止键入2秒钟?下面是一个我希望能帮你解决问题的类。包括底部显示的示例用法 public class EventDelayer { /// <summary> /// Contains info on an individual event that was queued; /// </summary> public class Delay

我有一个快速查找文本框。我想筛选包含quickFind字符串的记录集合


我怎样才能延迟搜索,直到用户停止键入2秒钟?

下面是一个我希望能帮你解决问题的类。包括底部显示的示例用法

public class EventDelayer
{

    /// <summary>
    /// Contains info on an individual event that was queued;
    /// </summary>
    public class DelayedEventInfo
    {
        private readonly object _sender;
        private readonly EventArgs _eventArgs;
        private readonly DateTime _eventTime;

        public DelayedEventInfo(object sender, EventArgs eventArgs, DateTime eventTime)
        {
            _sender = sender;
            _eventArgs = eventArgs;
            _eventTime = eventTime;
        }
        public object Sender { get { return _sender; } }
        public EventArgs EventArgs { get { return _eventArgs; } }
        public DateTime EventTime { get { return _eventTime; } }
    }

    /// <summary>
    /// contains a list of 
    /// </summary>
    public class DelayedEventArgs : EventArgs, IEnumerable<DelayedEventInfo>
    {
        private readonly List<DelayedEventInfo> _eventInfos;
        public DelayedEventArgs(IEnumerable<DelayedEventInfo> eventInfos)
        {
            _eventInfos = new List<DelayedEventInfo>(eventInfos);
        }

        public IEnumerator<DelayedEventInfo> GetEnumerator()
        {
            return _eventInfos.GetEnumerator();
        }

        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return _eventInfos.GetEnumerator();
        }

    }

    private readonly List<DelayedEventInfo> _infoList = new List<DelayedEventInfo>();
    private readonly TimeSpan _delayTime;
    private readonly object _lock = new object();

    private System.Threading.Timer _timer;

    public event EventHandler<DelayedEventArgs> DelayedEvent;

    public EventDelayer(TimeSpan delayTime)
    {
        _delayTime = delayTime;
    }


    /// <summary>
    /// call to 'enqueue' an event.
    /// </summary>
    public void Enqueue(object sender, EventArgs args)
    {
        lock (_lock)
        {
            _infoList.Add(new DelayedEventInfo(sender, args, DateTime.Now));
            if (_timer != null)
            {
                _timer.Dispose();
                _timer = null;
            }
            _timer = new System.Threading.Timer(ThreadProc, this, _delayTime, TimeSpan.FromMilliseconds(-1));
        }
    }

    /// <summary>
    /// raises the event.
    /// </summary>
    private void HandleTimer()
    {
        lock (_lock)
        {
            var ev = this.DelayedEvent;
            if (ev != null)
            {
                DelayedEventArgs args = new DelayedEventArgs(_infoList);
                Invoke(()=> ev(this, args));
            }
            _infoList.Clear();

        }
    }

    private static void ThreadProc(Object stateInfo)
    {
        EventDelayer thisObj = (EventDelayer)stateInfo;
        thisObj.HandleTimer();
    }

    private static Lazy<System.Windows.Threading.Dispatcher> _dispatchObject = new Lazy<System.Windows.Threading.Dispatcher>(() =>
    {
        if (Application.Current != null)
        {
            return Application.Current.Dispatcher;
        }
        else
        {
            return null;
        }
    });

    public static void Invoke(Action action)
    {
        if (_dispatchObject.Value == null || _dispatchObject.Value.CheckAccess())
        {
            action();
        }
        else
        {
            _dispatchObject.Value.Invoke(action);
        }
    }


}

private class ExampleUsage
{
    /// <summary>
    /// shows how to create a event delayer and use it to listen to the events from a text box and call if no further changes for 2 seconds.
    /// </summary>
    private static void ShowUsage(System.Windows.Controls.TextBox textBox)
    {
        EventDelayer eventDelayer = new EventDelayer(TimeSpan.FromSeconds(2));
        textBox.TextChanged += eventDelayer.Enqueue;
        eventDelayer.DelayedEvent += eventDelayer_DelayedEvent;
    }

    /// <summary>
    /// redo search here. if required you can access the event args originally raised from the textbox through the event args of this method
    /// </summary>
    static void eventDelayer_DelayedEvent(object sender, EventDelayer.DelayedEventArgs e)
    {
        foreach (var eventInfo in e)
        {
            var originalSender = eventInfo.Sender;
            var args = eventInfo.EventArgs;
            var timeInitiallyCalled = eventInfo.EventTime;
        }
    }
}
公共类事件延迟器
{
/// 
///包含排队的单个事件的信息;
/// 
公共类DelayedEventInfo
{
私有只读对象\u发送方;
私有只读事件args\u事件args;
私有只读日期时间_eventTime;
public DelayedEventInfo(对象发送方、EventArgs EventArgs、DateTime eventTime)
{
_发送方=发送方;
_eventArgs=eventArgs;
_eventTime=eventTime;
}
公共对象发送方{get{return}\u Sender;}
public EventArgs EventArgs{get{return\u EventArgs;}}
public DateTime EventTime{get{return\u EventTime;}}
}
/// 
///包含以下内容的列表:
/// 
公共类DelayedEventArgs:EventArgs,IEnumerable
{
私有只读列表_eventInfos;
公共延迟事件目标(IEnumerable eventInfos)
{
_eventInfos=新列表(eventInfos);
}
公共IEnumerator GetEnumerator()
{
返回_eventInfos.GetEnumerator();
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
返回_eventInfos.GetEnumerator();
}
}
私有只读列表_infoList=新列表();
私有只读时间span\u delayTime;
私有只读对象_lock=新对象();
专用系统.Threading.Timer\u Timer;
公共事件处理程序DelayedEvent;
公共事件延迟器(TimeSpan delayTime)
{
_延迟时间=延迟时间;
}
/// 
///调用“排队”事件。
/// 
public void排队(对象发送方、事件args args)
{
锁
{
_Add(newdelayedeventinfo(sender,args,DateTime.Now));
如果(_timer!=null)
{
_timer.Dispose();
_定时器=空;
}
_timer=new System.Threading.timer(ThreadProc,this,_delayTime,TimeSpan.frommilless(-1));
}
}
/// 
///引发事件。
/// 
私有void HandleTimer()
{
锁
{
var ev=此.DelayedEvent;
如果(ev!=null)
{
DelayedEventTargets args=新的DelayedEventTargets(\u信息列表);
调用(()=>ev(this,args));
}
_infoList.Clear();
}
}
私有静态void ThreadProc(对象状态信息)
{
EventDelayer thisObj=(EventDelayer)状态信息;
thisObj.HandleTimer();
}
private static Lazy_dispatchObject=新的Lazy(()=>
{
if(Application.Current!=null)
{
返回Application.Current.Dispatcher;
}
其他的
{
返回null;
}
});
公共静态无效调用(操作)
{
如果(_dispatchObject.Value==null | | | _dispatchObject.Value.CheckAccess())
{
动作();
}
其他的
{
_dispatchObject.Value.Invoke(操作);
}
}
}
私有类示例用法
{
/// 
///演示如何创建事件延迟器,并使用它从文本框中侦听事件,并在2秒钟内没有进一步更改时调用。
/// 
专用静态void ShowUsage(System.Windows.Controls.TextBox TextBox)
{
EventDelayer EventDelayer=新的EventDelayer(TimeSpan.FromSeconds(2));
textBox.TextChanged+=eventDelayer.Enqueue;
eventDelayer.DelayedEvent+=eventDelayer\u DelayedEvent;
}
/// 
///重做此处搜索。如果需要,您可以通过此方法的事件参数访问最初从文本框中引发的事件参数
/// 
静态void eventDelayer\u DelayedEvent(对象发送方,eventDelayer.DelayedEventArgs e)
{
foreach(e中的var eventInfo)
{
var originalSender=eventInfo.Sender;
var args=eventInfo.EventArgs;
var timeInitiallyCalled=eventInfo.EventTime;
}
}
}

将文本框文本绑定到字符串,然后设置绑定延迟

<TextBox>
    <TextBox.Text>
        <Binding Path="searchText" UpdateSourceTrigger="PropertyChanged" Delay="2000" />
    </TextBox.Text>
</TextBox>

以下是完成整个过程的反应式UI方法(在2秒延迟后过滤项目):


我喜欢计时器和锁的例子,因为它显示了反应UI是多么简单:)

时间戳框从空变为有字符的点,然后每次发生更改时,检查以确保在调用搜索函数之前至少2秒。嗯,这很有效,很简单,但事实也是如此untestable@Paul“不稳定”是什么意思。您当然可以编写一个测试来进行搜索,因为您可以将绑定绑定到自己的字符串。您不必测试延迟,它是framework.BTW的一部分,延迟属性仅在WPF 4.5或更高版本中可用。
// These are defined in your ViewModel class as settable Properties
string FilterText;
ReactiveList<Record> ListOfRecords;
IReactiveDerivedList<Record> FilteredRecords;

// This is in your ViewModel constructor
FilteredRecords = ListOfRecords.CreateDerivedCollection(
    x => !String.IsNullOrWhiteSpace(FilterText) ? recordContainsString(FilterText) : true,
    x => x.Id,
    this.WhenAnyValue(x => x.FilterText).Throttle(TimeSpan.FromSeconds(2.0));
this.WhenAnyValue(x => x.SomeProperty)
    .Throttle(TimeSpan.FromSeconds(2.0), RxApp.MainThreadScheduler)
    .Subscribe(x => Console.WriteLine("The item is " + x);