What is difference between Container.Resolve<View>() and new View()? - c#

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).

Related

Why my Subscribe method is not called when using Prism EventAggregator?

I am learning Prism. Few hours already I am facing a problem, when subscribing to the event, the subscription method is not called. I am using Prism and Autofac.
In the simplified example below, in MainViewModel Publish("dupa"); event is called in the ctor. And on button click UpdateWindow is opened. In backend of the window is created instance of UpdateViewModel.
Inside of update VM ctor is ran, but after Subscribe(UpdateName); the UpdateName is not executed, because of some reason that I do not understand.
Complete code:
public class MainViewModel : ViewModelBase
{
private IEventAggregator _eventAggregator;
public MainViewModel(IEventAggregator eventAggregator)
{
_eventAggregator = eventAggregator; //Prism
_eventAggregator.GetEvent<UpdateNameEvent>().Publish("dupa");
OnOpenCommand = new DelegateCommand(OnOpenWin);
}
public void OnOpenWin(object obj)
{
UpdateWindow win = new UpdateWindow();
win.Show();
}
public ICommand OnOpenCommand { get; private set; }
}
public class UpdateViewModel : ViewModelBase
{
private IEventAggregator _eventAggregator;
public UpdateViewModel(IEventAggregator eventAggregator)
{
_eventAggregator = eventAggregator; //Prism
_eventAggregator.GetEvent<UpdateNameEvent>().Subscribe(UpdateName);
}
private void UpdateName(string name)
{
this.Name = name; //is not called at all
}
private string _name;
public string Name
{
get
{
return _name;
}
set
{
_name = value;
OnPropertyChanged();
}
}
}
public partial class UpdateWindow : Window
{
public UpdateWindow()
{
var bootStrapper = new BootStrapper();
var container = bootStrapper.BootStrap();
UpdateViewModel vm = container.Resolve<UpdateViewModel>();
InitializeComponent();
DataContext = vm;
}
}
UPDATE
After investigating, I notied, that when subscribing to the events like this, it works fine:
Utility.EventAggregator.GetEvent<UpdateNameEvent>().Subscribe(UpdateName);
When subscribing with used injected eventAggregator, it does not work:
_eventAggregator.GetEvent<UpdateNameEvent>().Subscribe(UpdateName);
And EventAggregator is registered by Autofac as follows:
builder.RegisterType<EventAggregator>()
.As<IEventAggregator>().SingleInstance();
I do not understand why this dependency does not work?
I do not understand why this dependency does not work?
Because you create a new EventAggregator for the UpdateViewModel.
var bootStrapper = new BootStrapper();
var container = bootStrapper.BootStrap();
UpdateViewModel vm = container.Resolve<UpdateViewModel>();
This looks as if a new container is created for the UpdateWindow, and the new container will have a new - that is, a different - EventAggregator. Those two will not send events to each other, of course.
So the solution is to use one single container to resolve all your stuff. This is what happens when you use the static Utility. You should avoid using a service-locator like this. Have a look at the ViewModelLocator, which makes it really easy to create the view models for a given view, for example, or pass the container to the UpdateWindow when it is created (somewhat ugly, too, though).

How to pass Unity container to child page from window for DI

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?

Switch View in different Modules using Prism and Unity

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>();

Showing Prism modules inside Prism Module

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.

PRISM View Injection/Navigation in Same Module

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.

Categories

Resources