How can I bind to an Entry's "focused" event with Reactive? - c#

I'm trying to bind to an text Entry fields "focused" event using reactive but my code is failing to compile.
Here's what I'm doing now, which works fine:
Entry _qty; // at class level
_qty.Focused += (s, e) => { /* do stuff */ };
Attempt
But I'd like to do something like this instead:
// class level
IObservable<string> _qtyFocusObservable;
Entry _qty;
// in a setup function
_qtyFocusObservable =
Observable
.FromEventPattern<EventHandler<FocusEventArgs>>(
x => _qty.Focused += x,
x => _qty.Focused -= x
);
Problem
I've tried quite a few variations of the code above and I get compiler errors saying that the compiler can't implicitly convert from whatever type I specify to System.EventHandler<System.EventHandler<Xamarin.Forms.FocusEventArgs>>, even if the type I specify is indeed System.EventHandler<System.EventHandler<Xamarin.Forms.FocusEventArgs>>.
Question
How do I bind to my Entry's Focused event using reactive?

So to get a basic observable working from an event I usually structure like so:
var focusObservable = Observable.FromEventPattern<EventHandler, FocusEventArgs>(
x => _qty.Focused += x.Invoke,
x => _qty.Focused -= x.Invoke);
Then when I need to do something from that observable event I link a command to it like so:
var doStuffCommand = ReactiveCommand.CreateAsyncTask(DoStuffAsync);
focusObservable.InvokeCommand(doStuffCommand);
With a DoStuffAsync implementation of something like this:
public async Task DoStuffAsync(object value, CancellationToken token = default(CancellationToken))
{
// Do stuff here
}
I'm still fairly new to Reactive as well but this (should?) get you going in the right direction.
Cheers, and happy coding!

So, after a year of using ReactiveUI, this is how I fire an event when focusing an input.
var focusedObservable =
Observable
.FromEventPattern<FocusEventArgs>(
x => _totalBirds.Focused += x,
x => _totalBirds.Focused -= x)
.Select(x => x.EventArgs.IsFocused);
// fires when focused
focusedObservable
.WhenIsTrue() // extension method, basically .Where(x => x == true)
.ObserveOn(RxApp.MainThreadScheduler)
.InvokeCommand(this, x => DoSomething)
.DisposeWith(ControlBindings); // extension that uses composite disposable
// fires when changing state back to unfocused
focusedObservable
.WhenIsFalse() // extension method, basically .Where(x => x == false)
.ObserveOn(RxApp.MainThreadScheduler)
.InvokeCommand(this, x => x.ViewModel.DoSomethingElse)
.DisposeWith(ControlBindings); // extension that uses composite disposable
This is pretty straight forward, if you need to see any additional code, let me know. Also, if you want to snag the .DisposeWith extension you can grab it here.

Related

Take(1) from a Select() on a hot observable

What I'm trying to do is have a hot observable, and then derive another observable from it through Select.
Next I want to use await Take(1) to get a single value from the derived observable and then subsequently subscribe to it.
int i = 1;
var o1 = Observable
.Interval(TimeSpan.FromSeconds(1))
.Select(x => { i++;return i; })
.Publish()
.RefCount()
.Do(x => Console.WriteLine($"emit {x}"));
var o2 = o1.Select(x => x + 5);
await o2.Take(1);
Console.ReadLine();
using (o2.Subscribe(x =>
{
Console.WriteLine($"output {x}");
}))
{
Console.WriteLine($"subscrbied");
Console.ReadLine();
}
Console.ReadLine();
However, what I'm seeing is that after the await Take(1), the observable no longer "works" (no "emit x" is being printed anymore).
Why is this?
EDIT
Interestingly, if I add a Task.Delay it works:
var o2 = o1.Select(x => x + 5);
await o2.Take(1);
await Task.Delay(1);
Console.ReadLine();
The combination .Publish().RefCount() can be a bit nasty to work with. There are occasions when the subscribers go to zero that the query cannot be resubscribed to.
However, in this case there seems to be a race condition that I've yet to fully figure out.
Here's how to make your code work:
await o2.Take(1).ObserveOn(Scheduler.Default);
The addition of the ObserveOn allows it to operate the way you expect.
Your use of await Task.Delay(1); did the same thing. But why is still confusing me.
Finally, ignoring the kludge to make it work, the only reason it appears to work is that you're using the external state int i = 1;. You can remove the .Publish().RefCount() and it'll still work as expected. You should avoid this kind of external state and if you do use it you should use Interlocked.Increment(ref i) instead of i++.
Based on our discussions, here's an alternative way to get what you need:
var subject = new ReplaySubject<long>(1);
var source = Observable.Interval(TimeSpan.FromSeconds(1.0));
var subscription = source.Subscribe(subject);
Thread.Sleep(TimeSpan.FromSeconds(4.5));
var z = await subject.Take(1);
Console.WriteLine($"0:{z}z");
Thread.Sleep(TimeSpan.FromSeconds(2.5));
subject.Take(5).Subscribe(x => Console.WriteLine($"1:{x}x"));
Thread.Sleep(TimeSpan.FromSeconds(2.5));
subject.Take(5).Subscribe(x => Console.WriteLine($"2:{x}x"));

