hi i want when application luanched, application navigate to a view automatically so i used like this:
protected override Window CreateShell()
{
return Container.Resolve<MainWindow>();
}
protected override void RegisterTypes(IContainerRegistry containerRegistry)
{
containerRegistry.RegisterForNavigation<MainContent>();
containerRegistry.RegisterForNavigation<Subscene>();
}
and in MainWindowViewModel
public MainWindowViewModel(IRegionManager regionManager)
{
_regionManager = regionManager;
_regionManager.RequestNavigate("ContentRegion", "Subscene", myparameter);
}
this code not work, No errors and no navigation
my region exist in another usercontrol called MainContent
This works for "normal" view models only, not for the view model of the shell, because that one is created too early. You should do the initial navigation in OnInitialized.
internal class MyApp : PrismApplication
{
// ...
protected override void OnInitialized()
{
base.OnInitialized();
Container.Resolve<IRegionManager>().RequestNavigate("ContentRegion", "Subscene", myparameter);
}
// ...
}
Unrelated sidenote: if you use the parameter instead of the field in the constructor, Resharper will tell you when the field is no longer needed in future refactorings.
Related
I'm trying to set up a WPF app to call the new window on a menu click with the data provider interface injected into the new viewmodel.
Followed many tutorials and created the Bootstrapper for Caliburn, a service locator and module for ninject. So far the main view doesn't need the IDataProvider but I'd like to open a new window on click event.
The Bootstrapper:
public class Bootstrapper : BootstrapperBase
{
public Bootstrapper()
{
Initialize();
}
protected override void OnStartup(object sender, StartupEventArgs e)
{
DisplayRootViewFor<MainScreenViewModel>();
}
}
The Service Locator and Module:
public class ServiceLocator
{
private readonly IKernel _kernel;
public ServiceLocator()
{
_kernel = new StandardKernel(new ServiceModule());
}
public MainScreenViewModel MainScreenViewModel => _kernel.Get<MainScreenViewModel>();
public NewLayoutViewModel NewLayoutViewModel => _kernel.Get<NewLayoutViewModel>();
}
public class ServiceModule : NinjectModule
{
public override void Load()
{
Bind<ISqlite>().To<Sqlite>();
Bind<IDataProvider>().To<DataProvider>();
}
}
And this is where I got stuck:
public class MainScreenViewModel : Conductor<object>
{
private IWindowManager _windowManager;
public MainScreenViewModel()
{
_windowManager = new WindowManager();
}
public void NewLayout()
{
_windowManager.ShowWindow(new NewLayoutViewModel());
}
}
since the NewLayoutViewModel requires the IDataProvider.
Not sure, what am I missing, but in my understanding Ninject should take care of this di for NewLayoutViewModel.
Found a good solution from Tim Corey on YouTube.
Basically the answer is, if you not insist Ninjet, use Caliburn.Micro's build-in DI solution "SimpleContainer".
Recently I am going through some old code and found the below code
public class ProfileModule : IModule
{
private readonly IRegionManager regionManager;
private readonly IUnityContainer container;
private IEventAggregator eventAggregator;
public ProfileModule(IUnityContainer c, IRegionManager r, IEventAggregator e)
{
container = c;
regionManager = r;
eventAggregator = e;
}
public void Initialize()
{
// Create and add profiles as new Tab items
container.RegisterType<IProfileViewModel, Profile1ViewModel>(new ContainerControlledLifetimeManager());
regionManager.Regions[RegionNames.HomeRegion].Add(container.Resolve<ProfileView>());// HomeRegion is of type TabControl
container.RegisterType<IProfileViewModel, Profile2ViewModel>(new ContainerControlledLifetimeManager());
regionManager.Regions[RegionNames.HomeRegion].Add(container.Resolve<ProfileView>());
container.RegisterType<IProfileViewModel, Profile3ViewModel>(new ContainerControlledLifetimeManager());
regionManager.Regions[RegionNames.HomeRegion].Add(container.Resolve<ProfileView>());
}
}
Below is the ProfileView.xaml.cs
public partial class ProfileView : INotifyPropertyChanged
{
[InjectionConstructor]
public ProfileView(IProfileViewModel vm)
{
DataContext = vm;
InitializeComponent();
}
}
Below are the viewModels
public abstract class ProfileViewModelBase : IProfileViewModel, IDataErrorInfo, INotifyPropertyChanged
{
public ProfileViewModelBase(IEventAggregator eventAggregator, IRegionManager regionManager)
{
}
}
public class Profile1ViewModel : ProfileViewModelBase
{
[InjectionConstructor]
public Profile1ViewModel(IEventAggregator eventAggregator, IRegionManager regionManager)
: base (eventAggregator, regionManager)
{
}
}
public class Profile2ViewModel : ProfileViewModelBase
{
[InjectionConstructor]
public Profile2ViewModel(IEventAggregator eventAggregator, IRegionManager regionManager)
: base (eventAggregator, regionManager)
{
}
}
public class Profile3ViewModel : ProfileViewModelBase
{
[InjectionConstructor]
public Profile3ViewModel(IEventAggregator eventAggregator, IRegionManager regionManager)
: base (eventAggregator, regionManager)
{
}
}
The part of the code that is not clear for me is the ProfileModule.Initialise().
Everytime when the region manager is adding a view a new new instance of ProfileView is getting created and the viewModel is the one that is registered last.
First time ProfileView is created with Profile1ViewModel as a Datacontext.
Second time ProfileView is created with Profile2ViewModel as a Datacontext.
Third time ProfileView is created with Profile3ViewModel as a Datacontext.
How the container knows exactly which viewmodel to create when creating the view.
Also I understand , container.Resolve will return the view if it already got one, first time view is created and returned, second time I except same view will be returned, but a new view is created. same with third.
Can anyone explain what is happening?
Here goes:
What you can see inside the Initialize method is that after registering the IProfileViewModel the code is then immediately calling Resolve<ProfileView> which on the first Resolve is providing Profile1ViewModel to the ProfileView constructor. Then the second Register replaces the first registration with Profile2ViewModel. Therefore subsequent calls to Resolve will never give you an instance (or the singleton instance) of Profile1ViewModel.
If for some reason you really want to resolve the same instance of ProfileView then you need to Register this with the Unity container as a singleton like the below.
container.RegisterType(new ContainerControlledLifetimeManager());
This is obviously assuming you have an interface defined called IProfileView
This question already has answers here:
Prism 6 with Unity - resolving view models for views without naming convention
(2 answers)
Closed 6 years ago.
I use prism 6.1. I set up the Unity configuration in the method ConfigureContainer of Bootstrapper class. But, when the Prism framework try to call the View Model, it can't create this, and throw the exception 'No parameterless constructor defined for this object.'.
Bootstrapper.cs
public class Bootstrapper: UnityBootstrapper
{
...
protected override void ConfigureContainer()
{
base.ConfigureContainer();
this.Container.RegisterType<IMyService, MyService>(new ContainerControlledLifetimeManager());
this.Container.RegisterType<MyFormViewModel>(new ContainerControlledLifetimeManager());
}
...
}
MyFormModule.cs
public class MyFormModule : IModule
{
private readonly IRegionViewRegistry regionViewRegistry;
public SkypeActionModule(IRegionViewRegistry registry)
{
this.regionViewRegistry = registry;
}
public void Initialize()
{
regionViewRegistry.RegisterViewWithRegion("MainRegion", typeof(Views.MyFormView));
}
}
MyFormViewModel.cs
public class MyFormViewModel : BindableBase
{
private readonly IMyService myService;
public SkypeActionViewModel(IMyService myService)
{
this.myService = myService;
}
...
}
In this line throw the exception:
regionViewRegistry.RegisterViewWithRegion("MainRegion", typeof(Views.MyFormView));
In the prism documentation explains how to configurate unity, but this is in the Module class (in my case is MyFormModule). I don't understand that because in this way, I need to configurate that in each module, and I haven't the object this.container.RegisterType in my module class.
In others links, I found some configuration similar to "MEF" configuration, where the "DI" configuration are in the ConfigureContainer method. But it doesn't work for my, or something missing in my configuration.
Edit
I include the container in my module class. But I have the same problem. I suppose that is normal, because the problem is when Prism create the view model class MyFormViewModel.
public class MyFormModule : IModule
{
private readonly IRegionViewRegistry regionViewRegistry;
private readonly IUnityContainer container;
public MyFormModule(IRegionViewRegistry registry, IUnityContainer container)
{
this.regionViewRegistry = registry;
this.container = container;
}
public void Initialize()
{
this.container.RegisterType<IMyService, MyService>(new ContainerControlledLifetimeManager());
this.container.RegisterType<MyFormViewModel>();
this.container.RegisterType<MyFormView>();
regionViewRegistry.RegisterViewWithRegion("MainRegion", typeof(MyFormView));
}
}
Edit
Solution: I found the solution in this question on stackoverflow
Create the method BindViewModelToView on Bootstrap class:
public void BindViewModelToView<TViewModel, TView>()
{
ViewModelLocationProvider.Register(typeof(TView).ToString(), () => Container.Resolve<TViewModel>());
}
and then, in the method ConfigureViewModelLocator in Bootstrap class call all view models it to bind with views:
protected override void ConfigureViewModelLocator()
{
BindViewModelToView<ViewAVM, ViewA>();
BindViewModelToView<ViewAVM, ViewB>();
}
How do you tell prism to create the view model? Are you using ViewModelLocator.AutoWireViewModel="True"?
If so, you want to do something like
ViewModelLocationProvider.SetDefaultViewModelFactory( type =>
{
return Container.Resolve(type);
});
in the bootstrapper to make sure that your container is used for resolving the view model...
Hi I wrote code for view navigation, For that i added button on my navigation area on that button click i want to open view in content area My code is
ModuleCode
public class ClientModule : ModuleBase
{
public ClientModule(IUnityContainer container, IRegionManager regionManager)
: base(container, regionManager) { }
protected override void InitializeModule()
{
RegionManager.RegisterViewWithRegion("NavigationRegion", typeof(Navigation));
RegionManager.RegisterViewWithRegion("ContentRegion", typeof(Content));
}
protected override void RegisterTypes()
{
Container.RegisterType<object, Navigation>(typeof(Navigation).FullName);
Container.RegisterType<object,Content>(typeof(Content).FullName);
}
}
}
Bootstrap is
public class Bootstrapper : UnityBootstrapper
{
protected override DependencyObject CreateShell()
{
return this.Container.Resolve<Shell>();
}
protected override void InitializeShell()
{
base.InitializeShell();
App.Current.MainWindow = (Window)this.Shell;
App.Current.MainWindow.Show();
}
protected override void ConfigureContainer()
{
base.ConfigureContainer();
Container.RegisterType<IShellViewModel, ShellViewModel>();
}
protected override RegionAdapterMappings ConfigureRegionAdapterMappings()
{
RegionAdapterMappings mappings = base.ConfigureRegionAdapterMappings();
mappings.RegisterMapping(typeof(StackPanel), Container.Resolve<StackPanelRegionAdapter>());
return mappings;
}
protected override void ConfigureModuleCatalog()
{
base.ConfigureModuleCatalog();
ModuleCatalog moduleCatalog = (ModuleCatalog)this.ModuleCatalog;
moduleCatalog.AddModule(typeof(ClientModule));
}
}
and my navigationview have 1 button as below
<StackPanel>
<Button Command="{x:Static infCommands:ApplicationCommands.NavigateCommand}"
CommandParameter="{x:Type views:Content}"
Name="btnTest">Navigate to Content</Button>
</StackPanel>
now problem is when i run my application button always show disable, Can any one tell me what is the problem and what is its solution.
Thanks
I got this working.
I was using same code following a pluralsight video about Prism.
Issue was that the only commmand associated with CompositeCommand was not set to Active. After setting IsActive to true, CompositeCommand became active, and button was enabled.
Following is from msdn docs for Prism
The CompositeCommand can be configured to evaluate the active status of child DelegateCommands (in addition to the CanExecute status) by specifying true for the monitorCommandActivity parameter in the constructor. When this parameter is set to true, the CompositeCommand class will consider each child DelegateCommand's active status when determining the return value for the CanExecute method and when executing child commands within the Execute method.
When the monitorCommandActivity parameter is true, the CompositeCommand class exhibits the following behavior:
CanExecute. Returns true only when all active commands can be executed. Child commands that are inactive will not be considered at all.
Execute. Executes all active commands. Child commands that are inactive will not be considered at all.
I've been trying to write a WPF application using PRISM and MEF and I've been able to get the Shell up and running. I want to be able to load Modules on demand so I need an instance of IModuleManager in the Shell. However, when I try to import it, the application breaks. Here is the relevant code:
Bootstrapper:
public class Bootstrapper : MefBootstrapper
{
protected override DependencyObject CreateShell()
{
return this.Container.GetExportedValue<Shell>();
}
protected override void InitializeShell()
{
base.InitializeShell();
Application.Current.MainWindow = (Shell)this.Shell;
Application.Current.MainWindow.Show();
}
protected override void ConfigureAggregateCatalog()
{
base.ConfigureAggregateCatalog();
// Add this assembly to export ModuleTracker
this.AggregateCatalog.Catalogs.Add(new AssemblyCatalog(typeof(Bootstrapper).Assembly));
DirectoryModuleCatalog moduleCatalog = new DirectoryModuleCatalog();
moduleCatalog.ModulePath = #".\Modules";
moduleCatalog.Load();
foreach (ModuleInfo moduleInfo in moduleCatalog.Modules)
{
this.ModuleCatalog.AddModule(moduleInfo);
}
DirectoryCatalog catalog = new DirectoryCatalog(#".\Modules");
this.AggregateCatalog.Catalogs.Add(catalog);
base.ConfigureAggregateCatalog();
}
protected override void ConfigureContainer()
{
//Export the Container so that it can be injected if needed.
this.Container.ComposeExportedValue(Container);
//Export the Module Catalog so that it can be injected if needed.
this.Container.ComposeExportedValue(ModuleCatalog);
base.ConfigureContainer();
}
protected override IModuleCatalog CreateModuleCatalog()
{
return new ConfigurationModuleCatalog();
}
}
Shell:
[Export(typeof(Shell))]
public partial class Shell : Window, IPartImportsSatisfiedNotification
{
[Import(AllowRecomposition = false)]
private IModuleManager moduleManager;
public Shell()
{
InitializeComponent();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
}
public void OnImportsSatisfied()
{
}
}
The exception I'm getting is:
No exports were found that match the constraint:
ContractName Shell
RequiredTypeIdentity Shell
If I remove the [Import] attribute for the IModuleManager, everything works fine. Is there something I need to do to export IModuleManager?
Resolved this issue by commenting the following line in the Bootstrapper:
this.Container.ComposeExportedValue(ModuleCatalog);
Not sure why it was causing an issue but would welcome any insights into this matter.
One thing that you have to watch out for is having multiple exports for the same type. This can easily happen when you are using ComposeExportedValue as well as using a DirectoryCatalog (which could contain an Export of the same type).
There is a great package on Nuget for diagnosing these problems called MEFX
If you get this library, you can add the following lines to help find out what is going on
var compositionInfo = new CompositionInfo(AggregateCatalog, Container);
CompositionInfoTextFormatter.Write(compositionInfo, Console.Out);
I would be interested to see what this prints to the Output window when you leave this.Container.ComposeExportedValue(ModuleCatalog); in your program, if you wouldn't mind posting any errors from it.