Има ли начин да чуете, че няма събития, които се повдигат в Reactive Extensions?

Имам изискване да изключа определена функция, когато потребителят започне да въвежда, което е просто. И когато потребителят спре да въвежда, искам да включа функцията отново.

Без реактивни разширения човек може просто да приложи тази функция с 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");
}

person 123 456 789 0    schedule 20.05.2014    source източник


Отговори (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 ще изтегли всичко останало, необходимо за кода. Това е .NET 4.5.

Това е пример, който показва как да решите проблема. т.е. не препоръчвам използването на .ObserveOnDispatcher(), не препоръчвам писане на код отзад и знам, че настройката IsEnabled в квадратче за отметка не е всъщност извършване на проверка на правописа. Надявам се, че това е достатъчно за публиката, за да пресъздаде тяхното действително решение.

Все пак се надявам да помогне.

person Lee Campbell    schedule 22.05.2014
comment
Благодаря за помощта, наистина проработи! Защо не препоръчвате да използвате ObserveOnDispatcher? Ами ако това е персонализирана контрола, която трябва да се използва, MVVM е напълно различна употреба за персонализирана контрола, тъй като SpellCheck е прикачено свойство, което не притежавам или не трябва да бъде в ViewModel. - person 123 456 789 0; 22.05.2014
comment
Бих предложил да използвате ObserveOn(IScheduler) и да подадете dispatcherScheduler (за тестване). Вижте introtorx.com/Content/v1.0.10621.0/16_TestingRx.html #TestingRx. След това бих използвал MVVM, имам свойство, наречено SuppressSpellCheck и бих го свързал към вашето AttachedProperty. Сега Rx заявката по-горе просто превключва това, за да активира/деактивира вашата проверка на правописа. Ако не тествате Unit, игнорирайте всичко това :-) - person Lee Campbell; 22.05.2014

Страхотен въпрос.

Вероятно има много начини за решаване на това, но ето един, с който можете да работите. Първо имаме нужда от наблюдаемата последователност на източника. Това вероятно е събитие с натискане на клавиш или събитие, променено на свойството, което е преобразувано в последователност за наблюдение с помощта на FromEvent или някаква друга фабрика/преобразуване или може би ReactiveUI.

В този пример ще използвам 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));

Въпреки това, както беше отбелязано по-горе, това извикване 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");
}
person Lee Campbell    schedule 21.05.2014
comment
Здравей Лий, изпробвах твоя код и замених Observable.Interval с Keypress. Не получавам желания резултат, но когато потребителят започне да въвежда отново, той не се публикува на абонатите, за да активира или деактивира повторно проверката на правописа. Направи го само веднъж (активиране/деактивиране) и това е всичко - person 123 456 789 0; 21.05.2014
comment
Актуализирах кода, за да докажа, че работи, когато има поредица от събития в бърза последователност, известно мълчание, след това още събития в бърза последователност. Ако покажете кода, който всъщност използвате, мога да посоча грешката. - person Lee Campbell; 22.05.2014
comment

Вашият псевдоним трябва да бъде във вашия .profile файл, а не във вашия скрипт, ако го извиквате при подканата.

Ако поставите псевдоним във вашия скрипт, тогава трябва да го извикате в рамките на вашия скрипт.

Източникът на файла е правилният отговор, когато се опитвате да стартирате скрипт, който вътре има псевдоним.

source yourscript.sh
- person Lee Campbell; 22.05.2014
comment
Това е WPF приложение, което има RichTextBox, наречено meh, и току-що промених това, което казахте на KeyPress. Така че почти ще изглежда по същия начин като вашата проба. Това е действителният код, който използвам:) Актуализирах го с XAML - person 123 456 789 0; 22.05.2014
comment
Добре. Явно прекъсване на комуникацията ;-). Моят пример беше скрипт на LinqPad, който се надявах да можете да видите, че емулира писане чрез смесване на събития с slience. По-долу съм публикувал пълно решение за вас. - person Lee Campbell; 22.05.2014

