Reactive Extensions / Reactive UI Observe collection, group it, and display it
Asked Answered
P

1

5

I have a specific data set that requires storing data that is both grouped and ordered. Each row then needs to be able to recalculate it's sums whenever any individual item changes.

This is the basic look of the end result:

enter image description here

The object structure is as such:

public class MyObject : INotifyPropertyChanged
{
  public ObservableCollection<MySubObject> Objects { get; set; }
  public Decimal TotalOfAll { get; set; }

  /* Ommited INPC and other properties for brevity */
}

public class MySubObject : INotifyPropertyChanged
{
  public Decimal Value { get; set; }
  public String RowType { get; set; }

  /* Ommited INPC and other properties for brevity */
}

The view needs to bind to MyObject, then group the Objects property by it's Type property.

Now, I've already accomplished this without using reactive extensions, but it feels hackish...I would like to accomplish this by converting the Objects property of MyObject to an observable which should, in theory, allow me to update the sums whenever the Value property of MySubObject changes.

I already have the view side of things built, so that's not the issue...it's getting the RX part completed.

NOTE:

I can alternatively expose my data like this:

public class MyObject : INotifyPropertyChanged
{
  public ObservableCollection<MyRowObject> Objects { get; set; }
  public Decimal TotalOfAll { get; set; }

  /* Ommited INPC and other properties for brevity */
}

public class MyRowObject : INotifyPropertyChanged
{
  public ObservableCollection<MySubObject> Objects { get; set; }
  public String RowType { get; set; }
  public Decimal RowTotal { get; set; }

  /* Ommited INPC and other properties for brevity */
}

public class MySubObject : INotifyPropertyChanged
{
  public Decimal Value { get; set; }

  /* Ommited INPC and other properties for brevity */
}

This would take care of the grouping issue, but I still cannot get it to work

Person answered 14/8, 2013 at 18:40 Comment(1)
I have edited your title. Please see, "Should questions include “tags” in their titles?", where the consensus is "no, they should not".Bushwa
E
7

You can use ReactiveUI's reactive collection, which provides a number of observables including one for when the collection changes (Changed), and one for when the items in the collection change (ItemChanged). This can be combined with ObservableAsPropertyHelper for the dependent properties.

The totalOfAll dependent property is quite straightforward. The other dependent property - groupedObjects - is a bit more complex. I'm not sure if I've understood your grouping requirements exactly. Hopefully the code below will be a starting point - it will project the collection of subobjects into an IEnumerable of anonymous objects, each containing a total, a row header, and the items. You should be able to bind to this in your view

public class MyObject : ReactiveObject
{
    public ReactiveCollection<MySubObject> Objects { get; set; }

    private ObservableAsPropertyHelper<IEnumerable<object>> groupedObjectsHelper;
    public IEnumerable<object> GroupedObjects
    { 
        get {return groupedObjectsHelper.Value;}
    }

    private ObservableAsPropertyHelper<Decimal> totalOfAllHelper;
    public Decimal TotalOfAll 
    {
        get {return totalOfAllHelper.Value;}
    }

    public MyObject()
    {
         var whenObjectsChange=
           Observable.Merge(Objects.Changed.Select(_ => Unit.Default),
                            Objects.ItemChanged.Select(_ => Unit.Default));

         totalOfAllHelper=
           whenObjectsChange.Select(_=>Objects.Sum(o => o.Value))
                            .ToProperty(this, t => t.TotalOfAll);

         groupedObjectsHelper=
           whenObjectsChange.Select(_=>Objects
                              .GroupBy(o => o.RowType)
                              .Select(g => new {RowType=g.Key,
                                                RowTotal=g.Sum(o => o.Value),
                                                Objects=g}))
                            .ToProperty(this, t => t.GroupedObjects);

    }
}
Earthward answered 15/8, 2013 at 8:28 Comment(3)
this seems like it might do the trick...however, was ReactiveCollection deprecated or something? I can never seem to find it in the current libary, so I assume using ReactiveList instead should be fine right?Person
Yep - you're right, I just checked on github and it looks like ReactiveCollection became ReactiveList in ReactiveUI 5. I'm limited to .Net 4.0 at the mo, so unfortunately can't use latest ReactiveUI and didn't notice the change.Earthward
Ahh, that makes sense...I dug a bit for it but I couldn't seem to come up with that, so thanks for finding that cause I've been at a loss on all the documentation for a few days now lol...I'm working on implementing your answer now and it appears to be putting me on the right trackPerson

© 2022 - 2024 — McMap. All rights reserved.