I have a Prism 4.0 enabled WPF application that uses RequestNavigate extensively, and it is working well. I have a scenario where I would like to render part of my UI to an image and store it for later use from a Windows service. I already know how to use RenderTargetBitmap to generate the image, but whenever my code tries to call RequestNavigate, nothing happens. I am calling the bootstrapper, so I would expect that all of the types are loaded, but it just is not working. Can anyone tell me if this should even be possible? Is there anything inherent to RequestNavigate that prevents it from working when there is no UI present?
Individual steps:
First, I call Run on my MefBootstrapper. This loads up all of the assemblies into the AggregateCatalog.
Then, I use MEF CompositionContainer.GetExportedValue to create a WPF UserControl that has a single ContentControl that is assigned a RegionManager.RegionName. This always creates the initial UserControl just fine.
Finally, I call MefRegionManager.RequestNavigate with the region name on my UserControl and the path to another UserControl that I want it to load. This fails to load the UserControl that I am attempting to navigate to.
If these are the only steps that I follow, then the final UserControl fails to load whether I am running from a Windows Service or from within my WPF application. However, if I call SetRegionManager to explicitely add the region from my host UserControl before calling RequestNavigate, then the last UserControl will load properly, as long as the code is run from within the WPF application. If this same code is run from my Windows service, then it still does not load.
RequestNavigate is exactly that, a request to navigate to a loaded (but inactive) region. It won't do any loading itself. You need to separately manage the loading of views (including views within views).
If your nested user control is only over loaded within the parent, and you don't need to manage it at runtime, then you can use ViewDiscovery. You register all the views in advance, and then when the region is created, it looks for (and loads) all the nested views. If you need to manage the views at runtime, switch them in and out, etc, then you can use ViewInjection.
For ViewDiscovery, in the Initialize method of the ModuleInit class in your module, insert the following line:
_regionManager.RegisterViewWithRegion("RegionNameOfYourNestedControl",
() => this.container.Resolve<NestedUserControl>());
Then when your region is loaded, the NestedUserControl will automatically be loaded into your ContentControl (region).
For more detail on ViewDiscovery, ViewInjection and UI composition, have a read of the prism documentation
Related
I have a desktop application that is based on the Caliburn Micro framework. Everything works great. Now I am trying to port the same app into Outlook as a plugin.
In the desktop app, based on an entry inside app.xaml, Caliburn knows where to find the bootstrapper and instantiates it.
In case of the Outlook plugin, I've created an overridden bootstrapper that I instantiate explicitly inside ThisAddIn.ThisAddIn_Startup(). This one of course does not use the Application object.
I can even invoke a particular view using code similar to this
var windowManager = IoC.Get<IWindowManager>();
windowManager.ShowDialog(new MyViewModel());
And that will cause the view associated with the view model to be shown in a modal window on top of Outlook (hence validating that Caliburn Micro is able to find a view from a view model inside my Outlook plugin)
What I haven't figured out how to do is instantiate the Shell so that I can start using its functionality.
My expectation was that since my bootstrapper derives from BootStrapper, and I have registered my shell view model implementation with the MEF container as exporting IShell, Caliburn will automatically instantiate the shell view model and start using it. That is not happening.
My goal is to get the shell loaded inside my plugin's task pane as the container for other views that I will be loading based on user actions.
Any ideas or tips on how I can get this to work? In general has anyone got a shell implementation loading inside an Outlook or Office plugin's task pane?
Thanks!
Do you mean instantiate via Bootstrapper<Shell>. This uses the Window Manager underneath but I don't think that extends to outlook. There is nothing stopping you using the same code above to initialize your shell manually, composition will handle the rest of the application.
IoC.Get, by default calls Activator.CreateInstance so it is possible your problem is with MEF. The method that drives opening the Shell DisplayRootViewFor() calls this line.
windowManager.ShowWindow(IoC.GetInstance(viewModelType, null), null, settings);
If MEF is not hooked up properly it will fail causing your shell not to load.
I have a WP7 Silverlight app that displays a bunch of data using Pivot. When first creating the project, the Visual Studio template calls ViewModel.LoadData in Page_Loaded:
private void MainPage_Loaded(object sender, RoutedEventArgs e)
{
App.ViewModel.LoadData();
}
However on MSDN it says you should use OnNavigatedTo instead of Page_Loaded, because OnNavigatedTo is called only once when a page becomes active, while Page_Loaded is called everytime an item is added to the page's visual tree. The MSDN article is for Silverlight 4 instead of for Silverlight Windows Phone though.
You override the OnNavigatedTo method
to examine the navigation request and
prepare the page for display. For
example, you can load the requested
data and enable or disable visual
elements.
Typically, you use the OnNavigatedTo
method instead of creating an event
handler for the Loaded event. The
OnNavigatedTo method is preferable
because it is only called once for
each time the page becomes active. The
Silverlight framework raises the
Loaded event each time the element is
added to the visual tree, which
potentially can happen more than once
when activating a page. The
OnNavigatedTo method is called for
each request, even when the page is
retrieved from the cache. You should
include in this method code that must
be executed for each request rather
than placing that code in the Page
constructor.
Is there any reason why the Visual Studio Pivot template calls ViewModel.LoadData in Page_Loaded?
I think you're confusing LayoutUpdated and Loaded.
Loaded should only get called once each time the page is displayed.
Loading data specific to a page after that page has been constructed is a simple way of working with data tied to a specific page. My guess is that the default templates use this as it's simple to understand and learn from.
Update
The article in MSDN that you refer to is for Page.Loaded. WP7 does not use Page instead it uses PhoneApplicationpage.
Having worked with WP7 for over a year and created dozens of apps, I've never seen the Loaded event called multiple times in a WP7 app.
I have a PRISM RegionManager with a couple of region - a Ribbon region on the top, and a main content region for my view underneath it - fairly basic.
The app starts with a "home" view in the main content area. When I click a button on the Ribbon, I inject a second view into the content area and navigate to it in the region manager. When I click a button on this view, it should be removed and the original view should be shown.
I'm currently doing this using the RegionManager.Add() method to manually add the second view. When I want to remove it, I publish an event which is consumed by a manager class that gets the current active remove, calls Remove() on the Region Manager for it, and then navigates back to the original view.
This all works great, except that when the second view is left alive after I call Remove(), and keeps a handle onto any subscriptions that it made during its lifetime! I've tried calling Subscribe explicitly with false for weak event references, but this is the default anyway, and it didn't help. I've tried both types of creating the view (discovery and injection) and removing the view via Remove and Deactivate. None of them helped. This is a real problem as when I want to create the same view in the future (a new instance of that view), I'm left with several instances of them, all subscribing to the same events, even though some of those instances were removed ages ago from the Region Manager.
Do I need to manually unsubscribe from every event that I subscribe to in my views (highly undesirable)? Or is there some way that I can dispose of the control / mark it as deactivated so that its subscriptions get removed?
I get the feeling that I'm doing something silly here but it's got me completely stumped.
Thanks
This is a known issue. There is a bug in the eventAggregator code. Take a look at the following blog and the suggested hack to overcome it
http://greenicicleblog.com/2010/04/28/prism-event-aggregator-more-leaky-than-it-seems/
Hope this helps
I am playing around with the WP7 SDK and the Prism for WP7 beta and have come across a problem I can't figure out (or even a workaround for).
First of all, I'm new into WPF/Silverlight/WP7 and Prism, so I could be overlooking something very obvious.
So I have a Shell page that has my region that is used to hold my content pages, and all of this is working great! Now my problem is that I have a settings control that will allow the users to edit the settings of the application (names, locations, etc). Now I can get this page to work with no problems by having a button on one of my controls that will transition the region manager to the control.
However, I would like to use the application bar on the phone to have the button, but I cannot for the life of me figure out how to get access to my model object from within the page that is opened by the Application bar click. I can only do a NavigationService.Navigate() to a settings page, but the PhoneApplicationPage objects in WP7 do not allow injection on the constructors (the constructors must be parameterless) so I cannot pass in the object instance in that way.
So my question is, how can I access (or pass) objects between pages or controls?
Thanks!
In the examples they use this technique to set the data context of a form after it is navigated to from another form:
NavigationService.Navigate(new Uri("/Page2.xaml", UriKind.Relative));
FrameworkElement root = Application.Current.RootVisual as FrameworkElement;
root.DataContext = some_object;
I have a WPF "wizard" where I am using PageFunction objects to display the various pages in the wizard. Several of these pages use references to objects in other assemblies within the application so the user can select "packages" they want to include in a generated ouput. In the page function, I have the keep alive flag set to true so that when the user presses cancel or finish in the wizard I can capture the wizard results and the corresponding data the user selected.
The problem is that the wizard pages never go away even after the scope in which they were created has been exited, Thus, when I try to remove one of the referenced objects that the wizard was databound to from a collection, it causes the wizard page to throw an exception. I cannot seem to find a way to correct this issue without setting the KeepAlive flag to false, but this breaks the ability to capture the user data from the wizard.
Any ideas?
Thanks!
I have something very similar (if not the same). A wizard created with a navigation window and a set of pagefunction pages to collect data. Some of the pagefunction pages are even pulled from assemblies in the application folder but I have never had this problem. Can you be more specific on the problem? How do you try to remove the referenced objects? Can you post some code so I can look at it?