Select which view to display when Main Window open in Prism - c#

This really should be easy, but I could not establish it.
I have small WPF application with Prism 6
I have Main Window and two views inside it.
MainWindow with MainWindowViewModel view model class
ConfigurationView with ConfigurationViewModel view model class
SignInView with SignInViewModel view model class
Now when show the main window for the first time, I want to select which view to show according to some boolean condition
here is the snippet of the bootstrap class.
protected override void ConfigureViewModelLocator()
{
base.ConfigureViewModelLocator();
ViewModelLocationProvider.Register<MainWindow, MainWindowViewModel>();
ViewModelLocationProvider.Register<SignInView, SignInViewModel>();
ViewModelLocationProvider.Register<ConfigurationView, ConfigurationViewModel>();
Container.RegisterInstance(new SignInView());
Container.RegisterInstance(new ConfigurationView());
}
protected override DependencyObject CreateShell()
{
return Container.Resolve<MainWindow>();
}
protected override void InitializeShell()
{
Application.Current.MainWindow.Show();
IRegionManager _regionManager = Container.Resolve<IRegionManager>();
IRegion _region = _regionManager.Regions[RegionNames.MainRegion];
_region.Add(Container.Resolve<SignInView>());
_region.Add(Container.Resolve<ConfigurationView>());
}
Right now always the SignInView is displayed when the main window of the application is opened?
How can I select which view to show according to some condition which need to be brought from the MainWindowViewModel class ?
Update
public class MainWindowViewModel
{
private IAccountService _accountService;
public MainWindowViewModel(IUnityContainer container)
{
IRegionManager regionManager = Container.Resolve<IRegionManager>();
_accountService = container.Resolve<IAccountService>();
if (_accountService.IsSignedIn)
regionManager.RequestNavigate(RegionNames.MainRegion, new Uri(nameof(ConfigurationView), UriKind.Relative));
else
regionManager.RequestNavigate(RegionNames.MainRegion, new Uri(nameof(SignInView), UriKind.Relative));
}
}
Main Window View
<Window x:Class="Shell.Views.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:prism="http://prismlibrary.com/"
prism:ViewModelLocator.AutoWireViewModel="True">
this code is not working.

The region needs to be created before you can add a view to it. So you'd better create the MainViewModel yourself after the MainWindow has been created:
protected override void ConfigureViewModelLocator()
{
base.ConfigureViewModelLocator();
ViewModelLocationProvider.Register<SignInView, SignInViewModel>();
ViewModelLocationProvider.Register<ConfigurationView, ConfigurationViewModel>();
Container.RegisterInstance(new SignInView());
Container.RegisterInstance(new ConfigurationView());
}
protected override DependencyObject CreateShell()
{
return Container.Resolve<MainWindow>();
}
protected override void InitializeShell()
{
IRegionManager _regionManager = Container.Resolve<IRegionManager>();
IRegion _region = _regionManager.Regions[RegionNames.MainRegion];
_region.Add(Container.Resolve<SignInView>());
_region.Add(Container.Resolve<ConfigurationView>());
var mainWindowViewModel = Container.Resolve<MainWindowViewModel>();
Application.Current.MainWindow.DataContext = mainWindowViewModel;
Application.Current.MainWindow.Show();
}
Remove this from the MainWindow:
prism:ViewModelLocator.AutoWireViewModel="True">

