Dynamic Loading of xaml

May 17, 2011 at 10:21 AM

i want to enable the loading of parts of a UI from Xaml files at runtime.

The viewmodel will somehow return the xml needed for the UI when its loaded from the View.

What would be the best way to keep this MVVM ?

How can i use the View directy from the ViewModel?

I was thinking about using a Service to dynamicly load Xaml, so the ViewModel calls this Injected service with (Stream dynamicXml, Object view);

And it will handle the rest, but how to get the view reference from the viewmodel?

The loaded usercontrols should be databounded to the viewmodel behind the view.

However this command can be the same for all action by the dynamic buttons, i should be able to add parameters to the Command call in the usercontrol so that in the VM i get something like ExecuteDynamicCommand(string commandName)

May 18, 2011 at 12:09 PM
Edited May 18, 2011 at 12:18 PM

Mmmm I am not sure this would work to be honest as CinchV2 is built around the idea of using View 1st and then via an attached DP resolving the ViewModel which MUST exist in the MEF container, but since you are loading viewmodel from database (or wherever) you will not be able to use the view 1st approach. And I imagine your serialization process will rely on a default constructor for the ViewModel that is needs to create for the read XAML.

 

What you could do is new up the view then deserialize the XAML into a ViewModel of a known type and then call some method on the ViewModel where you pass in the View wrapped up in either a ViewAwareStatus or ViewAwareStatusWindow service which you can obtain from the MEF container like this

 

So you would do something like

 

View someView = new SomeView();

SomeViewModel vm = (SomeViewModel) readFromXamlStream();

IViewAwareStatus viewAwareStatus  =   ViewModelRepository.Instance.Resolver.Container.GetExport<IViewAwareStatus>().Value;

viewAwareStatus.InjectContext(someView);

vm.SetContext(viewAwareStatus);
someView.DataContext = vm;

 



Where the SomeViewMode.InjectContext() method would look like this in the dynamically read ViewModel


void InjectContext(IViewAwareStatus viewAwareStatus)
{
   this.viewAwareStatus = viewAwareStatus;
}



This would allow dynamic ViewModel to know about the view lifecyle events such as Loaded/UnLoaded etc etc and also the other things the viewAwareStatus service exposes.

NOTE : If you want your dynamic ViewModel to use other common Cinch services you could change the dynamic ViewModel to have method something like this


void InjectServices(List<object> mefAvailableServices)
{
   this.viewAwareStatus = mefAvailableServices[0];
   this.messageBoxService = mefAvailableServices[1];
}



Which you might call like this


View someView = new SomeView();
SomeViewModel vm = (SomeViewModel) readFromXamlStream();
IViewAwareStatus viewAwareStatus  =   ViewModelRepository.Instance.Resolver.Container.GetExport<IViewAwareStatus>().Value;
IMessageBoxService messageBoxService  =   ViewModelRepository.Instance.Resolver.Container.GetExport<IMessageBoxService>().Value;
viewAwareStatus.InjectContext(someView);
vm.InjectServices(new List<Object> { viewAwareStatus, messageBoxService});
someView.DataContext = vm;

 

There is also a MEF extension for the Desktop which allows you to satisy imports for single part by using the extension something like this (where you might have this in the ViewModel and you would have to make all the MEF servcies (Except IViewAwareStatus as this needs to know about the View) Property Imports for the ViewModel.

CompositionInitializer.SatisfyImports(this);

See this post for where to download this : http://mef.codeplex.com/discussions/207539

 

I have not tried any of this, this is code, but its all from memory, it could work I feel......But as this is so out of the ordinary you are kind of on your own and I can not really offer any real words of wisdom, its all trial and error I am afraid, I can say you will not be able to use the normal View/ViewModel coupling mechanism as that relies on attached DP in view to resolve ViewModel, but you are bypassing all of that by reading ViewModel from some XAML stream somewhere.

May 18, 2011 at 2:26 PM

Well luckily so far it seems that it will only be parts of the view and viewmodel that will be dynamic.

So all view/viewmodel loading will be standard however some part of the view and some part of the viewmodel will be resolved at runtime by loading part of the UI from xaml.

This part i will then databind to the already databounded viewmodel but then to a property

So i have 

ViewX

<Window>

.....

 <ContentControl Name="PluginControl" Margin="0,231.104,72,8" Grid.Row="2">                                    </ContentControl>

</Window>

ViewModelX has property DynamicLoadedPart

which is set by some service that determines if some plugin is avaiable if it is then it sets the property to that object.

When loading the viewmodel i see if the part is not null , if its not then some other service will set the plugincontrol content to dynamicloadedpart.GetXaml 

the controls of the plugincontrol content are then databound to properties and commands fo the dynamicloadedpart property of the viewmodel

So far this works :) only thing not pretty is how to set the content of the control, now i do this from within the viewmodel basicly, but i want to create a dedicated service that is injected that will load dynamic content in the view to xaml thats coming from a dynamicpart property of the viewmodel.

 So the viewmodel itself does not need to know anything about the dynamic part ..

The dynamic part should know about the viewmodel though as alot of the time it needs values from the viewmodel its part of the work.

May 25, 2011 at 8:38 AM

Iceball

To be honest your requirement is so specific to er well your requirements, I just don't know how much help I can offer. Sounds like you will get there in the end though.