PRISM creates new ViewModel by each navigation request? - c#

I am using Prism v6.3.0 for a UWP app. I am using the Unity Container for DI.
Following problem occurs: whenever my app navigates to a given view (e.g. MainPage) there seems that a new instance of the corresponding ViewModel is created. I would like to reuse the same VM (basically, to have it only once created).
I am having: prismMvvm:ViewModelLocator.AutoWireViewModel="True" in all of the pages' XAML.
My code never calls directly any VM constructor, so it cannot be that. I am also registering only services in the ConfigureContainer override.
What I've tried is to register the Views in App.xaml.cs (it seems to make no difference):
private void RegisterViews()
{
Container.RegisterInstance(new LoginPage(), new ContainerControlledLifetimeManager());
Container.RegisterInstance(new SettingsPage(), new ContainerControlledLifetimeManager());
Container.RegisterInstance(new MainPage(), new ContainerControlledLifetimeManager());
}
Any hints?

Since no one answered, I tried figuring out on my own once more.
Now, I know that probably the best way to go about it would be to modify the container that Prism uses so it registers all the VMs as singletons. Because that sounded like too much work for this issue, I instead chose to first take a look at the ViewModelLocationProvider class. The ViewModelLocationProvider is used by PRISM to look up the VM for a given View (that has AutoWireViewModel set to true) and to inject the found VM type in the given View's DataContext.
I tried coming up with a factory for a given view that would ensure that only one VM instance would be created ever and found following solution:
protected override void ConfigureViewModelLocator()
{
base.ConfigureViewModelLocator();
ViewModelLocationProvider.Register<MainPage>((delegate
{
var vm = Container.Resolve<MainViewModel>();
if (Container.Registrations.All(r => r.RegisteredType != typeof(MainViewModel)))
Container.RegisterInstance<MainViewModel>(vm, new ContainerControlledLifetimeManager());
return vm;
}));
ViewModelLocationProvider.Register<SettingsPage>((delegate
{
var vm = Container.Resolve<SettingsViewModel>();
if (Container.Registrations.All(r => r.RegisteredType != typeof(SettingsViewModel)))
Container.RegisterInstance<SettingsViewModel>(vm, new ContainerControlledLifetimeManager());
return vm;
}));
}

Related

How to instantiate a Service with parameters in MVVM Light ViewModelLocator?

I have been searching for about 3 hours.
I need to know how do I instantiate a Service with parameters that are produced by another service in MVVM Light's ViewModelLocator.
In my ViewModelLocator class I register my View Models and then register services that they use, like this:
SimpleIoc.Default.Register<INetworkClient>(() => new NetworkClient());
SimpleIoc.Default.Register<IApiClientAuthentication>(() => new ApiClientAuthentication());
SimpleIoc.Default.Register<IApiClient>(() =>
{
INetworkClient networkClient = ServiceLocator.Current.GetInstance<INetworkClient>();
return new ApiClient(networkClient, new Uri("https://someuri.com/"),
authResponse.AdminUserId, authResponse.ApiKey);
});
The problem is that the "authResponse" that I pass to the ApiClient above is a result produced by calling a method on implementation of IApiClientAuthentication (which I also register with IoC container above). That response is not available when I create the ApiClient above. How should I go about it.
Please, at least push me in the right direction.

MEF and Caliburn.Micro ViewModelFactory with Parameters

