Bellow you can see my bootstrapper. I want to register all the views from the bootstrapper.
When I start the application, WebView and EditView are created. GeneralView is a part of EditView and I have to navigate first to EditView in order to instantiate it.
How can I instantiate all the views when starting the executable?
class Bootstrapper : UnityBootstrapper
{
protected override DependencyObject CreateShell()
{
// Register views
IRegionManager regionManager = this.Container.Resolve<IRegionManager>();
regionManager.RegisterViewWithRegion("ContentRegion", typeof(WebView));
regionManager.RegisterViewWithRegion("ContentRegion", typeof(EditView));
// The following view is instantiated for the first time when I navigate to EditView
regionManager.RegisterViewWithRegion("GeneralRegion", typeof(GeneralView));
return Container.Resolve<MainWindow>();
}
protected override void InitializeShell()
{
Application.Current.MainWindow.Show();
}
protected override void InitializeModules()
{
base.InitializeModules();
}
}
A view shouldn't be instantiated before it is actually being displayed on the screen. Besides, a view should only define the user interface.
If you expect a specific view model to be alive when you are sending an event using the event aggregator from another view model, you are actually introducing an indirect coupling between these two view models. And this is exactly what you want to avoid by using an event aggregator in the first place.
So if you rely on all events being handled, you should probably consider using a shared service that you instantiate as a singleton in the bootstrapper. You could then inject your view models with this shared service and communicate between them through the service interface.
Related
I am using WPF Prism 6 with autofac and having issues navigating between views. What I have is a view that I only want to keep alive till I leave it, and the next time I navigate to it, I want to create a new version of this view.
On load, I regist an IModule that has the following code
_regionManager.RegisterViewWithRegion(RegionNames.MainRegion,
typeof(DxfDisplay.Views.DxfDisplay));
This registers my view and the system works on initial load, I implement the INavigationAware and IRegionMemberLifetime interfaces on the view model and have public bool KeepAlive => false; implementing the IRegionMemberLifetime so that my view is disposed when I am done.
When I navigate away from this view everything is fine, but when I attempt to navigate to navigate to the view using
_regionManager.RequestNavigate(RegionNames.MainRegion,
new Uri("DxfDisplay", UriKind.Relative), parameters);
The view is not opened and a view model constructor is not called. To make the navigation work correctly, I need to register with view with the region again. Or if I change the KeepAlive to true I can navigate back to the original view, but I cannot generate a new view if INavigationAware.IsNavigationTarget returns false.
My question is how do I register the view with the region manager in such a way that when I call _regionManager.RequestNavigate, it will create a new instance of the view and display it. I feel like I am missing something simple and just overlooking it.
_builder.RegisterTypeForNavigation<DxfDisplay.Views.DxfDisplay>();
In Prism 7, this is now called RegisterForNavigation<T>() and exists on the IContainerRegistry interface.
For example, in your module:
public class MyModule : IModule
{
public void RegisterTypes(IContainerRegistry containerRegistry)
{
containerRegistry.RegisterForNavigation<MyView>();
}
}
I am using IRegionManager to load and navigate to views, I have no problem loading content to my main region in my main view which is loaded with my bootstrapper class but I cant load content to regions inside my loaded views, the region manager does not seem to be registering these regions.
my bootstrapper class:
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<DocumentView>();
this.Container.RegisterTypeForNavigation<EmailView>();
this.Container.RegisterTypeForNavigation<WorkTypeSelectionView>();
}
the DocumentView is user control with another region the method that runs when the command is triggered is this:
private void ViewEmailAction()
{
NavigationParameters parameters;
parameters = new NavigationParameters();
parameters.Add(nameof(this.CurrentEmail), this.CurrentEmail);
this.regionManager.Regions[this.EmailRegion].RequestNavigate(nameof(EmailView), parameters);
}
This throws and exception with the message "The region manager does not contain the EmailRegion region."
Thanks in advance!
There are two different things going on here:
UserControls:
This should work with not issues whatsoever. Chances are that you are trying to navigate to a region that is defined in a View that hasn't been loaded yet. Make sure you are navigating to a region after it has been loaded. Navigating inside of ViewModel constructors is one of the biggest sources of this problem. If you want to post your sample to GitHub, I can take a look.
ControlTemplates:
This is a known issue in Prism. Here is your fix:
http://southworks.com/blog/2011/11/10/regions-inside-datatemplates-in-prism-v4-using-a-region-behavior/
First of all this is my first contact with Caliburn.Micro, C# and WPF. I have gone through the Calibur.Micro tutorial and stopped in the moment of "All about actions" and First View subsection. Author wrote the solution for Silverlight application as follow:
public class MefBootstrapper : BootstrapperBase
{
//same as before
protected override void OnStartup(object sender, StartupEventArgs e)
{
Application.RootVisual = new ShellView();
}
//same as before
}
So this is solution how to say bootstrapper which view use as base to show. About WPF I get only enigmatic information:
In this scenario, we simply override OnStartup, instantiate the view
ourselves and set it as the RootVisual (or call Show in the case of
WPF).
So the Silverlight example is very clear for me, we just manually instantiate the proper View to property Application.RootVisual. But is for me totally unclear what is the Show method, which member it is. How to call it.
Thanks for help!
Caliburn.Micro provides a BootstrapperBase class from which you can inherit your own bootstrapper class. It has a virtual method OnStartup which you can override to do the initialization of your shellview. It also provides a utility method DisplayRootViewFor which can be used for displaying the related view for a specified viewmodel type.
So a simple implementation would look like this,
protected override void OnStartup(object sender, StartupEventArgs e)
{
DisplayRootViewFor<TShellViewModel>();
}
where TShellViewModel is the type of your Shell ViewModel. Framework will resolve the view using convention and does the necessary groundwork to display the same. This link will provide a broader picture for a MEF based IOC supported bootstrapper implementation for WPF.
This is more complex than it sounds. I'm implementing MVVM pattern, which states a ViewModel cannot have a reference to it's View. That being said, I'm implementing Page navigation so that changes in views are done by using NavigationService in the View's code behind (let's say by pressing the "Next" button).
At some point in the program, we need to change Page using a voice command instead of a button (using speech recognition), and that logic is handled in the ViewModel (which doesn't have a reference to NavigationService).
So, without keeping a reference to thew View inside the ViewModel, how can I change page using NavigationService?
You could publish a "next page requested" message from within your view model using something like an event aggregator. Your view then subscribes to the message and handles it by using NavigationService to change the page. If you are using an MVVM framework most of them provide a way to publish / subscribe to messages out of the box.
In your Core (nonUI) project that containts your view models. Create a INavigationService interface:
public interface INavigation
{
void Navigate(IViewModel viewmodel);
void GoBack();
}
Then in your UI project, provide the implementation for that interface. You can get fancy with how you provide that implementation to the view model.
In the simple form you'd want to do something like:
public class MyViewModel
{
public MyViewModel(INavigationService navigationService)
{
_navigationService = navigationService;
}
}
When the app starts up give the view model the implementation. At that point all your navigation can live in the view model. If you need to navigate from a view, execute a command on the view model and have it navigate.
Take a look at how MvvmLight does it:
INavigationService,
NavigationService
I'm totally new in Prism (Composite Wpf). I want to create messaging module for my application: invisible panel on the top of the main window which appears when I invoke ShowMessage(string message) (and disappears after 5 seconds, for instance).
What I've done:
Create infrastructure project (contains only one interface IUIMessagesService)
Create module project:
Project contains user control - it's panel for the message (View)
Project contains UIMessagesService class, which implements IUIMessagesService
In module class I did so:
public UIMessagesModule(IRegionManager regionManager, IUnityContainer container)
{
_regionManager = regionManager;
_container = container;
}
and
public void Initialize()
{
_regionManager.RegisterViewWithRegion("UIMessagesRegion", typeof(UIMessagesView));
_container.RegisterType<IUIMessagesService, UIMessagesService>(new ContainerControlledLifetimeManager());
}
Create shell project (bootstrapper, shell view with region e.t.c)
Questions:
How can I change properties of my view in class UIMessagesService (in this case RenderTrasform to show panel)? May be I need define theese properties in view model? How to change view model properties?
How to execute module methods ShowMessages from application?
For your first question you can use Event Aggregation
For second:
you can use ServiceLocator or container to resolve your type
Not sure if this is exactly you wanted. But you can use wpfextended toolkit busyindicator. This can show on top of your view with a glossy screen and you can control it just by setting or binding IsBusy dependency property.
take a look at example here