I noticed that in v3, MvvmCross removed the generic declaration <TViewModel>
on MvxTouchViewController
and renamed it to MvxViewController
.
This means the ViewModel
property is typed as a generic interface of IMvxViewModel
rather than the specific TViewModel
.
If I need to access the TViewModel
in my view controller, is there a convenient way of getting the ViewModel already cast to the specific instance type for this View? Or do I have to cast it myself every time?
The previous Generic-based MvvmCross Views were removed from MvvmCross mainly because of the threat of 'Heizenbugs' in the objective-C based platforms.
For what little anyone knows about Heizenbugs
, see http://forums.xamarin.com/discussion/771/exporting-generic-type-to-objc-supported
I don't believe I ever saw an Heizenbug, but Xamarin were very clear in their advice to avoid them at all costs - for example, they twice changed the compiler to issue errors for our generics. Indeed, on .Mac such generic code remains an error today, while on .iOS it's just a very scary warning.
Added to this, we did also encounter some issues with Xaml based platforms when inheriting from Generic base classes - although these were mainly resolved (e.g. XamlParseException when I inherit a Page from a Generic base class)
(Aside - to allow some backwards compatibility, WindowsPhone does still have some limited generic View support, but this is marked as Obsolete
and I do regret allowing this to live on...)
The good news is that, in my experience, the majority of Views do not need to know their ViewModel type - instead, the majority of Views can be built with 'pure bindings' without declaring a typed ViewModel.
For those remaining Views which do need to know their ViewModel type, then a simple added property quickly adds this - e.g.:
protected MyViewModel MyViewModel
{
get { return (MyViewModel)base.ViewModel; }
/* set is optional - not typically needed
set { base.ViewModel = value; }
*/
}
Alternatively you can probably write an extension method for this if you want to - e.g. something like:
public static TViewModel TypedViewModel<TViewModel>(this IMvxView view) where TViewModel : class, IMvxViewModel
{
return view.ViewModel as TViewModel;
}
Very alternatively....
.... If you are not scared of ghosts, goblins or Heizenbugs
....
One way to add the TypedViewModel
property to all of your views would be to add generics back into your view hierarchy - this is easy for you to do - e.g. in Android adding
public class BaseActivity<TViewModel> : MvxActivity
where TViewModel : class, IMvxViewModel
{
protected TViewModel TypedViewModel
{
get { return (TViewModel)base.ViewModel; }
/* set is optional - not typically needed
set { base.ViewModel = value; }
*/
}
}
This should work fine for you... but if you hit an Heizenbug, then I don't think anyone will be able to assist you. Xamarin have been very clear in recommending against this pattern - especially on the objC based platforms.