First of all, register the types of your views for navigation, not view instances.
Secondly: conditions - or more general: data - rarely originates from a view model (unless it's user input), so you should have some IUserManagement service to pull the current user from.
But that being said, if you have your view registered, you should be able to navigate to it. Without a concrete exception, it's difficult to guess what's going wrong.
Container.RegisterTypeForNavigation<SignInView>();
...
_regionManager.RequestNavigate( RegionNames.MainRegion, typeof(SignInView).Name );

Related

Prism/WPF views/viewmodels being created twice

I have a Prism 7 / WPF / MVVM app that is configured with AutowireViewModel="True" in the views that have a viewmodel. Most of my view models have dependencies that I have configured in the Prism Unity container and these dependencies are injected into the viewmodel contructor. I do not explicitly create instances of views/viewmodels in the code behind anywhere. I also do not set the data context in XAML i.e. using DataContext element or d:DataContext). The only references in the XAML are to views (i.e. part of the HamburgerMenu control).
All is working fine except each view/viewmodel is ALWAYS constructed twice for some reason, whether this is the main window, or views within modules. I have placed breakpoints in the module managers (only gets hit once) - and also in the view constructors and viewmodel constructors which are both hit twice.
The following is some code for App class and also some code/view/viewmodel for a module named Messaging that is loaded at runtime not on demand. Due to wishing to use the MVVM pattern as much as possible, I have very little code in the code behind of the views. NOTE: I tried to see if this is just an issue with views for modules that are loaded on start up, but the same fate is present for the modules loaded on demand.
The App/PrismApplication class in full:
public partial class App : PrismApplication
{
private ILoggerFactory loggerFactory;
private TaskbarIcon _taskbarIcon;
protected override Window CreateShell()
{
// Register an instance of the Window (used by the dialog service to get a handle on the window when displaying dialogs)
Container.GetContainer().RegisterInstance(typeof(Window), "MainWindow", Container.Resolve<MainWindow>(), new ContainerControlledLifetimeManager());
return Container.Resolve<MainWindow>();
}
protected override void OnInitialized()
{
base.OnInitialized();
// Create the links to each module for the banner view to display
var menuService = Container.Resolve<IMenuService>();
var regionManager = Container.Resolve<IRegionManager>();
menuService.AddItem("Home", "Home", () => regionManager.RequestNavigate("MainRegion", "HomeMainView"));
menuService.AddItem("Messaging", "Messaging", () => regionManager.RequestNavigate("MainRegion", "MessagingMainView"));
menuService.AddItem("Charts", "Charts", () => regionManager.RequestNavigate("MainRegion", "ChartsMainView"));
menuService.AddItem("Admin", "Admin", () => regionManager.RequestNavigate("MainRegion", "AdminMainView"));
// Register banner view with region manager and display it
regionManager.RegisterViewWithRegion("BannerRegion", typeof(BannerView));
// Load the desired module into the main window on start up
Container.Resolve<IRegionManager>().RequestNavigate("MainRegion", "HomeMainView");
}
protected override void RegisterTypes(IContainerRegistry containerRegistry)
{
var container = this.ConfigureLogging(containerRegistry);
// Register types
container.RegisterInstance<IDbConnectionFactory>(
new SqlConnectionFactory(
ConfigurationManager.ConnectionStrings["Messaging"].ConnectionString,
loggerFactory));
/************************************************************/
// TODO: Not sure if need singletons or not - to test......
/************************************************************/
containerRegistry.RegisterSingleton<IMetroMessageDisplayService, MetroMessageDisplayService>();
containerRegistry.RegisterSingleton<IUserService, UserService>();
containerRegistry.RegisterSingleton<IUserStore, UserStore>();
containerRegistry.RegisterSingleton<IMenuService, MenuService>();
containerRegistry.Register<ICustomerService, CustomerService>();
containerRegistry.Register<INotifyIconService, NotifyIconService>();
containerRegistry.RegisterDialog<DefaultDialog, DefaultDialogViewModel>("Default");
containerRegistry.RegisterDialog<HtmlDialog, HtmlDialogViewModel>("Html");
// Get the current user's details - prevent a deadlock in the way we use Task.Run(...).Result
// Then add to container as we will be injecting into VMs
var clientUser = Task.Run(GetCurrentUserDetails).Result;
containerRegistry.RegisterInstance(clientUser);
containerRegistry.RegisterSingleton<IClientUser, ClientUser>();
// Add the task bar icon
_taskbarIcon = (TaskbarIcon)FindResource("NotifyIcon");
containerRegistry.RegisterInstance(_taskbarIcon);
// Create a logger instance
var logger = loggerFactory.CreateLogger<App>();
logger.LogDebug("Finished registering types in App.xaml.cs");
}
private async Task<ClientUser> GetCurrentUserDetails()
{
var userService = Container.Resolve<IUserService>();
var data = await userService.GetClientUsersAsync(ConfigurationManager.AppSettings.Get("TempUserName")).ConfigureAwait(true);
if (!data.Any())
{
// log unable to load user from database then return as no point in loading messages for a user that cannot be found!!
return null;
}
return data.FirstOrDefault();
}
protected override IModuleCatalog CreateModuleCatalog()
{
// We are returning a type that reads the modules from the config file.
return new ConfigurationModuleCatalog();
}
protected override void OnExit(ExitEventArgs e)
{
// The icon would clean up automatically, but this is cleaner
_taskbarIcon.Dispose();
base.OnExit(e);
}
private IUnityContainer ConfigureLogging(IContainerRegistry containerRegistry)
{
// Configure logging - Needed Unity.Microsoft.Logging package
// see https://github.com/unitycontainer/microsoft-logging and https://github.com/unitycontainer/examples/tree/master/src/Logging/Microsoft.Logging
var serilogLogger = new LoggerConfiguration()
.ReadFrom.AppSettings()
.CreateLogger();
this.loggerFactory = new LoggerFactory().AddSerilog(serilogLogger);
var container = containerRegistry.GetContainer();
container.AddExtension(new LoggingExtension(loggerFactory));
return container;
}
}
The messaging module:
public class MessagingModule : IModule
{
public void OnInitialized(IContainerProvider containerProvider)
{
// Register main view with region manager
var regionManager = containerProvider.Resolve<IRegionManager>();
regionManager.RegisterViewWithRegion("MainRegion", typeof(MessagingMainView));
}
public void RegisterTypes(IContainerRegistry containerRegistry)
{
containerRegistry.RegisterSingleton<IMessageStore, MessageStore>();
containerRegistry.RegisterSingleton<IMessageService, MessageService>();
containerRegistry.RegisterSingleton<IChatService, ChatService>();
containerRegistry.RegisterSingleton<IFileDialogService, FileDialogService>();
containerRegistry.RegisterDialog<InboxMessageView, InboxMessageViewModel>("HtmlMessage");
// Here we are loading the config/settings from the app.config for this module so we
// can register a ChatServiceConfiguration type as it is injected into ChatService
var filename = Assembly.GetExecutingAssembly().Location;
var configuration = ConfigurationManager.OpenExeConfiguration(filename);
if (configuration != null)
{
var hubUrl = configuration.AppSettings.Settings["HubUrl"].Value;
if (string.IsNullOrEmpty(hubUrl))
{
throw new ArgumentException("The HubUrl app setting cannot ne null or whitespace.");
}
containerRegistry.RegisterInstance(new ChatServiceConfiguration(hubUrl));
containerRegistry.RegisterSingleton<IChatServiceConfiguration, ChatServiceConfiguration>();
}
}
}
The MessagingMainView:
<UserControl x:Class="Ascensos.Wpf.Modules.Messaging.Views.MessagingMainView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:Controls="http://metro.mahapps.com/winfx/xaml/controls"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:iconPacks="http://metro.mahapps.com/winfx/xaml/iconpacks"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:materialDesignConverters="clr-namespace:MaterialDesignThemes.Wpf.Converters;assembly=MaterialDesignThemes.Wpf"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:views="clr-namespace:Ascensos.Wpf.Modules.Messaging.Views"
xmlns:helpers="clr-namespace:Ascensos.Wpf.Modules.Messaging.Helpers"
xmlns:prism="http://prismlibrary.com/"
prism:ViewModelLocator.AutoWireViewModel="True"
d:DesignHeight="300"
d:DesignWidth="400"
mc:Ignorable="d">
</UserControl>
The code behind:
public sealed partial class MessagingMainView : UserControl
{
public MessagingMainView()
{
this.InitializeComponent();
}
}
The viewmodel (calls a base message view model class):
public sealed class MessagingMainViewModel
{
public MessagingMainViewModel()
{
}
}
UPDATE: I have removed code from my app, so that only the above code is within the view and viewmodel. The view constructor and viewmodel constructor is still being hit twice during the module initialization.
I do not explicitly create instances of views/viewmodels in the code behind anywhere (i.e. only in XAML).
This is a contradiction - creating a view model in xaml means creating one in the same way as in code behind or anywhere else.
As said before, just don't do it. Remove all references to your view models' constructors from xaml, too (like <DataContext><MyViewViewModel/></DataContext>). To get intellisense in xaml, use d:DataContext.
I have found the issue. It is the following line in App CreateShell() method:
Container.GetContainer().RegisterInstance(typeof(Window), "MainWindow", Container.Resolve<MainWindow>(), new ContainerControlledLifetimeManager());
Commenting this out has sorted the problem. Looking at it again I have been rather silly to not spot this, like the novice I am. This code was added so I could get access to the MainWindow/MetroWindow in a service class - but I will need to try this another way. Thanks for your time #Haukinger.

