WhenAny / ObservableForProperty how to access previous and new value? - reactiveui

Simple case:
public class Foo : ReactiveObject
{
public Foo()
{
this.ObservableForProperty(t => t.Bar, t => t).Subscribe(t =>
{
//Logic using previous and new value for Bar
}
}
private int _bar;
public int Bar
{
get { return _bar; }
set { this.RaiseAndSetIfChanged(ref _bar, value); }
}
}
In the ObservableForProperty subscription, only the new value of Bar is accessible (via t). We can call ObservableForProperty with true for the "beforeChange" parameter, and instead only have the previous value and not the new.
I know I can just plug my code logic in the property setter, but I'd like to keep the ObservableForProperty behavior (filter the first setter and when the value does not change). The property is also used in a XAML binding and require the propertyChanged trigger.
Anything I missed? How can I do that easily? Thanks

How about something like this:
public Foo()
{
this.WhenAnyValue(t => t.Bar)
.Buffer(2, 1)
.Select(buf => new { Previous = buf[0], Current = buf[1] })
.Subscribe(t => { //Logic using previous and new value for Bar });
}
Note that this will not trigger the subscription logic the first time you alter the Bar. In order to get that functionality, add .StartWith(this.Bar) before buffering.
This uses Buffer operator in overlapping mode (skip < count).

Related

Correct implementation of IGrouping to be used inside longlistselector groupheader

I have an ObservableCollection like the following-
private ObservableCollection<KeyedList<int, Anime>> _grp;
public ObservableCollection<KeyedList<int, Anime>> GroupedAnimeByGenre
{
get
{
return _grp;
}
set
{
_grp = value;
RaisePropertyChanged("GroupedAnimeByGenre");
}
}
I am using this to populate a LongListSelector with grouping. The KeyedList is implemented like this-
public class KeyedList<TKey, TItem> : List<TItem>
{
public TKey Key { protected set; get; }
public KeyedList(TKey key, IEnumerable<TItem> items)
: base(items)
{
Key = key;
}
public KeyedList(IGrouping<TKey, TItem> grouping)
: base(grouping)
{
Key = grouping.Key;
}
}
I have the following code to feed the ObservableCollection. Keep in mind AnimeList2 is a temporary Collection.
var groupFinale = AnimeList2.GroupBy(txt => txt.id).Where(grouping => grouping.Count() > 1).ToObservableCollection();
GroupedAnimeByGenre = groupFinale ;
But I am unable to convert/use groupFinale with GroupedAnimeByGenre. I am missing the extension method part as I am not well aware of the syntax. Please help
If you remove the ToObservableCollection() call and take just that part
var groupFinale = AnimeList2.GroupBy(txt => txt.id).Where(grouping => grouping.Count() > 1);
you'll see that the type of groupFinale is IEnumerable<IGrouping<int, Anime>>. Hence applying ToObservableCollection() will result in ObservableCollection<IGrouping<int, Anime>>. However, the type of the GroupedAnimeByGenre is ObservableCollection<KeyedList<int, Anime>>. So you need to convert IEnumerable<IGrouping<int, Anime>> to IEnumerable<KeyedList<int, Anime>> which in LINQ is performed by the Select method.
Shortly, you can use something like this
var groupFinale = AnimeList2
.GroupBy(txt => txt.id)
.Where(grouping => grouping.Count() > 1)
.Select(grouping => new KeyedList<int, Anime>(grouping))
.ToObservableCollection();
You can make such conversion easier by providing an extension method (similar to BCL provided ToArray() / ToList()) that will allow skipping the type arguments like this
public static class KeyedList
{
public static KeyedList<TKey, TItem> ToKeyedList<TKey, TItem>(this IGrouping<TKey, TItem> source)
{
return new KeyedList<TKey, TItem>(source);
}
}
Then you can use simply
var groupFinale = AnimeList2
.GroupBy(txt => txt.id)
.Where(grouping => grouping.Count() > 1)
.Select(grouping => grouping.ToKeyedList())
.ToObservableCollection();

How do I tell ReactiveUI to update bindings (i.e. RaiseCanExecuteChanged())?

How do I tell ReactiveUI to update bindings?
Normally, I would do something like this:
string _instructorNameInput;
public string InstructorNameInput
{
get { return _instructorNameInput; }
set
{
this.RaiseAndSetIfChanged(ref _instructorNameInput, value);
Submit.RaiseCanExecuteChanged();
}
}
However, the following isn't supported:
Submit.RaiseCanExecuteChanged();
As a result, how can I force bindings to update based on the CanExecute predicate that my command relies on?
Updated:
public partial class FormViewModel : ReactiveObject
{
public FormViewModel()
{
Submit = ReactiveCommand.Create(this.WhenAnyValue(x => x.CanSubmit));
Submit.Subscribe(x => OnSubmit());
}
bool _canExecute;
public bool CanSubmit
{
get { return !GetUnsatisfied().Any(); }
set { this.RaiseAndSetIfChanged(ref _canExecute, value); } // Need to update view based on command.CanExecute state change
}
void OnSubmit()
{
var rosterInfo = new RosterInfo(new Course(CourseInput.Name),
new Instructor(InstructorNameInput, InstructorIdInput));
var repository = GetRepository();
repository.AddCourseInfo(rosterInfo);
Publish(REQUEST_NAVIGATION_TO_SUBMITION_CONFIRMATION, rosterInfo);
}
ObservableCollection<RequiredField> GetUnsatisfied()
{
RequiredFields.Clear();
RequiredFields = Review();
return RequiredFields;
}
}
Multiple issues:
Have a read at the fundamentals on ReactiveObject, in particular how "Read-Write Properties" are written.
In your case, this.WhenAnyValue(x => x.CanSubmit) will trigger a refresh on the command whenever the property CanSubmit changes, but this one never does, because you never call the setter (and the getter has an incorrect impl).
Currently, your method GetUnsatisfied() has "polling" semantics, which mean you need something to trigger this method to update your command. This isn't reactive at all, you should instead bind/listen to updates.
If there's no way for you to make your Review() logic reactive, then you may do something like:
var canExec = Observable.Timer(TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(1))
.Select(_ => !GetUnsatisfied().Any());
Submit = ReactiveCommand.Create(canExec);
Submit.Subscribe(x => OnSubmit());
Basically, having a timer to do your polling.
(But I strongly suggest going further down the reactive way)

Understanding Get Method Overrides

I am totally new to Web API an am not understanding how to Filter get calls.
This method returns all items in my database.
// GET: api/LogEntries
public IQueryable<LogEntry> GetLogEntries()
{
return db.LogEntries;
}
This method returns a specific item in my database.
// GET: api/LogEntries/5
[ResponseType(typeof(LogEntry))]
public IHttpActionResult GetLogEntry(int id)
{
LogEntry logEntry = db.LogEntries.Find(id);
if (logEntry == null)
{
return NotFound();
}
return Ok(logEntry);
}
So now I want to filter the returned records so I created this method but it won't work because the specific item method gets called. I seem to be missing a concept and am hoping you can point me to more clear understanding. Thanks
// GET: api/LogEntries
public IQueryable<LogEntry> GetLogEntries(string levelID)
{
int levIdInt;
if (Int32.TryParse(levelID, out levIdInt))
{
return db.LogEntries.Take(300).Where(l => (int)l.Level == levIdInt).OrderByDescending(d => d.TimeStamp);
}
return db.LogEntries.Where(i => i.ID < 0);
}
You need to specify the route for that method
[Route("api/LogEntries/Level/{levelID}"]
public IQueryable<LogEntry> GetLogEntries(string levelID)
{}
More on routing is available here http://www.asp.net/web-api/overview/web-api-routing-and-actions/attribute-routing-in-web-api-2

Prism Shell buttons shared by modules

I am using Prism 2, trying to add four navigation buttons (First Record, Last Record, Previous Record, Next Record) in shell to be used by modules. I also want these buttons to be disable if active View/ViewModel does not provide these functions.
I tried using events but didn't know how to achieve my second goal regarding disabling buttons. It seems I need to check current active View/ViewModel to see if they subscribed the click event during View switch. But I think publisher should be unaware of subscriber...
Somehow I tried my own way. I create an IDocNavigation interface which has four method corresponding to my four buttons. At runtime I check modules' ViewModel if they implemented that interface or not, and change the ICommand on fly. Below is my code. I include one LastRecordCommand only:
public ShellViewModel(Views.Shell shell)
{
this.Shell = shell;
shell.DataContext = this;
shell.MainDocking.ActivePaneChanged += (s, e) =>
{
if (e.NewPane.Content is UserControl &&
((UserControl)e.NewPane.Content).DataContext is IDocumentNavigate)
{
IDocumentNavigate vm = ((UserControl)e.NewPane.Content).DataContext as IDocumentNavigate;
LastRecordCommand = new RelayCommand(x => vm.GotoLastRecord(), x => true);
}
else
{
LastRecordCommand = new RelayCommand(x => { }, x => false);
}
};
//...
I feel these are quite ugly. Creating an empty RelayCommand is also stupid. How can I improve ? or how can I achieve disabling command if event is more suitable in my case ?
You can make use of CompositeCommand in prism.
Define a globally available CompositeCommand
public static readonly CompositeCommand FirstRecord= new CompositeCommand(true);
Then in your your module view models
class Module1
{
public DelegateCommand Module1Firstrecord{ get; set; }
Module1()
{
Module1Firstrecord = new DelegateCommand(this.FirstRecord, CanExecute);
}
private void FirstRecord()
{
//do whatever you want
}
private bool CanExecute()
{
return true;
}
private void Module1_IsActiveChanged(object sender, EventArgs e)
{
//Find if your window is acive
// if it is active Module1Firstrecord.IsActive = true
//else false.
}
}
With IActiveAware you can handle the active window scenario easily. According to whether your active module have a handler for the command on not the buttons will enable/disable.

Zipping Rx IObservable with infinite number set

I have a IObservable [named rows in the sample below] from Reactive extensions framework and I want to add index numbers to each object it observes.
I've tried to implement this using Zip function:
rows.Zip(Enumerable.Range(1, int.MaxValue), (row, index) =>
new { Row = row, Index = index })
.Subscribe(a => ProcessRow(a.Row, a.Index), () => Completed());
.. but unfortunately this throws
ArgumentOutOfRangeException:
Specified argument was out of the range of valid values.
Parameter name: disposables
Am I understanding the Zip function wrong or is there a problem with my code?
The Range part of the code doesn't seem to be the problem and the IObservable isn't yet receiving any events.
.Select has an overload to include the index:
rows.Select((row, index) => new { row, index });
Apparently, Zip extension methods converts the original custom IObservable to an anonymous observable and Subscribing to it creates an System.Collections.Generic.AnonymousObserver, which doesn't implement IDisposable.
Thus, you cannot implement the Subscribe method the normal way (atleast the way I've seen it used), which is
public IDisposable Subscribe(IObserver<T> observer) {
// ..add to observer list..
return observer as IDisposable
}
More likely the correct answer would be:
return Disposable.Create(() => Observers.Remove(observer));
You should though note that the collction will probably be modified durin Completed-method, so create a copy of the list before processing them:
public void Completed()
{
foreach (var observer in Observers.ToList())
{
observer.OnCompleted();
}
}
I am not sure what your problem is, does this work for you (and what's missing here that you are doing?):
static void Main(string[] args)
{
var rows = new List<int> { 4,5,1,2,5 }.ToObservable();
rows.Zip(Enumerable.Range(1, int.MaxValue), (row, index) =>
new { Row = row, Index = index })
.Subscribe(a => ProcessRow(a.Row, a.Index), () => Completed());
Console.ReadLine();
}
static void ProcessRow(int row, int index) {
Console.WriteLine("Row {0}, Index {1}", row, index);
}
static void Completed() {
}

Resources