I've been working with Caliburn.Micro and MEF and I'm trying to get a viewmodelfactory implementation working. What I'm attempting is to create a chain of ViewModels for a dialog window (each ViewModel instantiates one or more viewmodels to generate the overall layout of the window). I'm having trouble importing the viewmodelfactory correctly; I can get it without a problem using [ImportingConstructor], however when I try to use [import] I end up with a null reference exception against viewModelFactory.
The "ViewModelFactory" which I have implemented is as per:
http://blog.pglazkov.com/2011/04/mvvm-with-mef-viewmodelfactory.html
and I'm trying to import the viewmodel as per the following:
[Import]
public IViewModelFactory viewModelFactory { get; set; }
and IViewModelFactory itself has an export declared (and works correctly with [ImportingConstructor]
[Export(typeof(IViewModelFactory))]
[PartCreationPolicy(CreationPolicy.Shared)]
public class ViewModelFactory : IViewModelFactory
Attempt 2
MY next effort was trying to add an instance of ViewModelFactory into the composition container:
protected override void Configure()
{
var catalog =
new AggregateCatalog(
AssemblySource.Instance.Select(x => new AssemblyCatalog(x)).OfType<ComposablePartCatalog>());
container = new CompositionContainer(catalog);
var batch = new CompositionBatch();
batch.AddExportedValue<IWindowManager>(new WindowManager());
batch.AddExportedValue<IEventAggregator>(new EventAggregator());
batch.AddExportedValue<IViewModelFactory>(new ViewModelFactory());
batch.AddExportedValue(container);
container.Compose(batch);
}
However this results in an error within the ViewModelFactory, stating that the composition container which is Lazy loaded is null.
I'm trying to find a solution that will allow me to still use the Factory approach, as it allows me to use constructor parameters which are currently required as part of my viewmodels.
EDIT
I was able to get this to work by having an "Initialise" function within my viewmodels, using [ImportingConstructor] on my ViewModels with a constructor that only contains the IViewModelFactory declaration. However, this now required me to instantiate the viewmodel and make a call to the "initialise" function whenever I am creating these, so a more elegant approach would be great.
Thanks.
Managed to implement a different solution to this, which was to use:
IoC.Get<*ViewModelName*>();
Still haven't worked out why the [Import] by itself didn't work, however this certainly solved the issue for me.

Unity Dependency Injection (constructor or property)

I've been using Unity for some time in my project. I have a singleton Container which I use to register types and/or instances, and later resolve.
today i wanted to automate some of the instantiation by using property or constructor injection.
I started with Logger class. In Application start i have this code:
RegisterType<Logger, Logger>();
than in my ViewModel
[Dependency]
public Logger MyLogger {
get;
set;
}
here is how i instantiate the viewmodel that has this property (in MainWindow.xaml.cs)
private void InitializeViewModel() {
_vm = new MainViewModel(MainGrid);
...
MyContainer.GetInstance().Container.RegisterInstance<MainViewModel>(_vm);
I can't get that [property injector] to work. Does property injection NEED to be paired up with a constructor? I am already using a constructor that has some parameters..
Something's wrong in your example. If you want the Logger to be injected into MainViewModel, you'd have to let the container create the MainViewModel for you.
However, in your code you are creating it by yourself. As I look at it, it should be:
_vm = Container.Resolve<MainViewModel>();
or at least
_vm = new MainViewModel();
Container.BuildUp( _vm );
Instead of using new MainViewModel() to create the viewmodel, you need to have your Unity Container do the creation.
Container.RegisterType<MainViewModel>(
new ContainerControlledLifetimeManager(),
new InjectConstructor(MainGrid)
);
And then you can use Container.Resolve<MainViewModel>() to get your singleton formerly known as _vm.
Note: The ContainerControlledLifetimeManager part tells Unity to only create a single instance of MainViewModel and return it for everything.

Prism, Unity and default type registration in Modules

Technologies
C# 4.0
Prism 4 with Unity for DI
WPF
MVVM
Preface
There are two projects in my solution, MyApp.Shell and MyApp.ModuleFoo
MyApp.Shell's Unity Bootstrapper
protected override IModuleCatalog CreateModuleCatalog()
{
// Module assemblies are read from a directory.
DirectoryModuleCatalog moduleCatalog = new DirectoryModuleCatalog();
moduleCatalog.ModulePath = #".\Modules";
return moduleCatalog;
}
The project MyApp.ModuleFoo contains a View and a View Model.
The ViewModel
// Somehow, Unity sees this class and registers the type.
public class FooViewModel : ViewModelBaseClass
{
public string FooText
{
get { return "Foo!"; }
}
}
The View
<Label Content={Binding FooText} />
The View's Code-behind
// Unity automatically sees this as Constructor Injection,
// which is exactly what I desire.
public FooView(FooViewModel viewModel)
{
DataContext = viewModel;
...
}
MyApp.FooModule's Initialization
Perhaps registering FooView with the region manager is inadvertently registering FooViewModel with Unity?
public void Initialize()
{
var regionManager = ServiceLocator.Current.GetInstance<IRegionManager>();
regionManager.RegisterViewWithRegion("FooRegion", typeof(FooView));
}
The view correctly displays "Foo!".
Problems
How do I tell Unity to register only a single instance of FooViewModel?
Additionally, (and I'm thinking ahead here), how would I tell unity not to register FooViewModel?
Thanks for the help.
Edit:
Added MyApp.FooModule's Initialization code
Edit (Solution):
It turns out RegisterViewWithRegion has two overloads. From Prism's documentation, when the overload I'm using is used, a new instance of the view is created. I'm assuming this also creates a new instance of FooViewModel.
The other overload uses a delegate to resolve FooView. The documentation says this overload is used in the "ViewModel-first" approach. I'm going to make this question as answered, but if anyone has any additional insight, I'd love to hear.
// Somehow, Unity sees this class and registers the type.
public class FooViewModel : ViewModelBaseClass
...
I am surprised that you say this as Unity does not register types inside the container by default. You have to tell it to do so either programmatically or in the config file.
When you have concrete classes (not interfaces) they will automatically get created by Unity whether they are registered or not. If not the default behavior is to create a new instance each time. No lifetime management is applied also.
As far as your questions:
To register only one type within your initialisation of your module just have.
Container.RegisterType<FooViewModel>(new ContainerControlledLifetimeManager());
The lifetime manager will instruct unity to only create one instance of the view model.

Prism 2.1 Injecting Modules into ViewModel

I've been trying to inject the modules from my ModuleCatalog into my Shell's ViewModel but I'm not having much luck...
I'm creating the ModuleCatalog in my Bootstrapper and my module is getting onto the screen from its Initializer without problem. However, I'd love to be able to bind my list of modules to a container with a DataTemplate which allowed them to be launched from a menu!
Here's my Boostrapper file, I'll be adding more modules as times goes on, but for now, it just contains my rather contrived "ProductAModule":
public class Bootstrapper : UnityBootstrapper
{
protected override void ConfigureContainer()
{
Container.RegisterType<IProductModule>();
base.ConfigureContainer();
}
protected override IModuleCatalog GetModuleCatalog()
{
return new ModuleCatalog()
.AddModule(typeof(ProductAModule));
}
protected override DependencyObject CreateShell()
{
var view = Container.Resolve<ShellView>();
var viewModel = Container.Resolve<ShellViewModel>();
view.DataContext = viewModel;
view.Show();
return view;
}
}
Following on from that, here's my Shell's ViewModel:
public class ShellViewModel : ViewModelBase
{
public List<IProductModule> Modules { get; set; }
public ShellViewModel(List<IProductModule> modules)
{
modules.Sort((a, b) => a.Name.CompareTo(b));
Modules = modules;
}
}
As you can see, I'm attempting to inject a List of IProductModule (to which ProductAModule inherits some of its properties and methods) so that it can then be bound to my Shell's View. Is there something REALLY simple I'm missing or can it not be done using the Unity IoC? (I've seen it done with StructureMap's extension for Prism)
One more thing... When running the application, at the point the ShellViewModel is being resolved by the Container in the Bootstrapper, I receive the following exception:
Resolution of the dependency failed, type = "PrismBasic.Shell.ViewModels.ShellViewModel", name = "". Exception message is: The current build operation (build key Build Key[PrismBasic.Shell.ViewModels.ShellViewModel, null]) failed: The parameter modules could not be resolved when attempting to call constructor PrismBasic.Shell.ViewModels.ShellViewModel(System.Collections.Generic.List`1[[PrismBasic.ModuleBase.IProductModule, PrismBasic.ModuleBase, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]] modules). (Strategy type BuildPlanStrategy, index 3)
Anyway, simple huh... Looks bemused...
Any help would be greatly appreciated!
Rob
I think you could probably just do this:
public class Bootstrapper : UnityBootstrapper
{
protected override void ConfigureContainer()
{
Container.RegisterType<IProductModule>();
base.ConfigureContainer();
}
private static ObservableCollection<IProductModule> _productModules = new Obser...();
public static ObservableCollection<IProductModule> ProductModules
{
get { return _productModules; }
}
protected override IModuleCatalog GetModuleCatalog()
{
var modCatalog = new ModuleCatalog()
.AddModule(typeof(ProductAModule));
//TODO: add all modules to ProductModules collection
return modCatalog;
}
...
}
Then you would have a static property that anything could bind to directly, or could be used from your ViewModel.
Here is how to get a list of module names that have been registered with the module catalog.
public class MyViewModel : ViewModel
{
public ObservableCollection<string> ModuleNames { ... }
public MyViewModel(IModuleCatalog catalog)
{
ModuleNames = new ObservableCollection<string>(catalog.Modules.Select(mod => mod.ModuleName));
}
}
That's pretty much it. IModuleCatalog and IModuleManager are the only things that are setup in the container for you to access in terms of the modules. As I said, though, you won't get any instance data because these modules (hopefully) are yet to be created. You can only access Type data.
Hope this helps.
I think you misunderstood the purpose of the modules. The modules are just containers for the views and services that you wish too use. The shell on the other hand should just contain the main layout of your application.
What I think you should do is to define a region in your shell, and then register the views (which in your case are buttons) with that region.
How you wish do deploy your views and services in terms of modules is more related to what level of modularity you're looking for, i.e. if you want to be able to deploy the views and services of ModuleA independently of the views and services of ModuleB and so on. In your case it might be enough to register everything in one single module.
Take some time to play around with the examples provided with the documentation, they are quite good.
The reason why your examples throws an example is because your ShellViewModel is depending on List and that type is not registered in Unity. Furthermore you're registering IProductModule with Unity, which makes no sense because an Interface cannot be constructed.
I think I encountered a similar problem today, it turns out that PRISM creates the shell before initializing the modules, so you can't inject any services from the modules into the shell itself.
Try creating another module that depends on all of the others and implements the functionality you want, then you can add it to a region in the shell to display your list of services. Unfortunately I haven't had a chance to try it yet, but this is the solution I plan on implementing.
As a side note, I think you need to mark the property with an attribute to use property injection, but I could be mistake (it's been a while since I played with Unity directly).
Edit: You need to apply the DependencyAttribute to properties to use setter injection in Unity; you can read about it here.
var modules = new IProductModule[]
{
Container.Resolve<ProductAModule>()
//Add more modules here...
};
Container.RegisterInstance<IProductModule[]>(modules);
That's it! Using this code, I can inject my modules into the ShellViewModel and display each module as a button in my application!
SUCH a simple resolution! From a great guy on the CompositeWPF Discussion group. I recommend them without reserve ^_^

Categories