Navigate to a Default View when Application Loaded using Prism 7 in WPF

I follows samples code provided in https://github.com/PrismLibrary/Prism-Samples-Wpf/blob/master/17-BasicRegionNavigation
I want to achieve the following result when I run the application (without explicitly clicking Navigate to View A). Does anyone know how to do it?
I have tried adding Navigate("ViewA"); after this line. However, I cannot get the desired outcome. Is it because the module hasn't been initialized?
Thanks.
did you add your module to the modulecatalog using override method ConfigureModuleCatalog? take a look at here
Eventually I solve by adding the following code in MainWindow.xaml.cs
public partial class MainWindow
{
IRegionManager _regionManager;
public MainWindow()
{
InitializeComponent();
_regionManager = ServiceLocator.Current.GetInstance<IRegionManager>();
RegionManager.SetRegionManager(ContentRegion, _regionManager);
Loaded += MainWindow_Loaded;
}
private void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
_regionManager.RequestNavigate("ContentRegion", "ViewA");
}
}
Get idea from: https://github.com/MahApps/MahApps.Metro/issues/1020#issuecomment-44779574
I'm kinda late to the party here, but I also stumbled over the question of how to navigate to a default view during the applications startup.
I found two ways:
1. App decides the default view
This can be solved in the CreateShell()-override in the App-Class.
This is my CreateShell-Method:
/// <inheritdoc />
protected override Window CreateShell()
{
var window = this.Container.Resolve<MainWindow>();
window.Loaded += (sender, args) =>
{
var manager = this.Container.Resolve<IRegionManager>();
manager.RequestNavigate("ContentRegion", "ViewA");
};
return window;
}
2. ViewModel decides the default view
Add a constructor to MainWindowViewModel that looks like this:
public MainWindowViewModel(IRegionManager regionManager)
{
regionManager.RegisterViewWithRegion("ContentRegion", "ViewA");
}