Rx sequential groupBy (partition stream)

I have a stream of events:
event.EventTime: 1s-----2s----3s----4s----5s----6s---
stream: A-B-C--D-----------------E-F---G-H--
An event looks like this:
public class Event
{
public DateTime EventTime { get; set; }
public int Value { get; set; }
}
EventTime should correspond to a time at which the event arrives, but there can be a small delay. The events are not supposed to arrive out-of-order, though.
Now, when I specify an grouping interval, say 1 second, I expect the stream to be grouped like this
1s-------2s----3s----4s----5s-----6s---
[A-B-C]--[D]---[ ]---[ ]---[E-F]--[G-H]
(notice the empty intervals)
I have tried using Buffer, but sadly I need to partition by EventTime, not System.DateTime.Now. Even with boundaries, I'd need some kind of look-ahead since when I use Buffer(2,1) as boundary and compare [0] and [1], even though [1] succesfully breaks the buffer, it still gets inserted into the old one instead of the new one. I also tried GroupBy, but that yielded groups only after the input stream finished. Which should never happen. Then I tried some this thing:
var intervalStart = GetIntervalStartLocal(DateTime.Now) + intervalLength;
var intervals = Observable.Timer(intervalStart, intervalLength);
var eventsAsObservables = intervals.GroupJoin<long, Event, long, Event, (DateTime, IObservable<Event>)>(
data,
_ => Observable.Never<long>(),
_ => Observable.Never<Event>(),
(intervalNumber, events) => {
var currentIntervalStart = intervalStart + intervalNumber*intervalLength;
var eventsInInterval = events
.SkipWhile(e => GetIntervalStartLocal(e.EventTime) < currentIntervalStart)
.TakeWhile(e => GetIntervalStartLocal(e.EventTime) == currentIntervalStart);
return (currentIntervalStart, eventsInInterval);
});
var eventsForIntervalsAsObservables = eventsAsObservables.SelectMany(g => {
var lists = g.Item2.Aggregate(new List<Event>(), (es, e) => { es.Add(e); return es; });
return lists.Select(l => (intervalStart: g.Item1, events: l));
});
var task = eventsForIntervalsAsObservables.ForEachAsync(es => System.Console.WriteLine(
$"=[{es.intervalStart.TimeOfDay}]= " + string.Join("; ", es.events.Select(e => e.EventTime.TimeOfDay))));
await task;
I was thinking that I'd use GroupJoin which joins based on values. So first, I'll emit interval timestamps. Then, inside GroupJoins resultSelector, I'll compute a matching interval from each Event, using GetIntervalStartLocal function (truncates the date to an interval length). After that, I'll skip all the potential leftovers from a previous interval (SkipWhile expected interval is higher then actually computed from Event). Finally, I'll TakeWhile event computed interval matches expected.
However, there must be a problem before I even get to SkipWhile and TakeWhile, because resultSelector actually does not operate on all data from data, but ignores some, e.g. like this:
event.EventTime: 1s-----2s----3s----4s----5s----6s---
stream: A---C--D-------------------F-----H--
and then constructs (from what it operates on, correctly):
1s-----2s----3s----4s----5s---6s---
[A-C]--[D]---[ ]---[ ]---[F]--[H]--
I think I must be doing something terribly wrong here, because it shouldn't be that hard to do partitioning on a stream based on a stream event value.
You need to clarify what you want. Given this:
time : 1s-------2s----3s----4s----5s-----6s---
stream: A-B-C----D-----------------E-F----G-H-- (actual)
group : [A-B-C]--[D]---[ ]---[ ]---[E-F]--[G-H] (desired result)
It's not clear whether 'time' here is your event time-stamp, or actual time. If it's actual time, then that is of course impossible: You can't pass a list of ABC before C has arrived. If you're referring to your event time-stamp, then Buffer or perhaps Window will have to know when to stop, which isn't that easy to do.
GroupBy does work for me as follows:
var sampleSource = Observable.Interval(TimeSpan.FromMilliseconds(400))
.Timestamp()
.Select(t => new Event { EventTime = t.Timestamp.DateTime, Value = (int)t.Value });
sampleSource
.GroupBy(e => e.EventTime.Ticks / 10000000) //10M ticks per second
.Dump(); //LinqPad
The only problem with this is that each group doesn't have a close criteria, so it's a giant memory leak. So you can add a timer to close the groups:
sampleSource
.GroupBy(e => e.EventTime.Ticks / 10000000) //10M ticks per second
.Select(g => g.TakeUntil(Observable.Timer(TimeSpan.FromSeconds(2)))) //group closes 2 seconds after opening
.Dump(); //LinqPad
This closing also allows us to return lists with .ToList(), rather than Observables:
sampleSource
.GroupBy(e => e.EventTime.Ticks / 10000000) //10M ticks per second
.SelectMany(g => g.TakeUntil(Observable.Timer(TimeSpan.FromSeconds(2))).ToList())
.Dump(); //LinqPad

