I would like to implement a map from selected objects to property entries in property editor. For example like in Visual Studio xaml editor.
The target map is something like this (or maybe using ReactiveCollection from ReactiveUI?)
Selected objects Filled categories to display in PropertyEditor
------------------------- ---------------------------------------
ObservableCollection<obj> -> ObservableCollection<Category>
The map in plain English:
- Collect all unique property types from objects
- Group by category (e.g. Text, Layout)
- Add/Remove categories as necessary to reflect selected objects
- Add/Remove properties from existing categories as necessary
The challenge is to have declarative/functional code without add/remove branches. (I do already have an imperative/event based code which is quite ugly and error prone.)
I think we could assume that Category and Property collections are sets with the usual operations: Union, Substract, and IsMember.
The inspiration is the code from ReactiveUI by Paul Betts which is beatiful for simple one-to-one map:
var Models = new ReactiveCollection<ModelClass>();
var ViewModels = Models.CreateDerivedCollection(x => new ViewModelForModelClass(x));
// Now, adding / removing Models means we
// automatically have a corresponding ViewModel
Models.Add(new Model(”Hello!”));
ViewModels.Count();
>>> 1
Using Seq and F# the straightforward nonobservable map would look like this:
selectedObjects
|> Seq.collect GetProperties |> Seq.unique |> Seq.groupBy GetPropertyCategory
|> Seq.map (fun categoryName properies -> CreateCategory(properties))
The above code is fine in theory but in practice it would recreate all the view models from scratch on each change in selected objects. What I would love to achieve with Rex is to have the version of the above map with incremental updates, so the WPF will update only the changed parts of the GUI.
This is a very interesting problem :-). Sadly, I don’t think there is any F# library that allows you to solve that easily. As far as I can tell, Bindable LINQ was one attempt to implement LINQ query pattern (i.e.
Select,SelectManyandWheremethods) forObservableCollection<T>, which is essentially what you need. To use it from F#, you could wrap LINQ operations in functions likemapetc.As you say, functions like
Seq.mapwork by onIEnumerableand so they are not incremental. What you’d need here are functions likemap,filterandcollect, but implemented overObservableCollection<'T>in such a way that they do the changes incrementally – when new element is added to the source collection, themapfunction would process it and add a new element to the resulting collection.I experimented with that a bit and here is an implementation of
mapthat is incremental:Implementing
mapwas quite easy, but I’m afraid that functions likecollectandgroupBywill be quite tricky. Anyway, here is a sample showing how you can use it: