有没有一种方法可以监听在反应式扩展中没有引发的事件
本文关键字:扩展 反应式 事件 监听 一种 方法 有没有 | 更新日期: 2023-09-27 18:04:11
我有一个要求,当用户开始键入时关闭某个功能,这很简单。当用户停止键入时,我想重新打开该功能。
在没有响应式扩展的情况下,可以简单地使用timer
来实现此功能,该CCD_1在每次最后一次按键时将计时器重置为1 second
,并且在user stops typing and timer elapses
时将功能重新打开。
有什么方法我可以调用Reactive Extensions
来达到同样的效果吗?
CCD_ 5或CCD_
更新
XAML
<RichTextBox MaxHeight="1000" VerticalScrollBarVisibility="Visible" x:Name="meh"/>
扩展类
public static IObservable<EventArgs> ObserveTextChanged(this RichTextBox rtb)
{
return Observable.FromEventPattern<TextChangedEventHandler, EventArgs>(
h => rtb.TextChanged += h,
h => rtb.TextChanged -= h)
.Select(ep => ep.EventArgs);
}
类其中meh
是RichTextBox
public class MainWindow()
{
//Change this to be the keypress/propertychagned event. The type T doesn't matter we ignore it
var typing = meh.ObserveTextChanged().Take(4);
var silence = meh.ObserveTextChanged().IgnoreElements();
var source = typing.Concat(silence).Concat(typing);
var disableSpellcheck = source.Select(_ => false);
var enableSpellcheck = source.Select(_ => Observable.Timer(TimeSpan.FromSeconds(1)))
.Switch()
.Select(_ => true);
disableSpellcheck.Merge(enableSpellcheck)
.DistinctUntilChanged()
.Subscribe(SetFlag);
}
// Define other methods and classes here
public void SetFlag(bool flag)
{
Dispatcher.Invoke(new Action(() => SpellCheck.SetIsEnabled(meh, flag)));
Debug.Write("flag");
}
以下是所有代码,展示了如何将上面的代码移植到WPF。这里的交流似乎有差距,所以我创建了整个wpf应用程序来证明这一点。
<Window x:Class="StackoverFlow_23764884.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<StackPanel>
<HeaderedContentControl Header="When checked, spellcheck is enabled (emulated)">
<CheckBox x:Name="spellChecking" IsChecked="True" IsEnabled="False"/>
</HeaderedContentControl>
<HeaderedContentControl Header="Type here to see the Spellcheck enable and disable">
<RichTextBox x:Name="meh" Width="400" Height="300" />
</HeaderedContentControl>
</StackPanel>
</Window>
以及背后的代码:
using System;
using System.Reactive.Linq;
using System.Windows;
using System.Windows.Controls;
namespace StackoverFlow_23764884
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
var source = meh.ObserveTextChanged();
var disableSpellcheck = source.Select(_ => false);
var enableSpellcheck = source.Select(_ => Observable.Timer(TimeSpan.FromSeconds(1)))
.Switch()
.Select(_ => true);
disableSpellcheck.Merge(enableSpellcheck)
.DistinctUntilChanged()
.ObserveOnDispatcher()
.Subscribe(isEnabled => spellChecking.IsChecked=isEnabled);
}
}
public static class ObEx
{
public static IObservable<EventArgs> ObserveTextChanged(this RichTextBox rtb)
{
return Observable.FromEventPattern<TextChangedEventHandler, EventArgs>(
h => rtb.TextChanged += h,
h => rtb.TextChanged -= h)
.Select(ep => ep.EventArgs);
}
}
}
我在Rx-WPF nuget包中提取了代码所需的所有其他内容。这是.NET 4.5。
这是一个演示如何解决问题的示例。即,我不建议使用.ObserveOnDispatcher()
,我不推荐在后面写代码,而且我知道在复选框上设置IsEnabled实际上并不是进行拼写检查。我希望这足以让观众重现他们的实际解决方案。
我确实希望它能有所帮助。
好问题。
可能有很多方法可以解决这个问题,但这里有一种你可以使用的方法。首先我们需要源可观测序列。这可能是一个按键事件或属性更改事件,已使用FromEvent或其他工厂/转换或ReactiveUI转换为Observable序列。
在这个例子中,我将使用Observable.Interval(TimeSpan.FromSeconds(0.25)).Take(4);
作为源序列的替代品(只是为了证明这个概念(。
接下来,我们需要决定何时需要禁用该功能(SpellCheck?(。这是源序列产生值的时候。
var disableSpellcheck = source.Select(_=>false);
然后我们需要决定何时需要重新启用该功能。这是当源序列上已经有1秒的静默时。实现这一点的一个技巧是为源中的每个事件创建一个1秒计时器。创建新计时器时,请取消上一个计时器。您可以通过创建一个嵌套的可观察序列来实现这一点,并在生成新序列时使用Switch取消以前的内部序列。
var enableSpellcheck = source.Select(_=>Observable.Timer(TimeSpan.FromSeconds(1)))
.Switch()
.Select(_=>true);
现在我们想要合并这两个序列,并将结果推送到启用/禁用该功能的方法。
Observable.Merge(disableSpellcheck, enableSpellcheck)
.Subscribe(isEnabled=>SetFlag(isEnabled));
但是,如上所述,每次源序列返回值时,都会调用SetFlag(false(。使用DistinctUntilChanged()
算子可以很容易地解决这一问题。
最终样本(LinqPad(代码如下:
void Main()
{
//Change this to be the keypress/propertychagned event. The type T doesn't matter we ignore it
var typing = Observable.Interval(TimeSpan.FromSeconds(0.25)).Take(4);
var silence = Observable.Timer(TimeSpan.FromSeconds(1)).IgnoreElements();
var source = typing.Concat(silence).Concat(typing);
var disableSpellcheck = source.Select(_=>false);
var enableSpellcheck = source.Select(_=>Observable.Timer(TimeSpan.FromSeconds(1)))
.Switch()
.Select(_=>true);
Observable.Merge(disableSpellcheck, enableSpellcheck)
.DistinctUntilChanged()
.Subscribe(isEnabled=>SetFlag(isEnabled));
}
// Define other methods and classes here
public void SetFlag(bool flag)
{
flag.Dump("flag");
}
我相信这个带有Throttle的例子解决了类似的问题。
因此,一些类似的东西可能也适用于你:
var throttled = observable.Throttle(TimeSpan.FromMilliseconds(1000));
using (throttled.Subscribe(x => RestoreThing())) {}
我不是RX大师,但我创建了一个似乎可以工作的WinForms应用程序示例。在表格上,我有textBox1
和button1
,就这样
下面是代码:
public Form1()
{
InitializeComponent();
var observable = Observable.FromEventPattern(
s => textBox1.TextChanged += s, s => textBox1.TextChanged -= s)
//make sure we are on the UI thread
.ObserveOn(SynchronizationContext.Current)
//immediately set it to false
.Do(_ => UpdateEnabledStatus(false))
//throttle for one second
.Throttle(TimeSpan.FromMilliseconds(1000))
//again, make sure on UI thread
.ObserveOn(SynchronizationContext.Current)
//now re-enable
.Subscribe(_ => UpdateEnabledStatus(true));
}
private void UpdateEnabledStatus(bool enabled)
{
button1.Enabled = enabled;
}
这可以随心所欲地工作,并且不会每秒都碰到UpdateEnabledStatus
方法,至少在我的测试中是这样。