What is the functional way to properly set a dependent predicate for Observable sequence without side effect?

I have three observables oGotFocusOrDocumentSaved, oGotFocus and oLostFocus. I would like oGotFocusOrDocumentSaved to push sequences only when _active is true. My implementation below works as needed, but it introduces a side-effect on _active. Is there anyway to remove side-effect but still get the same functionality?
class TestClass
{
private bool _active = true;
public TestClass(..)
{
...
var oLostFocus = Observable
.FromEventPattern<EventArgs>(_view, "LostFocus")
.Throttle(TimeSpan.FromMilliseconds(500));
var oGotFocus = Observable
.FromEventPattern<EventArgs>(_view, "GotFocus")
.Throttle(TimeSpan.FromMilliseconds(500));
var oGotFocusOrDocumentSaved = oDocumentSaved // some other observable
.Merge<CustomEvtArgs>(oGotFocus)
.Where(_ => _active)
.Publish();
var lostFocusDisposable = oLostFocus.Subscribe(_ => _active = false);
var gotFocusDisposable = oGotFocus.Subscribe(_ => _active = true);
// use case
oGotFocusOrDocumentSaved.Subscribe(x => DoSomethingWith(x));
...
}
...
}
It does sound like you really want a oDocumentSavedWhenHasFocus rather than a oGotFocusOrDocumentSaved observable.
So try using the .Switch() operator, like this:
var oDocumentSavedWhenHasFocus =
oGotFocus
.Select(x => oDocumentSaved.TakeUntil(oLostFocus))
.Switch();
This should be fairly obvious as to how it works, once you know how .Switch() works.
A combination of SelectMany and TakeUntil should get you where you need to be.
from g in oGotFocus
from d in oDocumentSaved
.Merge<CustomEvtArgs>(oGotFocus)
.TakeUntil(oLostFocus)
It seems that you want to be notified when the document is saved, but only if the document currently has focus. Correct? (And you also want to be notified when the document gets focus, but that can easily be merged in later.)
Think in terms of windows instead of point events; i.e., join by coincidence.
Your requirement can be represented as a Join query whereby document saves are joined to focus windows, thus yielding notifications only when both overlap; i.e., when both are "active".
var oGotFocusOrDocumentSaved =
(from saved in oDocumentSaved
join focused in oGotFocus
on Observable.Empty<CustomEventArgs>() // oDocumentSave has no duration
equals oLostFocus // oGotFocus duration lasts until oLostFocus
select saved)
.Merge(oGotFocus);

Search on TextChanged with Reactive Extensions

