I created a WPF MVVM app using MEF and Prism 5.0. I have MEF loading a module at startup called MobileRecruiterModule (below). I need to read some settings from App.config (or any config file really) and update the ViewModel properties with those config values so the View can pick them up.
Where is the appropriate place to load settings here? Do I do it in the MEF module (the thing that implements Microsoft.Practices.Prism.Modularity.IModule or in my View?
MobileRecruiterModule.cs
[ModuleExport(typeof (MobileRecruiterModule))]
public class MobileRecruiterModule : IModule
{
/// <summary>
/// The region manager
/// </summary>
[Import] public IRegionManager Region;
/// <summary>
/// Notifies the module that it has be initialized.
/// </summary>
public void Initialize()
{
Region.RegisterViewWithRegion(RegionNames.MainContentRegion, typeof (MobileRecruiterView));
}
...
}
MobileRecruiterView.xaml.cs
[Export("MobileRecruiterView")]
[PartCreationPolicy(CreationPolicy.Shared)]
[RegionMemberLifetime(KeepAlive = false)]
[Export]
public partial class MobileRecruiterView : UserControl
{
[Import]
public MobileRecruiterViewModel ViewModel
{
get { return (MobileRecruiterViewModel)DataContext; }
set { DataContext = value; }
}
[ImportingConstructor]
public MobileRecruiterView(MobileRecruiterViewModel vm)
{
InitializeComponent();
DataContext = vm;
}
}
MobileRecruiterViewModel.cs
[Export]
public class MobileRecruiterViewModel : BindableBase
{
public string DatabaseServer { get; set; }
... and a few other properties that the XAML view binds to ...
}
I would suggest that you should load your settings in ViewModel constructor. Because your ViewModel is the DataContext for the View, you have to initialize it before you show it. I hope that you do not store any BLOB in it, so the time for *.config loading will be small enough to do it on UI thread.
Related
I am a WPF application beginner and I'm currently using MvvmLight for my application.
I have a MainViewModel that holds a ObservableCollection of ChildViewModels(Type ViewModelBase). Each ChildViewModel is bound to a tab item in the XAML. So, I have a TabControl with tab items and each tab item has its own View and ViewModel.
This is my MainViewModel. For now, I have only one ChildViewModel which is DozentViewModel.
public class MainViewModel : ViewModelBase
{
private ObservableCollection<ViewModelBase> _vmCollection;
public ObservableCollection<ViewModelBase> VmCollection
{
get { return _vmCollection; }
set
{
Set(ref _vmCollection, value);
RaisePropertyChanged("VmCollection");
}
}
private DozentViewModel _dozentviewmodel;
public DozentViewModel Dozentviewmodel
{
get { return _dozentviewmodel; }
set
{
Set(ref _dozentviewmodel, value);
}
}
/// <summary>
/// Initializes a new instance of the MainViewModel class.
/// </summary>
public MainViewModel(DozentViewModel dozentViewModel)
{
_dozentviewmodel = dozentViewModel;
VmCollection = new ObservableCollection<ViewModelBase>();
VmCollection.Add(_dozentviewmodel);
}
This is my ViewModelLocator code:
public ViewModelLocator()
{
ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);
SimpleIoc.Default.Register<MainViewModel>();
SimpleIoc.Default.Register<DozentViewModel>();
}
public MainViewModel Main
{
get
{
return ServiceLocator.Current.GetInstance<MainViewModel>();
}
}
public DozentViewModel DozentVM
{
get
{
return ServiceLocator.Current.GetInstance<DozentViewModel>();
}
}
I am trying to implement Dependency Injection by using Constructor Injection in the MainViewModel. The above code works but I am not sure if my implementation of DI is right. I would like to have an interface of IChildViewModel with which all my ChildViewModels will be implemented. If I use an interface like so, how can I achieve DI?
I have been trying to set up dependency injection in a wpf application using Unity, but can't seem to fully understand how the views and viewmodels should be set up.
Have looked into another SO post --> Wpf Unity but can't seem to understand it quite yet. I have used Unity before, but just in a MVC application, so I know how to inject it in the contructors.
Here is my views and viewModels in the application.
Views:
MainWindow.xaml
BookingView.xaml
ContactDetailsView.xaml
ReservationsView.xaml
ViewModels:
MenuViewModel (MainWindow uses this viewModel)
BookingViewModel
ContactViewModel
ReservationsViewModel
My ViewModels all have Interfaces implemented, like IMenuViewModel, should the view also have an interface?
I guess that since the MainWindow is the starting point, it should be here to register the container right?
Update:
Have found something, but not sure if I have done it right. Here is what I have done so far!
1: Using startup method in app.cs
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
IUnityContainer container = new UnityContainer();
container.RegisterType<IViewMainWindowViewModel, MainWindow>();
container.RegisterType<IViewMainWindowViewModel, MenuViewModel>();
var mainWindow = container.Resolve<MainWindow>(); // Creating Main window
mainWindow.Show();
}
}
2: Remove uri from start up.
3: Make IViewMainWindowViewModel interface in MainWindow class, the interface is empty.
public interface IViewMainWindowViewModel
{
}
4: Make a reference to this interface in the MainWindow
public partial class MainWindow : Window, IViewMainWindowViewModel
{
public MainWindow(IViewMainWindowViewModel viewModel)
{
InitializeComponent();
DataContext = viewModel;
}
5: Also for the MenuViewModel
public class MenuViewModel : IViewMainWindowViewModel
{
Code not shown!
}
This will not even start the application..
Update 2
My MainWindow class look like this:
public interface IViewMainWindowViewModel
{
}
public partial class MainWindow : Window, IViewMainWindowViewModel
{
public MainWindow(IViewMainWindowViewModel viewModel)
{
InitializeComponent();
DataContext = viewModel;
}
App class now look like this:
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
IUnityContainer container = new UnityContainer();
container.RegisterType<IViewMainWindowViewModel, MainWindow>();
container.RegisterType<IViewMainWindowViewModel, MenuViewModel>();
container.Resolve<MainWindow>().Show();
//Do the same actions for all views and their viewmodels
}
I get an exception on this line when running the application
container.Resolve<MainWindow>().Show();
Update 3
In my MenuViewModel it has two command which will open two views, do I then need to inject those views in the MenuViewModel's constructor or can you just make another empty interface between MenuViewModel and BookingView as an example?
Let me show an example with explanations just for your MainWindows, as for the rest views and viewmodels steps to do are the same.
At first, you should create a contract between View and ViewModel. It shoud be some interface and let it call IViewMainWindowViewModel (keep in mind that name has to be different for other view and viewModels, for example IViewBookingViewViewModel):
public interface IViewMainWindowViewModel
{
/*This interface should not have any methods or commands. It is just
contract between View and ViewModels and helps to decide to Unity
container what it should inject(appropriate viewModel to necessary
View)*/
}
then in your viewmodel we should implement this interface:
public MenuViewModel:IViewMainWindowViewModel
{}
The view should inject this interface MainWindows.xaml.cs:
public partial class MainWindows : UserControl, IContentAView
{
public MainWindows(IViewMainWindowViewModel viewModel)
{
InitializeComponent();
DataContext = viewModel;
}
}
Delete StartupUri and override a method OnStartup in App.xaml.cs:
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
IUnityContainer container = new UnityContainer();
container.RegisterType<IViewMainWindowViewModel, MainWindow>();
container.RegisterType<IViewMainWindowViewModel, MainWindowViewModel >();
container.Resolve<MainWindow>().Show();
//Do the same actions for all views and their viewmodels
}
I am trying to understand how to switch views and its viewmodels in a wpf mvvm application which uses prism and unity. I put something together from a tutorial but have additional question bec a few things dont seem right. What I have so far is a WPF application with a shell.xaml window that has section placeholders using prism regions. In addition, I have a bootstrapper class that register modules which will fill in the different regions in the shell.xaml window. In the modules which are class libraries I have the initialize function setting the views and viewmodels. I have only two regions in this application the navigation and the workspace. I have 2 buttons on the navigation and they change the view in the workspace. The workspace views are in their own modules. So at this point each workspace view has its own class library module. In a big application this seems unreasonable of having each view have its own class library. I wanted to know how can I have multiple views and viewmodels in one class library and swap them in and out. If you have a good step by step tutorial that would be great.
I know this is 4 years late, but I ll give an answer anyway.
First thing you do is add the Views you need to the Views folder and add their corresponding ViewModels in the ViewModels folder so that you have a structure as described below :
ViewModels
ViewModelA
ViewModelB
Views
ViewA
ViewB
The ViewModels and their corresponding view could look something like this :
ViewModelA
using System;
namespace SomeApp.DemoModule.ViewModels
{
public class ViewModelA : INavigationAware
{
public ViewModelA(IUnityContainer container, IRegionManager regionManager, IEventAggregator eventAggregator)
{
this.container = container;
this.regionManager = regionManager;
this.eventAggregator = eventAggregator;
}
public bool IsNavigationTarget(NavigationContext navigationContext)
{
return true;
}
public void OnNavigatedFrom(NavigationContext navigationContext)
{
}
public void OnNavigatedTo(NavigationContext navigationContext)
{
//Do stuff here
//For navigation back
navigationService = navigationContext.NavigationService;
}
#region Executes
/// <summary>
/// Command when ViewB button clicked
/// </summary>
public void Execute_ViewBCommand()
{
regionManager.RequestNavigate("REGIONNAME_HERE", new Uri("ViewB", UriKind.Relative));
}
ViewA
<UserControl x:Class="DemoModule.Views.ViewA"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:ViewInjection.Views"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<Button Content="VIEWB" FontSize="38" Command="{Binding ViewBCommand}"></Button>
</Grid>
ViewA.Xaml.cs
namespace SomeApp.DemoModule.Views
{
/// <summary>
/// Interaction logic for ViewA.xaml
/// </summary>
public partial class ViewA : UserControl
{
public ViewA(ViewModelA model)
{
InitializeComponent();
this.DataContext = model;
}
}
}
ViewModelB
using System;
namespace SomeApp.DemoModule.ViewModels
{
public class ViewModelB : INavigationAware
{
public ViewModelB(IUnityContainer container, IRegionManager regionManager, IEventAggregator eventAggregator)
{
this.container = container;
this.regionManager = regionManager;
this.eventAggregator = eventAggregator;
}
public bool IsNavigationTarget(NavigationContext navigationContext)
{
return true;
}
public void OnNavigatedFrom(NavigationContext navigationContext)
{
}
public void OnNavigatedTo(NavigationContext navigationContext)
{
//Do stuff here
//For navigation back
navigationService = navigationContext.NavigationService;
}
#region Executes
/// <summary>
/// Command when ViewA button clicked
/// </summary>
public void Execute_ViewACommand()
{
regionManager.RequestNavigate("REGIONNAME_HERE", new Uri("ViewA", UriKind.Relative));
}
ViewB
<UserControl x:Class="DemoModule.Views.ViewB"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<Button Content="VIEWA" FontSize="38" Command="{Binding ViewACommand}"></Button>
</Grid>
ViewB.Xaml.cs
namespace SomeApp.DemoModule.Views
{
/// <summary>
/// Interaction logic for ViewB.xaml
/// </summary>
public partial class ViewB : UserControl
{
public ViewB(ViewModelB model)
{
InitializeComponent();
this.DataContext = model;
}
}
}
You can register your views in several ways, we use a DemoModuleInit class in the project root that registers the view :
DemoModuleInit
public class DemoModuleInit : IModule
{
private IRegionManager regionManager;
/// <summary>
/// Bind your interfaces, subscribe to events, do stuff that needs to be done on intialization of module
/// </summary>
public void OnInitialized(IContainerProvider containerProvider)
{
// Setup Event listeners etc...
regionManager = containerProvider.Resolve<IRegionManager>();
}
/// <summary>
/// Register your views for this Module
/// </summary>
public void RegisterTypes(IContainerRegistry containerRegistry)
{
containerRegistry.RegisterForNavigation<ViewA>();
containerRegistry.RegisterForNavigation<ViewB>();
}
If implemented correctly you should be able to navigate from ViewA to ViewB and back within the same module
For more information on Prism check out :
Prsim on GitHub
you can have as many views in your module as you need. You just need to navigate through them. To do so, you need to register them and then from each of the viewmodel you can request navigate to another view from your regionmanager. Have a look here prism navigation
Using Prism, MVVM with View First View Injection: Unable to load Image in Module
I am using WPF and Prism in an MVVM pattern using the View First, View Injection pattern.
In my solution I have a SG.BannerModule project in which I just want to bind an Image in my BannerView.xaml.
I use the below BannerViewModel that implements IBannerViewModel because I am using containers to resolve my BannerViewModel.
Here is the IBannerViewModel interface:
public interface IBannerViewModel : IViewModel
{
Uri BannerImageUri { get; set; }
}
I exposed the BannerImageUri because I am resolve the BannerViewModel thru the container in the
BannerMainModule using the Interface.
The BannerViewModel implementation is
public class BannerViewModel : ViewModelBase, IBannerViewModel
{
#region Properties
private IUnityContainer _container;
private ILogger _logger;
private Uri _bannerImageUri;
public Uri BannerImageUri
{
get { return new Uri("pack://SG.Website:,,,SG.BannerModule;component/Assets/SGBanner2.png"); }
set
{
if (value != _bannerImageUri)
{
_bannerImageUri = value;
OnPropertyChanged("BannerImageUri");
}
}
}
#endregion
#region Constructors
public BannerViewModel(IBannerView bannerView, IUnityContainer container)
: base(bannerView)
{
View = bannerView;
View.ViewModel = this;
_container = container;
_logger = _container.Resolve<ILogger>();
_logger.WriteToLogs("BannerViewModel Constructor");
}
#endregion
}
The Image is located in the Assets directory of my SG.BannerModule project.
I have set the build properties for the Image SGBanner2.png to be a resource and have added the image in the resources tab of the Properties Pane for the SG.BannerModule project.
I have set the data context of the BannerView.xaml in the BannerView.xaml.cs file in the following way because I am using View Injection.
public partial class BannerView : UserControl, IBannerView
{
public BannerView()
{
InitializeComponent();
}
public IViewModel ViewModel
{
get
{
return (IBannerViewModel) DataContext;
}
set { DataContext = value; }
}
}
Because of the View First View Injection pattern I set the DataContext in my in code behind and do not set any in the XAML.
My question is
Because I am binding this image, do I need to use an ImageSource converter? If this is the case, will it be a problem because the image is a png and not a bitmap?
Or is the problem the Pack Uri. Because I am using this module in a region in my SG.WebSite project, I am not sure that my pack Uri is correct but I have not been able to trouble shoot correctly why my image is not showing up in my shell window.
Stumped?
Thanks!
Just so you know, it appears you are using ViewModel first. Having your ViewModel resolve your View and then probably calling region.Add(viewModel.View).
This is the packURI syntax for local resource files:
pack://application:,,,/Subfolder/ResourceFile.xaml
For reference resource files:
pack://application:,,,/ReferencedAssembly;component/Subfolder/ResourceFile.xaml
How do we inject IRegionManager in the ViewModel using MEF Container. I have to switch view in my ViewModel's Command delegate. Here is the brief description of what I am doing. I have an entity called Product whose list is displayed in one View (ProductListView). In that view the user can select the Product and click on Edit button. This would switch the view and present a new View(ProductEditView). For activating a different view, I would need a reference to IRegionManager something like this
public class ProductListVM : NotificationObject { //The Product List View Model
[Import]
public IRegionManager RegionManager { get; set; }
private void EditProduct() { //EditCommand fired from ProductListView
IRegion mainContentRegion = RegionManager.Regions["MainRegion"];
//Switch the View in "MainContent" region.
....
}
}
The above code fails with NullReferenceException for RegionManager. This seems logical because the above View Model is constructed by WPF through DataContext property in Xaml and DI doesn't come into play, so it doesn't get a chance to import the RegionManager instance. How do we resolve the IRegionManager in this scenario.
The Container instance can be exported in the bootstrapper using following
container.ComposeExportedValue<CompositionContainer>(container);
Then in the viewmodel, the IRegionManager instance can be imported using the code
IServiceLocator serviceLocator = ServiceLocator.Current;
CompositionContainer container = serviceLocator.GetInstance<CompositionContainer>();
RegionManager = container.GetExportedValue<IRegionManager>();
However, referring a View in ViewModel is a violation of the MVVM pattern. But since I was following an article here to learn Prism , I had to get along the same. Also the article was in Silverlight and I had to find a way to import RegionManager in wpf, which is little different.
regards,
Nirvan.
Try using [ImportingConstructor] like this:
public class ProductView : Window
{
private IProductViewModel viewModel;
[ImportingConstructor]
public ProductView(IProductViewModel ViewModel)
{
this.viewModel = ViewModel;
this.DataContext = this.viewModel;
}
}
public class ProductViewModel: IProductViewModel, INotifyPropertyChanged
{
private IRegionManager regionManager;
private ICommand editUserCommand;
[ImportingConstructor]
public ProductViewModel(IRegionManager InsertedRegionManager)
{
this.regionManager = InsertedRegionManager;
editUserCommand = new DelegateCommand(ExecuteEditUserCommand, CanExecuteEditUserCommand);
}
public ICommand EditUserCommand
{
get {return this.editUserCommnad;}
}
private bool CanExecuteEditUserCommand()
{
return true;
}
private void ExecuteEditUserCommand()
{
this.regionManager......
}
}