Есть ли способ услышать, что нет никаких событий, которые вызываются в реактивных расширениях?
У меня есть требование отключить определенную функцию, когда пользователь начинает печатать, что просто. И когда пользователь перестает печатать, я хочу снова включить эту функцию.
Без реактивных расширений можно просто реализовать эту функцию с помощью timer
, который сбрасывает таймер на каждом последнем нажатии клавиши до 1 second
и когда user stops typing and timer elapses
функция снова включается.
Есть ли какой-либо метод, который я могу вызвать для достижения того же эффекта с помощью Reactive Extensions
?
Throttle
или Timeout
продолжает вызывать действие subscriber / exception каждые 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");
}
4 ответа:
Вот весь код, чтобы показать, как код выше может быть перенесен в 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 на флажок не на самом деле делает проверку орфографии. Я надеюсь, что этого будет достаточно для аудитории, чтобы воссоздать их реальное решение.Я все же надеюсь, что это поможет.
Отличный вопрос.
Вероятно, есть много способов решить эту проблему, но вот один из них, с которым вы можете работать. Сначала нам нужна исходная наблюдаемая последовательность. Это, вероятно, событие нажатия клавиши или событие изменения свойства, которое было преобразовано в наблюдаемую последовательность с помощью FromEvent или какой-либо другой фабрики/преобразования или, возможно, ReactiveUI.В этом примере я буду использовать
Observable.Interval(TimeSpan.FromSeconds(0.25)).Take(4);
в качестве замены исходной последовательности (просто чтобы доказать концепцию).Далее, нам нужно решить, когда нам нужно отключить эту функцию (проверка орфографии?). Это когда исходная последовательность дает значение.
var disableSpellcheck = source.Select(_=>false);
Затем нам нужно решить, когда нам нужно повторно включить эту функцию. Это происходит, когда на исходную последовательность приходится 1 секунда молчания. Один трюк, который вы можете сделать, чтобы реализовать это, состоит в создании секундного таймера для каждого события из источника. При создании нового таймера отмените предыдущий. Это можно сделать, создав вложенную наблюдаемую последовательность и используя переключатель для отмены предыдущие внутренние последовательности, когда новый он произвел.
Теперь мы хотим объединить эти две последовательности и передать результат методу, который включает / выключает функцию.var enableSpellcheck = source.Select(_=>Observable.Timer(TimeSpan.FromSeconds(1))) .Switch() .Select(_=>true);
Observable.Merge(disableSpellcheck, enableSpellcheck) .Subscribe(isEnabled=>SetFlag(isEnabled));
Однако, как отмечалось выше, этот вызов SetFlag (false) каждый раз, когда исходная последовательность yileded значение. Это легко решается с помощью оператора
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"); }
Я считаю, что Этот пример с дроссельной заслонкой решает аналогичную задачу.
Так что что-то в этом роде может сработать и для вас:
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
Каждую секунду, по крайней мере в моем тестировании.