In Prism 7 I can RegisterForNavigation and RequestNavigate from IModule like this:
public class ModuleAModule : IModule
{
public void OnInitialized(IContainerProvider containerProvider)
{
var regionManager = containerProvider.Resolve<IRegionManager>();
regionManager.RequestNavigate("ContentRegion", "PersonList");
}
public void RegisterTypes(IContainerRegistry containerRegistry)
{
containerRegistry.RegisterForNavigation<PersonList>();
}
}
and I know that I can RegisterForNavigation from PrismApplication like this:
public partial class App : PrismApplication
{
protected override void RegisterTypes(IContainerRegistry containerRegistry)
{
containerRegistry.RegisterForNavigation<ViewA>();
}
}
but how can I RequestNavigate from PrismApplication immediately on start?
I have tried this:
public class MainWindowViewModel : BindableBase
{
public MainWindowViewModel(IRegionManager regionManager)
{
regionManager.RequestNavigate("ContentRegion", "ViewA");
}
}
but this.regions.Count is 0 in RegionManager from Prism
private IRegion GetRegionByName(string regionName)
{
return this.regions.FirstOrDefault(r => r.Name == regionName);
}
"ContentRegion" definitely exists, because it works if I try from the IModule and I know that RegisterTypes from PrismApplication executes before the MainWindowViewModel constructor.
I don't know what I am missing and I can't find the answer in any examples or tutorials.
Thank you for your help!
Your best bet is to override OnInitialized in your application and do the navigation there. You can access the container to fetch the region manager through the Container property.
If you use a bootstrapper, you can override InitializeModules and navigate there.
Related
My code looks like this:
Bootstrapper.cs
public class Bootstrapper : BootstrapperBase
{
private SimpleContainer _container = new SimpleContainer();
public Bootstrapper()
{
Initialize();
}
protected override void OnStartup(object sender, StartupEventArgs e)
{
base.OnStartup(sender, e);
DisplayRootViewFor<ShellViewModel>();
}
protected override void Configure()
{
_container.Singleton<IEventAggregator, EventAggregator>();
_container.Singleton<IWindowManager, WindowManager>();
_container.RegisterPerRequest(typeof(ShellViewModel), null, typeof(ShellViewModel));
}
protected override object GetInstance(Type service, string key)
{
return _container.GetInstance(service, key);
}
protected override IEnumerable<object> GetAllInstances(Type serviceType)
{
return _container.GetAllInstances(serviceType);
}
protected override void BuildUp(object instance)
{
_container.BuildUp(instance);
}
}
And my ShellViewModel looks like this:
ShellViewModel.cs
public class ShellViewModel : Conductor<Screen>
{
public ShellViewModel
{
var aViewModel = IoC.Get<AViewModel>();
ActivateItem(aViewModel);
}
}
But whenever I run the program, a blank screen is shown. When I debug it, it said that the aViewModel is null.
Is there anything wrong with the Bootstrapper?
Based on the code provided, AViewModel is not registered with the container in the Bootstrapper so IoC does not know it exists, thus it will return null when requested to Get that type
For example
_container.RegisterPerRequest(typeof(AViewModel), null, typeof(AViewModel));
All types that need to be resolved by IoC should first be registered with the backing container.
I am developing Windows Phone 8.1 app with MVVM.
I have base view model class which contains Navigation Service as below:
public abstract class BaseViewModel : INotifyPropertyChanged
{
protected readonly INavigationService NavigationService;
//....
}
There is my navigation service class:
public class NavigationService : INavigationService
{
public void Navigate(Type destinationPage)
{
((Frame)Window.Current.Content).Navigate(destinationPage);
}
public void Navigate(Type desitnationPage, object parameter)
{
((Frame)Window.Current.Content).Navigate(desitnationPage, parameter);
}
public void GoBack()
{
((Frame)Window.Current.Content).GoBack();
}
}
Everything is working fine when I am binding commands from XAML. There is problem when I want to override BackButton. I have also created base page model which also contains NavigationService. Each page has an overridde pf BackPressed as below:
public class BasePage : Page
{
protected INavigationService NavigationService => ComponentManager.GetInstance<INavigationService>();
public BasePage()
{
//...
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
HardwareButtons.BackPressed += HardwareButtons_BackPressed;
}
protected virtual void HardwareButtons_BackPressed(object sender, BackPressedEventArgs e)
{
//Frame.Navigate(typeof(MainPage));
(this.DataContext as BaseViewModel)?.Back.Execute(sender);
}
}
As you see in HardwareButtons_BackPressed method I've tried to make it in to ways but none is workings. Every time I press back button application crashes without any error.
I don't think the app is crashing, it's just exiting because that is the default behaviour of the back button.
What you need to do is flag that you've handled the back button by adding this line of code in your BackPressed event handler:
e.Handled = true;
I want to choose which user control to load but my MainWindowView is not even loaded yet so the region manager does not know any regions, How can I achieve this?
my bootstrapper looks like this:
protected override DependencyObject CreateShell()
{
return this.Container.Resolve<MainWindowView>();
}
protected override void InitializeShell()
{
Application.Current.MainWindow.Show();
}
protected override void ConfigureContainer()
{
base.ConfigureContainer();
this.Container.RegisterTypeForNavigation<WorkTypeSelectionView>();
}
and my viewmodel:
public MainWindowViewModel(IEventAggregator eventAggregator, IRegionManager regionManager)
{
this.eventAggregator = eventAggregator;
this.regionManager = regionManager;
this.AuthenticateUser();
if (this.LoggedUser.AvailableWorkTypes.Count > 1)
{
this.Navigate(nameof(WorkTypeSelectionView));
}
}
private void Navigate(string obj)
{
this.regionManager.RequestNavigate(DefaultContentRegion, obj);
}
thanks in advance!
EDIT:
Guess i was asking the wrong question, found this https://stackoverflow.com/a/7887936/171136 still want to explore other options. Thanks!
You can use View Discovery with regionManager.RegisterViewWithRegion("RegionName", typeof(View));. When the region is created, it will automatically inject the view.
I am running the latest PRISM 4.2. Unfortunately the Event Aggregator tutorial in the documentation is driven via Unity instead of MEF. And I can't get it running under MEF.
App.xaml.cs
public partial class App : Application
{
[Import]
public IEventAggregator eventAggregator;
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
Bootstrapper bootstrapper = new Bootstrapper();
bootstrapper.Run();
}
}
Bootstrapper.cs
public class Bootstrapper : MefBootstrapper
{
protected override DependencyObject CreateShell()
{
return new MainWindow();
}
protected override void InitializeShell()
{
base.InitializeShell();
App.Current.MainWindow = (Window)Shell;
App.Current.MainWindow.Show();
}
protected override void ConfigureAggregateCatalog()
{
base.ConfigureAggregateCatalog();
AggregateCatalog.Catalogs.Add(new AssemblyCatalog(this.GetType().Assembly));
}
protected override IModuleCatalog CreateModuleCatalog()
{
ModuleCatalog moduleCatalog = new ModuleCatalog();
return moduleCatalog;
}
}
MainWindow.xaml.cs
public partial class MainWindow : Window, IView
{
public MainWindow()
{
InitializeComponent();
DataContext = new MainViewModel();
}
}
MainViewModel.cs:
[ModuleExport(typeof(MainViewModel))]
public class MainViewModel : BindableBase, IServiceCallback
{
[Import]
public IEventAggregator eventAggregator;
[ImportingConstructor]
public MainViewModel()
{
eventAggregator.GetEvent<AppExitEvent>().Publish("");
}
}
Despite the [import] the event aggregator is always null both in App.xaml.cs and in MainViewModel. Why is that?
The second question is do I have to export my Viewmodels as a module (as I did above) to use an even aggregator inside them?
UPDATE:
Proof that latest version of PRISM doesn't support ComposeExportedValue anymore.
'System.ComponentModel.Composition.Hosting.CompositionContainer' does
not contain a definition for 'ComposeExportedValue' and no extension
method ...
The solution to this would be what SchubertJ replied on your same question at CodePlex:
How to utilise Event aggregator in MEF?
As a deeper analyisis, the Import attribute on Properties would not be resolved until the constructor finishes. That is why you would need to inject the EventAggregator dependency through the constructor as a parameter if this dependency would be used on the constructor implementation.
Therefore, if you would like to use the EventAggregator dependency on the ViewModel constructor, you should use [ImportConstructor] attribute instead, and ask the container for the EventAggregator instance by retreiving it as a parameter:
public class MainViewModel
{
private readonly IEventAggregator eventAggregator;
[ImportingConstructor]
public MainViewModel(IEventAggregator eventAggregator)
{
this.eventAggregator = eventAggregator;
this.eventAggregator.GetEvent<MyEvent>().Publish("");
}
}
You may find more related information regarding both import alternatives in the following post:
MEF - [Import] vs [ImportingConstructor]
I hope this helped you, Regards.
In your bootstrapper class have this method :
protected override void ConfigureContainer()
{
base.ConfigureContainer();
Container.ComposeExportedValue(new EventAggregator());
}
You should look at this article as it is answer both your first and second question in better details.
http://www.gonetdotnet.info/posts/wpf-articles/wpf-module-communication
Update:
If you create a class as below it will match your export with your import.
public class EventAggProvider
{
[Export(typeof(IEventAggregator))]
public IEventAggregator eventAggregator { get { return new EventAggregator(); } }
}
It is a little late, but there is also a solution for the case in which you want the EventAggregator gets injected in property. Implement 'IPartImportsSatisfiedNotification' and use the eventaggregator in 'OnImportsSatisfied' method.
public class MainViewModel : BindableBase, IPartImportsSatisfiedNotification
{
[Import]
public IEventAggregator eventAggregator;
public void OnImportsSatisfied()
{
eventAggregator.GetEvent<AppExitEvent>().Publish("");
}
}
EventAggregator does not depend on MEF or Unity it's design pattern coined by Martin Fowler and it's based on publisher subscriber scenario
try to follow this steps
//this should be in your infrastructure layer
public static class EventAggregatorHelper
{
private static IEventAggregator _Current = new EventAggregator();
public static IEventAggregator Current
{
get
{
return _Current;
}
}
}
The HandleAppExitEvent is a class declared as shown below:
public class AppExitEvent : CompositePresentationEvent<String>
{
}
and the subscriber would be something like this:
in your MainWindow.xaml.cs
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new MainViewModel();
//In this case the HandleAppExitEvent and the subscriber that deals with this event.
EventAggregatorHelper.Current.GetEvent<AppExitEvent>(). Subscribe(HandleAppExitEvent);
}
//note that this method should be public
public void HandleAppExitEvent(string mess)
{
if (!String.IsNullOrEmpty(mess))
{
//Do whatever you need to do here.
}
}
}
I am implementing application in PRISM, which need to import modules dynamically from dll files.
I managed to do that - they are importing, but I can't display it.
I decided to create a special module to encapsulate it - let us call it ModuleDock.
So we have something like that:
Bootstrapper.cs:
class Bootstrapper : UnityBootstrapper
{
protected override DependencyObject CreateShell()
{
return Container.Resolve<Shell>();
}
protected override void InitializeShell()
{
base.InitializeShell();
Application.Current.MainWindow = (Window)Shell;
Application.Current.MainWindow.Show();
}
protected override IModuleCatalog CreateModuleCatalog()
{
var modules = new DirectoryModuleCatalog
{
LoadSubdirectories = true,
ModulePath = #"C:\Modules"
};
modules.AddModule(typeof(ModuleDockModule));
return modules;
}
}
Shell.xaml:
<ContentControl regions:RegionManager.RegionName="ModuleDockModule" />
ModuleDockModule.cs (in ModuleDock project):
public class ModuleDockModule : IModule
{
private readonly IRegionManager _regionManager;
private readonly IUnityContainer _unityContainer;
public void Initialize()
{
RegisterIoc();
if (_regionManager.Regions.ContainsRegionWithName("ModuleDockModule"))
{
_regionManager.RegisterViewWithRegion("ModuleDockModule", typeof(ModuleDockView));
}
}
public ModuleDockModule(IRegionManager regionManager, IUnityContainer unityContainer, IRegionViewRegistry regionViewRegistry)
{
_regionManager = regionManager;
_unityContainer = unityContainer;
}
private void RegisterIoc()
{
_unityContainer.RegisterType<IModuleDockView, ModuleDockView>();
_unityContainer.RegisterType<IModuleDockViewModel, ModuleDockViewModel>();
}
}
and finally in one of loaded modules:
[Module(ModuleName = "TestModule", OnDemand = false)]
public class TestModuleModule : IModule
{
private readonly IRegionManager _regionManager;
private readonly IUnityContainer _unityContainer;
public void Initialize()
{
RegisterIoc();
if (_regionManager.Regions.ContainsRegionWithName("TestModule"))
{
_regionManager.RegisterViewWithRegion("TestModule", typeof(TestView));
}
}
public TestModuleModule(IRegionManager regionManager, IUnityContainer unityContainer)
{
_regionManager = regionManager;
_unityContainer = unityContainer;
}
private void RegisterIoc()
{
_unityContainer.RegisterType<ITestView, TestView>();
_unityContainer.RegisterType<ITestViewModel, TestViewModel>();
}
}
For test purposes I've created that line of XAML:
<ContentControl regions:RegionManager.RegionName="TestModule" />
Could you tell me, why that line displays TestModule in Shell.xaml, but don't display it in ModuleDockView.xaml?
Please, mind that in final stage I have to use various number of unknown modules provided by other users of my platform, so I can't make anything static (like module names or initializations).
Thank you in advance!
Based on the code you described, the TestModule module has a module dependency with ModuleDockModule as this is defining the region that the TestModule's Views would then be registered into.
Therefore, you would need to make sure that ModuleDockModule is initialized before TestModule and any other module that would depend on it. In order to do this, you would need to declare a dependency attribute on TestModuleModule class, right above the class definition as follows:
[Module(ModuleName = "TestModule", OnDemand = false)]
[ModuleDependency("ModuleDockModule")]
public class TestModuleModule : IModule
{ ...
For more information about Moduled you may refer to the following MSDN Prism Guide chapter:
Modular Application Development
I hope this helped you, Regards.