I want to navigate from ModuleA View to ModuleB View. How can I implement navigation between modules?
In my application which uses Prism framework, I have two module
ModuleA
ModuleB
I configure two modules in my Bootstrapper like this:
protected override void ConfigureModuleCatalog()
{
base.ConfigureModuleCatalog();
ModuleCatalog moduleCatalog = (ModuleCatalog)this.ModuleCatalog;
moduleCatalog.AddModule(typeof(ModuleA));
moduleCatalog.AddModule(typeof(ModuleB));
}
I register my both Views in my ModuleA and ModuleB like this:
public class ModuleA : IModule
{
private readonly IRegionManager regionManager;
private readonly IUnityContainer container;
public StaffModule(IUnityContainer container, IRegionManager regionManager)
{
this.container = container;
this.regionManager = regionManager;
}
public void Initialize()
{
this.regionManager.RegisterViewWithRegion("MainRegion", () => this.container.Resolve<StaffView>());
}
}
public class AccountModule : IModule
{
private readonly IRegionManager regionManager;
private readonly IUnityContainer container;
public AccountModule(IUnityContainer container, IRegionManager regionManager)
{
this.container = container;
this.regionManager = regionManager;
}
public void Initialize()
{
container.RegisterType<object, AccountView>("AccountView");
////this.regionManager.RegisterViewWithRegion("MainRegion", () => this.container.Resolve<AccountView>());
}
}
When I click Button from StaffView in ModuleA, I want to navigate to AccountView in ModuleB. Here is my code for navigation.
private void LodeViewfromModule()
{
IUnityContainer unityContainer = ServiceLocator.Current.GetInstance<IUnityContainer>();
var regionManager=unityContainer.Resolve<IRegionManager>();
var uri = new Uri("pack://application:,,,/PrismAuto.Account;component/AccountView.xaml", UriKind.RelativeOrAbsolute);
regionManager.RequestNavigate("MainRegion", uri);
}
But it shows:
System.Object exception.
Please, anyone help me to solve this problem.
You are registering your view for navigation using:
container.RegisterType<object, AccountView>("AccountView");
and navigating to it like:
var uri = new Uri("pack://application:,,,/PrismAuto.Account;component/AccountView.xaml", UriKind.RelativeOrAbsolute);
regionManager.RequestNavigate("MainRegion", uri);
This is wrong. You need to navigate to it using the key your provided when you registered it for navigation:
regionManager.RequestNavigate("MainRegion", "AccountView");
Also, if you are using Prism 6 there is an extension method in the Prism.Unity namespace for registering your views for navigation like this:
container.RegisterTypeForNavigation<AccountView>();
Related
Now I'm practicing Prism with "Prism-Samples-Wpf" in Github.
(https://github.com/PrismLibrary/Prism-Samples-Wpf/tree/master/06-ViewActivationDeactivation)
This code is part of MainWindow.xaml.cs
public partial class MainWindow : Window
{
IContainerExtension _container;
IRegionManager _regionManager;
IRegion _region;
ViewA _viewA;
ViewB _viewB;
public MainWindow(IContainerExtension container, IRegionManager regionManager)
{
InitializeComponent();
_container = container;
_regionManager = regionManager;
this.Loaded += MainWindow_Loaded;
}
private void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
_viewA = new ViewA();
_viewB = _container.Resolve<ViewB>();
_region = _regionManager.Regions["ContentRegion"];
_region.Add(_viewB);
_region.Add(_viewA);
}
}
there return same type like this.
(1) new ViewA(); // ActivationDeactivation.Views.ViewA
(2) _container.Resolve<ViewB>(); // ActivationDeactivation.Views.ViewB
What is difference between Container.Resolve() and new ViewA()?
What is difference between Container.Resolve<ViewA>() and new ViewA()?
Nothing really (besides new always returning a new instance, while ViewA might be registered as singleton).
But imagine new SomeService( aDependency, new AnotherDependency(), () => new SomeProduct( new ThirdDependency(), aDependency ) as opposed to Container.Resolve<SomeService>()...
Unity, or any DI-container, that is, just makes life easier when creating instances. Sure, you can do everything by hand, you don't have to. Be careful, though - you should not inject the container. Rather inject a factory, if you have to create instances on the fly.
Example of what MainWindow could look like:
public partial class MainWindow : Window
{
private readonly Func<ViewA> _viewAFactory;
private readonly Func<ViewB> _viewABFactory;
IRegionManager _regionManager;
IRegion _region;
ViewA _viewA;
ViewB _viewB;
public MainWindow(Func<ViewA> viewAFactory, Func<ViewB> viewBFactory, IRegionManager regionManager)
{
InitializeComponent();
_viewAFactory = viewAFactory;
_viewBFactory = viewBFactory;
_regionManager = regionManager;
this.Loaded += MainWindow_Loaded;
}
private void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
_viewA = _viewAFactory();
_viewB = _viewBFactory();
_region = _regionManager.Regions["ContentRegion"];
_region.Add(_viewB);
_region.Add(_viewA);
}
}
All of this should really happen in MainWindowViewModel, though. Have a look ViewModelLocator for a very simple way to make your views magically create their view models (properly resolved with all the dependencies).
I managed to set up DI using Unity:
App.xaml.cs
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
IUnityContainer container = new UnityContainer();
container.RegisterType<ApplicationDbContext>();
container.RegisterType<MainWindowViewModel>();
container.RegisterType<PageConsignmentsViewModel>();
container.RegisterType<MainWindow>();
container.RegisterType<PageConsignments>();
var mainWindow = container.Resolve<MainWindow>();
var pc = container.Resolve<PageConsignments>();
mainWindow.Show();
}
MainWindow.xaml.cs
public partial class MainWindow : Window
{
[Dependency]
public MainWindowViewModel ViewModel
{
set => DataContext = value;
}
public MainWindow()
{
InitializeComponent();
}
}
MainWindowViewModel.cs
public class MainWindowViewModel : BaseViewModel
{
private Page currentPage;
public Page CurrentPage { get { return currentPage; } set { currentPage = value; OnPropertyChanged("CurrentPage"); } }
private readonly ApplicationDbContext db;
public MainWindowViewModel(ApplicationDbContext db)
{
this.db = db;
CurrentPage = new PageConsignments();
}
#region Navigation Menu Commands
...
#endregion
}
And I can see that Db has been injected into MainWindow and the DataContext is correct (new instance of MainWindowViewModel).
However, when I navigate to ConsignmentsPage with CurrentPage = new PageConsignments I can see that the ConsingmentsPage.DataContext is null - presumably because this new view is not sitting in my Unity container.
I think I managed to resolve this by navigating with :
IUnityContainer container = new UnityContainer();
container.RegisterType<ApplicationDbContext>();
container.RegisterType<PageConsignmentsViewModel>();
var pc = container.Resolve<PageConsignments>();
CurrentPage = pc;
instead, but I was wondering - Is that the recommended method, or is there some way that I can define all these DIs in App.OnStartup rather than creating a new container for every navigation?
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.
i am trying to register multiple regions to my region like in Unity:
public class ModuleA : IModule
{
private IUnityContainer _container;
private IRegionManager _regionManager;
public ModuleA(IUnityContainer container, IRegionManager regionManager)
{
_container = container;
_regionManager = regionManager;
}
public void Initialize()
{
IRegion region = _regionManager.Regions[RegionNames.ToolbarRegion];
region.Add(_container.Resolve<ToolbarView>());
region.Add(_container.Resolve<ToolbarView>());
region.Add(_container.Resolve<ToolbarView>());
region.Add(_container.Resolve<ToolbarView>());
region.Add(_container.Resolve<ToolbarView>());
}
}
}
but i can't succeed to do it with MEF
[ModuleExport(typeof(ModuleAModule))]
public class ModuleAModule : IModule
{
[Import]
private IRegionManager _regionManager;
public void Initialize()
{
_regionManager.RegisterViewWithRegion(RegionNames.ContentRegion, typeof(ContentView));
IRegion region = _regionManager.Regions[RegionNames.ToolbarRegion];
}
}
i can get the region, but i don't know how to translate the resolving into MEF.
This is ModuleInit.cs in Products module
public class ModuleInit : IModule
{
private readonly IUnityContainer _container;
private readonly IRegionManager _regionManager;
public ModuleInit(IUnityContainer container, IRegionManager regionManager)
{
_container = container;
_regionManager = regionManager;
}
#region IModule Members
public void Initialize()
{
App app = (App)Application.Current;
_regionManager.RegisterViewWithRegion(RegionNames.ModuleRegionProducts, () => _container.Resolve<Views.ProductsCycle>());
}
#endregion
}
Below is button event handler in ProductsCycle.cs to go to another view still within same module:
private void btnForward_Click(object sender, RoutedEventArgs e)
{
IRegion productsRegion = _regionManager.Regions["ModuleRegionProducts"];
var productsListView = _container.Resolve<Views.ProductsList>();
productsRegion.Add(productsListView, "ProductsList");
productsRegion.Activate(productsListView);
}
State: ProductsCycle page is successfully loaded on first load.
Problem: View doesn't changed from ProductCycle page to ProductsList page when btnForward is clicked.
I'm using Silverlight 4 and PRISM2.
Please your advice, thank you.
change productsRegion.Activate(productsListView);
to productsRegion.Remove(this);
because my productListView is not inherited directly from UserControl.