I was trying to implement instant search on a database table with 10000+ records.
The search starts when the text inside the search text box changes, when the search box becomes empty I want to call a different method that loads all the data.
Also if the user changes the search string while results for another search are being loaded, then the loading of the those results should stop in favor of the new search.
I implemented it like the following code, but I was wondering if there is a better or cleaner way to do it using Rx (Reactive Extension) operators, I feel that creating a second observable inside the subscribe method of the first observable is more imperative than declarative, and the same for that if statement.
var searchStream = Observable.FromEventPattern(s => txtSearch.TextChanged += s, s => txtSearch.TextChanged -= s)
.Throttle(TimeSpan.FromMilliseconds(300))
.Select(evt =>
{
var txtbox = evt.Sender as TextBox;
return txtbox.Text;
}
);
searchStream
.DistinctUntilChanged()
.ObserveOn(SynchronizationContext.Current)
.Subscribe(searchTerm =>
{
this.parties.Clear();
this.partyBindingSource.ResetBindings(false);
long partyCount;
var foundParties = string.IsNullOrEmpty(searchTerm) ? partyRepository.GetAll(out partyCount) : partyRepository.SearchByNameAndNotes(searchTerm);
foundParties
.ToObservable(Scheduler.Default)
.TakeUntil(searchStream)
.Buffer(500)
.ObserveOn(SynchronizationContext.Current)
.Subscribe(searchResults =>
{
this.parties.AddRange(searchResults);
this.partyBindingSource.ResetBindings(false);
}
, innerEx =>
{
}
, () => { }
);
}
, ex =>
{
}
, () =>
{
}
);
The SearchByNameAndNotes method just returns an IEnumerable<Party> using SQLite by reading data from a data reader.
I think you want something like this. EDIT: From your comments, I see you have a synchronous repository API - I'll leave the asynchronous version in, and add a synchronous version afterwards. Notes inline:
Asynchronous Repository Version
An asynchronous repository interface could be something like this:
public interface IPartyRepository
{
Task<IEnumerable<Party>> GetAllAsync(out long partyCount);
Task<IEnumerable<Party>> SearchByNameAndNotesAsync(string searchTerm);
}
Then I refactor the query as:
var searchStream = Observable.FromEventPattern(
s => txtSearch.TextChanged += s,
s => txtSearch.TextChanged -= s)
.Select(evt => txtSearch.Text) // better to select on the UI thread
.Throttle(TimeSpan.FromMilliseconds(300))
.DistinctUntilChanged()
// placement of this is important to avoid races updating the UI
.ObserveOn(SynchronizationContext.Current)
.Do(_ =>
{
// I like to use Do to make in-stream side-effects explicit
this.parties.Clear();
this.partyBindingSource.ResetBindings(false);
})
// This is "the money" part of the answer:
// Don't subscribe, just project the search term
// into the query...
.Select(searchTerm =>
{
long partyCount;
var foundParties = string.IsNullOrEmpty(searchTerm)
? partyRepository.GetAllAsync(out partyCount)
: partyRepository.SearchByNameAndNotesAsync(searchTerm);
// I assume the intention of the Buffer was to load
// the data into the UI in batches. If so, you can use Buffer from nuget
// package Ix-Main like this to get IEnumerable<T> batched up
// without splitting it up into unit sized pieces first
return foundParties
// this ToObs gets us into the monad
// and returns IObservable<IEnumerable<Party>>
.ToObservable()
// the ToObs here gets us into the monad from
// the IEnum<IList<Party>> returned by Buffer
// and the SelectMany flattens so the output
// is IObservable<IList<Party>>
.SelectMany(x => x.Buffer(500).ToObservable())
// placement of this is again important to avoid races updating the UI
// erroneously putting it after the Switch is a very common bug
.ObserveOn(SynchronizationContext.Current);
})
// At this point we have IObservable<IObservable<IList<Party>>
// Switch flattens and returns the most recent inner IObservable,
// cancelling any previous pending set of batched results
// superceded due to a textbox change
// i.e. the previous inner IObservable<...> if it was incomplete
// - it's the equivalent of your TakeUntil, but a bit neater
.Switch()
.Subscribe(searchResults =>
{
this.parties.AddRange(searchResults);
this.partyBindingSource.ResetBindings(false);
},
ex => { },
() => { });
Synchronous Repository Version
An synchronous repository interface could be something like this:
public interface IPartyRepository
{
IEnumerable<Party> GetAll(out long partyCount);
IEnumerable<Party> SearchByNameAndNotes(string searchTerm);
}
Personally, I don't recommend a repository interface be synchronous like this. Why? It is typically going to do IO, so you will wastefully block a thread.
You might say the client could call from a background thread, or you could wrap their call in a task - but this is not the right way to go I think.
The client doesn't "know" you are going to block; it's not expressed in the contract
It should be the repository that handles the asynchronous aspect of the implementation - after all, how this is best achieved will only be known best by the repository implementer.
Anyway, accepting the above, one way to implement is like this (of course it's mostly similar to the async version so I've only annotated the differences):
var searchStream = Observable.FromEventPattern(
s => txtSearch.TextChanged += s,
s => txtSearch.TextChanged -= s)
.Select(evt => txtSearch.Text)
.Throttle(TimeSpan.FromMilliseconds(300))
.DistinctUntilChanged()
.ObserveOn(SynchronizationContext.Current)
.Do(_ =>
{
this.parties.Clear();
this.partyBindingSource.ResetBindings(false);
})
.Select(searchTerm =>
// Here we wrap the synchronous repository into an
// async call. Note it's simply not enough to call
// ToObservable(Scheduler.Default) on the enumerable
// because this can actually still block up to the point that the
// first result is yielded. Doing as we have here,
// we guarantee the UI stays responsive
Observable.Start(() =>
{
long partyCount;
var foundParties = string.IsNullOrEmpty(searchTerm)
? partyRepository.GetAll(out partyCount)
: partyRepository.SearchByNameAndNotes(searchTerm);
return foundParties;
}) // Note you can supply a scheduler, default is Scheduler.Default
.SelectMany(x => x.Buffer(500).ToObservable())
.ObserveOn(SynchronizationContext.Current))
.Switch()
.Subscribe(searchResults =>
{
this.parties.AddRange(searchResults);
this.partyBindingSource.ResetBindings(false);
},
ex => { },
() => { });

