Reputation: 51
I am doing something I have never tried before. I am trying to create dynamic UI and bind it to a dynamic model. In other words, my web service is going to send back a small metadata description of my UI and the raw data to bind to it. Therefore, at build time, I don't know what UI I will be constructing and I don't know what my model will be. Binding them together seems VERY difficult if not impossible.
Mvx allows me to bind UI directly to a model WITHOUT it being an MvxViewModel. However, if I bind directly to the Model returned by the web service, I lose the ability to RaisePropertyChanged() since that only comes from MvxViewModel.
Normally, I would write a ViewModel that wraps the Model and have all the wrapped setters call RaisePropertyChanged(). However, in this case, my model is dynamic so I can't wrap it with a ViewModel at compile time since I don't know what it is until runtime.
Is there some cool trick I can use to construct a ViewModel that can wrap any C# model class and send out property changed events without knowing what properties the model class has until runtime?
I just discovered the DLR and the DynamicObject which seems to be perfect, but due to Apple restrictions, it will not work on Xamarin.iOS.
Upvotes: 2
Views: 1757
Reputation: 51
I am not sure if what I finalized on supports all possible functionality, but so far, it seems to satisfy everything that I need.
I really liked the idea of writing my own IMvxSourceBindingFactoryExtension
. However, in investigating how to do that, I started playing with the functionality that already exists within MvvmCross. I already knew that MvvmCross would honor an ObservableCollection
. What I didn't know was that I could use []
in my binding expressions AND that not only would integer indexers work, but also string indexers on a Dictionary
. I discovered that MvvmCross sample code already has an implementation of ObservableDictionary
within its GIT repo. It turns out, that is all that I needed to solve my problem.
So my model contains static properties AND an ObservableDictionary<string,object>
of dynamic properties where the key is the name of the dynamic property and the value is the value of the property.
My ViewModel
wraps this model class to send out PropertyChanged
notifications on the static properties. Since the Dictionary
of dynamic properties is observable, MvvmCross already handles changes to members of that dictionary, including 2-way.
The final issue is how to bind to it in my binding expression. That is where the []
comes in. If my ObservableDictionary
property name is called UserValues
and it contains a value at key user1
, then I can 2-way bind to it by using: UserValues[user1]
and everything seems to work perfectly.
One issue I see is that I am now requiring my dynamic data source to return an ObservableDictionary
to me instead of just a Dictionary
. Is that asking too much?
Upvotes: 2
Reputation: 66882
Without teasing DynamicObject
into life on iOS, the main approaches that think of are:
You could change your webservice generation code so that it produces INotifyPropertyChanged
- I've used libraries that do this - e.g. http://stacky.codeplex.com/SourceControl/latest#trunk/source/Stacky/Entities/Answer.cs - and if you can't change the webservice code generation itself, you might still be able to wrap or pervert the generated code using some kind of t4 or other templating trick.
You could investigate some kind of code that maps the web service objects to some kind of observable collection (Kiliman has suggested this in comments)
You could look at some kind of valueconverter (or maybe valuecombiner) which does the binding - I can fairly easily imagine a valueconverter which takes a wrapped model object and a string parameter (the property name) and which uses those two together (with some reflection) to work out what to do. I'm not as sure how this one would work with nested model objects... but even that could be possible...
You could look at some kind of custom binding extension for MvvmCross. This isn't as scary as it sounds, but does require some reflection trickery - to understand what might be involved take a look at the FieldBinding plugin - https://github.com/MvvmCross/MvvmCross-Plugins/tree/master/FieldBinding
During the actual data-binding process, the plugin will be called via IMvxSourceBindingFactoryExtension
- that would be your opportunity to hook into some other custom change event
(rather than INotifyPropertyChanged
). It might take a little experimentation to get this right... especially if you have nested objects (which then require "chaining" within the binding)... but I think it should be possible to produce something this way.
Upvotes: 2