Adding Views to the ItemsSource of an ItemsControl via a Region of a Scoped RegionManager

I am trying to populate the ItemsSource of a ComboBox (a derivative of ItemsControl) via a region.
View
The scoped RegionManager (found on the View Model) is assigned to the view via prism:RegionManager.RegionManager="{Binding RegionManager}".
MainWindow.xaml
<Window x:Class="Applications.Testing.Wpf.RegionCreationTester.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Applications.Testing.Wpf.RegionCreationTester"
xmlns:prism="http://www.codeplex.com/prism"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525"
prism:RegionManager.RegionManager="{Binding RegionManager}"
prism:ViewModelLocator.AutoWireViewModel="True">
<Grid>
<ComboBox prism:RegionManager.RegionName="{x:Static local:RegionNames.itemsControlRegion}"/>
</Grid>
</Window>
MainWindow.xaml.cs
namespace Applications.Testing.Wpf.RegionCreationTester
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window, IModelled<MainWindowViewModel>
{
public MainWindowViewModel ViewModel
{
get
{
return (MainWindowViewModel)DataContext;
}
set
{
DataContext = value;
}
}
public MainWindow()
{
InitializeComponent();
ViewModel.PopulateItemsControl();
}
}
}
ViewModel
The view model is assigned to the view's DataContext via prism:ViewModelLocator.AutoWireViewModel="True".
MainWindowViewModel.cs
namespace Applications.Testing.Wpf.RegionCreationTester
{
public class MainWindowViewModel
{
/// <summary>
/// Gets the <see cref="RegionManager"/> scoped to this control.
/// </summary>
/// <remarks>Exists so that child controls can register regions for their own child controls which are also child controls in this control.</remarks>
public RegionManager RegionManager { get; } = new RegionManager();
/// <summary>
/// Adds some child views to the <see cref="RegionNames.itemsControlRegion"/>.
/// </summary>
/// <remarks>Normally these views would be resolved using an IoC container but this have been omitted for brevity.</remarks>
public void PopulateItemsControl()
{
var region = RegionManager.Regions[RegionNames.itemsControlRegion];
region.Add(new TextBlock { Text = "Item #1" });
region.Add(new Button { Content = "Item #2" });
}
}
}
RegionNames.cs
namespace Applications.Testing.Wpf.RegionCreationTester
{
public static class RegionNames
{
public const string itemsControlRegion = "collectionRegion";
}
}
Bootstrapper
App.xaml
<Application x:Class="Applications.Testing.Wpf.RegionCreationTester.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"/>
App.xaml.cs
namespace Applications.Testing.Wpf.RegionCreationTester
{
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
new RegionCreationTesterBootstrapper().Run();
}
}
}
RegionCreationTesterBootstrapper.cs
namespace Applications.Testing.Wpf.RegionCreationTester
{
public class RegionCreationTesterBootstrapper : UnityBootstrapper
{
protected override DependencyObject CreateShell()
=> new MainWindow();
protected override void InitializeShell()
{
base.InitializeShell();
(Application.Current.MainWindow = (Window)Shell).Show();
}
}
}
Once all the region population has occurred and the application is about to run I get a Prism.Regions.UpdateRegionsException containing an InnerException with the message "Region with the given name is already registered: collectionRegion" on the line in App of new RegionCreationTesterBootstrapper().Run(). The last line in my code I am able to get a breakpoint hit for is the new MainWindow() in CreateShell after the call to the constructor for MainWindow has exited. Why am I being told the region is already registered when I am only trying to register it once? I have set breakpoints in the MainWindow's constructor to indeed confirm that it only being created once and even if it weren't, the RegionManager to which it is scoped should prevent this exception from occurring. What have I missed?
UPDATE
I have just commented out the code within PopulateItemsControl and found that the exception is thrown even if only one view is added to the region and stranger still, if no views are added to the region but the region is accessed (as done in the line: var region = RegionManager.Regions[RegionNames.itemsControlRegion];). Therefore the issue is now to do with accessing an existing region on a scoped RegionManager for View Injection in order to add views to it; I'm not sure why accessing a region from the RegionManager would change its state, this seems like a bug in Prism or perhaps something to do with lazy enumeration.
Well, you are doing it twice, you're just not aware. When you set RegionName attached property on your ComboBox, an event hanler that will create the region with given name is attached (that's static part of RegionManager). When the instance of RegionManager you instantiated in your VM tries to access the region collection, indexer first calls a static method on RegionManager class that raises the event. The global RegionManager instance that got the task of creating the region (when you used RegionName attached property) has not finished it's job - window hasn't been loaded when you try to access the region with your instance, the handler has not been removed and it's called again. If you called your PopulateItemsControl method after the window has loaded (say in MainWindow's Loaded event handler), you wouldn't get the exception, but your code would not work as you expect. That is because your instance of RegionManager is not "handling" your collectionRegion, global RegionManager is.
Injecting RegionManager instance
If you need a RegionManager instance in your VM, use constructor injection.
public class MainWindowViewModel : BindableBase
{
private IRegionManager rm;
public MainWindowViewModel(IRegionManager manager)
{
this.rm = manager;
}
public void PopulateItemsControl()
{
var region = rm.Regions[RegionNames.itemsControlRegion];
region.Add(new TextBlock { Text = "Item #1" });
}
}
Dependency injection container (Unity or whatever you're using) will resolve IRegionManager instance when creating the VM (PRISM is doing that job for you anyway, you're not instantiating it yourself).
Region scopes
RegionManager keeps a collection of regions and does not allow regions with same names. So, unless your window is going to have multiple of those ComboBoxes that all have a region named collectionRegion, the RegionManager (the global one) is fine. If your collectionRegion is going to have instances of same view class, that all define another region within itself, then you need region scopes - RegionManager instances with their own scope for those views. In that case, the Add method can create local instance of RegionManager for that view:
IRegion collectionRegion = this.regionManager.Regions["collectionRegion"];
bool makeRegionManagerScope = true;
IRegionManager localRegionManager =
collectionRegion.Add(view, null, makeRegionManagerScope);

