当用户开始输入时,我有要求关闭某个功能,这很简单。当用户停止输入时,我想重新打开该功能。
如果没有被动扩展程序,可以使用timer
简单地实现此功能,该计数会将每次最后一次击键上的计时器重置为1 second
,并在user stops typing and timer elapses
时功能已重新启用。
我可以调用任何方法来实现与Reactive Extensions
相同的效果吗?
Throttle
或Timeout
每隔1 second
更新
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");
}
答案 0 :(得分:3)
很棒的问题。
可能有很多方法可以解决这个问题,但这里有一个你可以使用的方法。首先,我们需要源可观察序列。这可能是一个keypressed事件或属性更改事件,已使用FromEvent或其他工厂/转换或ReactiveUI转换为Observable序列。
在此示例中,我将使用Observable.Interval(TimeSpan.FromSeconds(0.25)).Take(4);
代替源序列(仅用于证明概念)。
接下来,我们需要决定何时需要禁用该功能(SpellCheck?)。这是源序列产生值的时候。
var disableSpellcheck = source.Select(_=>false);
然后我们需要决定何时需要重新启用该功能。这是源序列上有1秒钟的静音。您可以执行此操作的一个技巧是为源中的每个事件创建一秒计时器。创建新计时器时,取消之前的计时器。您可以通过创建嵌套的可观察序列来执行此操作,并使用Switch在生成新序列时取消先前的内部序列。
var enableSpellcheck = source.Select(_=>Observable.Timer(TimeSpan.FromSeconds(1)))
.Switch()
.Select(_=>true);
现在我们要合并这两个序列,并将结果推送到启用/禁用该功能的方法。
Observable.Merge(disableSpellcheck, enableSpellcheck)
.Subscribe(isEnabled=>SetFlag(isEnabled));
但是,如上所述,每次源序列yileded一个值时,都会调用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");
}
答案 1 :(得分:3)
以下是显示上述代码如何移植到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 packeage将拉出代码所需的所有其他内容。这是.NET 4.5。
这是一个展示如何解决问题的示例。即我不建议使用.ObserveOnDispatcher()
,我不建议编写代码隐藏,我知道在复选框上设置IsEnabled并不是实际进行拼写检查。我希望这足以让观众重新创造他们的实际解决方案。
我希望它有所帮助。
答案 2 :(得分:0)
我相信this example与Throttle解决了类似的问题。
所以一些事情也可能对你有用:
var throttled = observable.Throttle(TimeSpan.FromMilliseconds(1000));
using (throttled.Subscribe(x => RestoreThing())) {}
答案 3 :(得分:0)
我不是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
方法,至少在我的测试中是这样。