C# 有没有一种方法可以确保在被动扩展中没有引发任何事件?
我有一个要求,当用户开始打字时关闭某个功能,这很简单。当用户停止输入时,我想重新打开该功能。 无需响应式扩展,只需使用C# 有没有一种方法可以确保在被动扩展中没有引发任何事件?,c#,system.reactive,reactive-programming,C#,System.reactive,Reactive Programming,我有一个要求,当用户开始打字时关闭某个功能,这很简单。当用户停止输入时,我想重新打开该功能。 无需响应式扩展,只需使用定时器即可实现此功能,该定时器在每次最后一次击键时将定时器重置为1秒,当用户停止键入且定时器消失时将重新启用功能 是否有任何方法可以调用以实现与反应式扩展相同的效果 Throttle或Timeout每隔1秒持续调用订户/异常操作 更新 XAML <RichTextBox MaxHeight="1000" VerticalScrollBarVisibility="Visibl
定时器
即可实现此功能,该定时器在每次最后一次击键时将定时器重置为1秒
,当用户停止键入且定时器消失时
将重新启用功能
是否有任何方法可以调用以实现与反应式扩展相同的效果
Throttle
或Timeout
每隔1秒持续调用订户/异常操作
更新
XAML
<RichTextBox MaxHeight="1000" VerticalScrollBarVisibility="Visible" x:Name="meh"/>
我相信与解决类似的问题
因此,一些类似的方法可能也适用于你:
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
方法,至少在我的测试中是这样。好问题
可能有很多方法可以解决这个问题,但这里有一个方法可以让你使用。首先,我们需要源可观测序列。这可能是一个按键事件或属性更改事件,已使用FromEvent或其他工厂/转换或ReactiveUI转换为可观察序列
在这个示例中,我将使用Observable.Interval(TimeSpan.FromSeconds(0.25)).Take(4)代码>作为源序列的替代(只是为了证明这个概念)
接下来,我们需要决定何时需要禁用该功能(拼写检查?)。这是源序列产生值的时间
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");
}
下面是显示如何将上述代码移植到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);
}
}
}
使用系统;
使用System.Reactive.Linq;
使用System.Windows;
使用System.Windows.Controls;
命名空间StackoverFlow_23764884
{
///
///MainWindow.xaml的交互逻辑
///
公共部分类主窗口:窗口
{
公共主窗口()
{
初始化组件();
var source=meh.ObserveTextChanged();
var disableSpellcheck=source.Select(=>false);
var enableSpellcheck=source.Select(=>Observable.Timer(TimeSpan.FromSeconds(1)))
.Switch()
.选择(=>true);
禁用拼写检查。合并(启用拼写检查)
.DistinctUntilChanged()
.ObserveOnDispatcher()
.Subscribe(isEnabled=>拼写检查.IsChecked=isEnabled);
}
}
公共静态类ObEx
{
公共静态IObservable ObserveTextChanged(此RichTextBox rtb)
{
返回可观察的.FromEventPattern(
h=>rtb.TextChanged+=h,
h=>rtb.TextChanged-=h)
.Select(ep=>ep.EventArgs);
}
}
}
我拉入了Rx WPF nuget packeage将拉入代码所需的所有其他内容。这是.NET4.5
这是一个演示如何解决该问题的示例。i、 e.我不建议使用.ObserveOnDispatcher()
,我不建议在后面编写代码,我知道在复选框上设置IsEnabled实际上并不是在进行拼写检查。我希望这足以让观众重现他们的实际解决方案
不过我还是希望它能有所帮助。假设这是一个TextChanged
,当用户开始键入时,我会关闭拼写检查,如果用户没有键入任何内容,那么我需要重新打开拼写检查。我不认为仅仅使用油门
就能达到我想要的效果。它只会关闭功能,但我需要将其关闭,因此,它将保持每1秒发射一次,这是我不想要的。我只想要最后一个。@ll我想如果你在打开拼写检查的情况下限制相同的文本更改并订阅该文本,它将按预期工作。是否有一种方法让它执行类似于>------------(事件停止)----1000-->ms publish to subscribers的操作。我以为你想在每次键入停止时触发该事件。油门不应该每1秒触发一次事件,它过滤并等待事件,而不是生成新事件。你使用哪个版本的Rx?那么这是否意味着当它观察到它不断调用UpdateEnableStatus(false)时?不,正如我在回答中所说,我没有看到任何证据表明,UpdateEnableStatus
每秒都会被点击(我坐在那里,上面有一个断点)尝试你的解决方案,但它一直在点击do(=>…)。每1秒:s@lllH
<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);
}
}
}