WPF Prism Module Initialization

I have a Prism Shell with two modules. One module is supposed to be the main application mock, MainAppMock, and the other module is supposed to be whatever that main system is using as a region, ModuleOne. Could be one, could be a million module.
The issue is understanding how Prism works. The MainAppModule initializes properly unless I call it's namespace in the Bootstrapper MainWindow.xaml file.
My question: Is this because it is loading the module at run time when I am calling that namespace and therefore Prism doesn't load it because it is already loaded? What is actually happening behind the scenes?
Shell:
class Bootstrapper : NinjectBootstrapper
{
protected override DependencyObject CreateShell()
{
return Kernel.Get<MainWindow>();
}
protected override void InitializeShell()
{
Application.Current.MainWindow = (Window)Shell;
Application.Current.MainWindow.Show();
}
protected override IModuleCatalog CreateModuleCatalog()
{
return new DirectoryModuleCatalog
{
ModulePath = AppDomain.CurrentDomain.BaseDirectory
};
}
}
MainAppMock and ModuleOne are the same except for the name.
ModuleOne Class:
[Module(ModuleName = "ModuleOne.Module")]
public class Module : IModule
{
private readonly IRegionManager _regionManager;
private readonly IKernel _kernel;
public Module(IRegionManager regionManager, IKernel kernel)
{
_regionManager = regionManager;
_kernel = kernel;
}
public void Initialize()
{
}
}
The problem is here. In the Bootstrapper MainWindow:
<Window x:Class="PrismTest.MainWindow"
xmlns:mainAppMock="clr-namespace:MainAppMock;assembly=MainAppMock"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Grid>
<mainAppMock:MainUserControl />
</Grid>
Note: It works fine if I remove the mainAppMock namespace as mentioned above.
Prism has Modules and Shell. Let's imagine that Shell is TV-Set, consequently, Modules will be channels in TV-Set. At start of program (you turn-on TV-Set) in the method protected override IModuleCatalog CreateModuleCatalog(), you are loading Modules(TV-Set loads your searched channels):
protected override IModuleCatalog CreateModuleCatalog()
{
ModuleCatalog catalog = new ModuleCatalog();
catalog.AddModule(typeof(ModuleZooModule));//Channel about Zoo
catalog.AddModule(typeof(ModuleSportModule));//Channel about Sport
catalog.AddModule(typeof(ModuleProgrammingModule));//Channel about Programming
return catalog;
}
And usually Shell should look like this:
<DockPanel >
<ContentControl Margin="5"
prism:RegionManager.RegionName="{x:Static inf:RegionNames.ContentRegion}"
prism:RegionManager.RegionManager="{Binding RegionManager}"/>
</DockPanel>
The row prism:RegionManager.RegionName="{x:Static inf:RegionNames.ContentRegion}" shows to Prism where Module(video stream of channel) should be injected in your Shell(TV-Set).
But in your case is not eligible to use such things:
<Grid>
<mainAppMock:MainUserControl />
</Grid>
As Prism does not know where Module(some channel) can be shown in your Shell(TV-Set).
After discussing this with a few people we've reached one conclusion.
We are loading the MainAppMock by calling it's namespace:MainUserControl in the Shell MainWindow. When Prism's bootstrapper runs it will go in the AppDomain.CurrentDomain.BaseDirectory and search through the DLLs for anything that inherits from IModule. It realizes that MainAppMock does that, but it is already loaded. It does not make sense to load a DLL that has already been loaded. Therefore, it won't reload it and the Module will never run.
It is worth mentioning that the MainAppMock initialization method is running when the DLL is loaded. We can actually do our Prism Module code there, but that is not recommended.