How do I refactor methods with different signatures but almost identical bodies?

Given this private method:
private static IEnumerable<LedgerSummary> FilterLedgers(IList<LedgerSummary> ledgers, List<ExcludedLedgerAccount> excludedLedgerAccounts)
{
var excludedLedgerEntries = ledgers.Where(x => excludedLedgerAccounts.Any(y => y.LedgerAccount == x.LedgerAccount)).ToList();
var filteredLedgers = ledgers.Except(excludedLedgerEntries).ToList();
// do some more filtering
return filteredLedgers;
}
and this one:
private static IEnumerable<LedgerPosting> FilterLedgers(IList<LedgerPosting> ledgers, List<ExcludedLedgerAccount> excludedLedgerAccounts)
{
var excludedLedgerEntries = ledgers.Where(x => excludedLedgerAccounts.Any(y => y.LedgerAccount == x.DistributionAccountLedgerAccount)).ToList();
var filteredLedgers = ledgers.Except(excludedLedgerEntries).ToList();
// do some more filtering
return filteredLedgers;
}
what's the best way to refactor them so that I'm not repeating what is essentially the same logic in the method bodies?
The reason why it's not so straightforward (in my mind) is because the method signatures are slightly different in that one's taking (and returning) a 'List' of LedgerSummarys and the other LedgerPostingss, and each of these have different property names (that map to the same properties in ExcludedLedgerAccount).
Unfortunately, I can't change the properties in either of those two classes or to use a common interface, for example, for reasons that would lengthen this discourse!
I know the answer's really straightforward (so I apologise in advance) but I seem to be having a programmer's block at the minute.
Try something like this (don't have a VS at hand now, might require a bit of debugging)
private static IEnumerable<T> FilterLedgers<T> FilterLedgers(
IList<T> ledgers,
List<ExcludedLedgerAccount> excludedLedgerAccounts,
Func<T, ExcludedLedgerAccount, bool> selector)
{
var excludedLedgerEntries = ledgers.Where(x => excludedLedgerAccounts.Any(y => selector(x, y)).ToList();
var filteredLedgers = ledgers.Except(excludedLedgerEntries).ToList();
// do some more filtering
return filteredLedgers;
}
And use it:
IEnumerable<LedgerSummary> result = FilterLedgers<LedgerSummary>(input, exclude, (i, e) => i.LedgerAccount == e.LedgerAccount);
IEnumerable<LedgerPosting> result = FilterLedgers<LedgerSummary>(input, exclude, (i, e) => i.LedgerAccount == e.DistributionAccountLedgerAccount);
You can extract a predicate that you're using in the Any extension method:
private static IEnumerable<LedgerSummary> FilterLedgersImpl(IEnumerable<LedgerSummary> ledgers, Func<LedgerSummary, LedgerAccount, bool> predicate)
{
var excludedLedgerEntries =
ledgers
.Where(x => excludedLedgerAccounts.Any(y => predicate(x, y)))
.ToList();
var filteredLedgers = ledgers.Except(excludedLedgerEntries).ToList();
// do some more filtering
return filteredLedgers;
}
And then you can use this helper method directly:
var list1 = FilderLedgersImpl(ledgers, (x, y) => y.LedgerAccount == x.LedgerAccount);
var list2 = FilderLedgersImpl(ledgers, (x, y) => y.LedgerAccount == x.DistributionAccountLedgerAccount);
Unless LedgerSummary and LedgerPosting share a common base class that does all the work you need (in which case you could use generics), you can't do much with this.
In C++, you could use templates (or even typedefs) for a nice solution but not in C# or java... unless you wanted to use a code generator.

Categories