有没有一种方法可以监听在反应式扩展中没有引发的事件

本文关键字:扩展 反应式 事件 监听 一种 方法 有没有 | 更新日期: 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);
}

其中mehRichTextBox

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应用程序示例。在表格上,我有textBox1button1,就这样

下面是代码:

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方法,至少在我的测试中是这样。