IoC.BuildUp doesn´t build object

I Am very new to Caliburn and have to expand an existing program, using caliburn.micro
I need my DataContext in a View to be set to a ViewModel which has some propertyinjected parameters. All this should work, so i only need to build it up.
however it does not! here is the code where i try to bulid:
public ProductionMainView()
{
InitializeComponent();
var gg = IoC.Get<ProductionMainViewModel>();
IoC.BuildUp(gg);
this.DataContext = gg;
}
As far as i can understand I need to get the object and then build it.
Now I should get a view "Containing" my ViewModel, but the View is blank,
I hope i have explained myself well enough.
Best regards
First off you are using a really old version, present version is 2.0.2, and you still need to have overrides for GetInstance, GetAllInstances, BuildUp methods in your BootStrapper or a variant for use with Ninject
http://putridparrot.com/blog/caliburn-micro-and-inversion-of-control-using-ninject/
Link above has some source for this.
Remember there is nearly no code in the Codebehind of the View... It will all go into the ViewModels (for the most part). ShellView => ShellViewModel with in ShellViewModel var gg = IoC.Get<ProductViewModel>() depending on you are intent is with that you can bind it to a <ContentControl x:Name="ProductViewModel" /> with a property in yoru viewmodel called exactly that. CM will go out and fine that ViewModel and associated view and mash them together automagically. Expected results on the screen should be your ProductViewModel in that contentcontrol location.
public class ShellViewModel : Screen
{
private ProductViewModel _productViewModel;
public ShellViewModel (){
}
protected override void OnActivate(){
ProductViewModel = IoC.Get<ProductViewModel>();
}
public ProductViewModel ProductViewModel {
get{ return _productViewModel;}
set{
_productViewModel = value;
NotifyOfPropertyChanged (); // 2.0.2 feature...
}
}
}
Now, if you decide to move to the latest version there are subtle changes one Bootstrapper<T> does not exist anymore it was refactored into BootstrapperBase. Next Initialize() will be placed into the ctor of your Bootstrapper(). From there everything will be based on your starting screen is after Initialize() is called in the CTOR you will call DisplayRootViewFor<ShellViewModel>() in the OnStartup override.
Some have said and I tend to agree over using IoC.Get<T>() can be anti-pattern, caution when using it. Sometimes things change unexpectedly. Those tend to DI the items via the CTOR and use from there accordingly.
/*version 1.52*/
public class Bootstrapper : BootStrapper<LoginViewModel>{
public Bootstrapper(){
Start();
}
protected override Configure(){
/* -- your Ninject configurations -- */
}
protected override object GetInstance(Type service, string Key){
if(service == null)
throw new ArgumentNullException("Service");
return _kernel.Get(service);
}
protected override IEnumerable<object> GetAllInstances(Type service){
return _kernel.GetAll(service);
}
protected override void BuildUp(object instance){
_kernel.Inject(instance);
}
protected override void OnExit(object sender, EventArgs e)
{
kernel.Dispose();
base.OnExit(sender, e);
}
}
To clarify do you have to login to get to the ProductionMainViewModel? What determines your logic for getting to ProductionMainViewModel to even display?

Categories

Resources