Вярвам в този пример с Throttle решава подобен проблем.

Така че нещо по този начин може да работи и за вас:

var throttled = observable.Throttle(TimeSpan.FromMilliseconds(1000));
using (throttled.Subscribe(x => RestoreThing())) {}
person Gábor Bakos    schedule 20.05.2014
comment
Да кажем, че това е TextChanged, където изключвам SpellCheck, когато потребителят започне да въвежда и ако потребителят не е въвел нищо, тогава трябва да включа SpellCheck обратно. Не мисля, че само с Throttle ще постигна това, което искам. Просто ще изключи функцията, но трябва да я включа отново - person 123 456 789 0; 20.05.2014
comment
Освен това ще продължи да стреля на всяка 1 секунда, което не искам. Искам само последното. - person 123 456 789 0; 20.05.2014
comment
@lll Мисля, че ако намалите същия TextChanged и се абонирате за него с включване на SpellCheck, ще работи както се очаква. - person Gábor Bakos; 20.05.2014
comment
Има ли начин да направи нещо като ›---------|(спиране на събитие)--|1000---› ms публикуване на абонати. - person 123 456 789 0; 20.05.2014
comment
Мислех, че искате да задействате събитието всеки път, когато писането спре. Throttle не трябва да задейства събитието за всяка 1 секунда, той някак си филтрира и чака събитията, без да генерира нови събития. Коя версия на Rx използвате? - person Gábor Bakos; 20.05.2014
comment
Дроселът ще продължи да публикува на абонатите. Използвам най-новата версия на Rx. Продължава да стреля, ако задържа натиснат клавиш, на всяка 1 секунда публикува - person 123 456 789 0; 20.05.2014
comment
Трябва да призная, че съм невеж. Бихте ли споделили как се опитвате да го използвате? (Това изображение с версия на Java предполага, че се задейства след определен интервал от време и само последният елемент от този интервал: github.com/Netflix/RxJava/wiki/) The Distinct/DistinctUntilChanged (introtorx.com/content/v1.0.10621.0/05_Filtering.html#Distinct) може да реши проблема с повтарящите се събития. - person Gábor Bakos; 20.05.2014
comment
Distinct ще продължи да слуша и да задейства всеки даден момент, това е като Event.Given(1 second).Last().Subscribe() това е нещо, което искам :P - person 123 456 789 0; 20.05.2014

Не съм майстор на 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 всяка секунда, поне при моето тестване.

person Sven Grosen    schedule 20.05.2014
comment
Означава ли това, докато наблюдава, продължава да извиква UpdateEnableStatus(false)? - person 123 456 789 0; 20.05.2014
comment
Не, както казах в отговора си, не видях доказателства за UpdateEnableStatus да бъде удрян всяка секунда (седях там с точка на прекъсване върху него) - person Sven Grosen; 20.05.2014
comment
Изпробвах вашето решение, то продължава да натиска Do(_ =› ...). На всяка 1 секунда. :с - person 123 456 789 0; 20.05.2014
comment
@lll как вашето приложение се различава от моето примерно приложение? - person Sven Grosen; 20.05.2014
comment
Използвам WPF, опитайте също да активирате SpellCheck, когато потребителят спре да въвежда. И обратно - person 123 456 789 0; 20.05.2014
comment
@lll Ще го пробвам с WPF - person Sven Grosen; 20.05.2014
comment
@lll Съставено в WPF, същото поведение, както видях преди. UpdateEnabledStatus не се удря всяка секунда. Има ли някаква друга подробност от вашето приложение, която е подходяща тук? - person Sven Grosen; 20.05.2014
comment
Продължете да натискате клавиш и не го пускайте. Той продължава да изстрелва на наблюдателите UpdateEnableStatus - person 123 456 789 0; 20.05.2014
comment
@lll Не знам как да обясня тази бръчка, ако това поведение е толкова вредно за вашето приложение, тогава ще трябва да се надявате, че някой, който знае повече за RX, ще отговори - person Sven Grosen; 20.05.2014