Prism INavigationAware methods not called with WPF app - wpf

We are using Prism 7.2.0.1441-ci in our WPF app. We have a problem that the INavigationAware
methods are not being called when navigating to a view. We use the below code to navigate.
The Login view is registered. The LoginViewModel has implemented INavigationAware
_regionManager.RegisterViewWithRegion(RegionNames.TabRegion, typeof(Login));
_regionManager.RequestNavigate(RegionNames.TabRegion, ViewNames.Login, parameters);
MainWindow.xaml
<DockPanel>
<!-- <Frame x:Name="_mainFrame" NavigationUIVisibility="Hidden" /> -->
<ContentControl prism:RegionManager.RegionName="{x:Static core:RegionNames.TabRegion}" />
</DockPanel>
This mechanism does work in another part of the app, this particular bit is in the startup code and is being called from the MainWindowViewModel constructor.
Any ideas?
Thanks

This mechanism does work in another part of the app, this particular bit is in the startup code and is being called from the MainWindowViewModel constructor.
You cannot navigate from the shell view model's constructor, because the regions aren't there yet.
Instead, do the first navigation from OnInitialized (or let the user click a button).

Related

How do I bind a button in a Shell, to a Command defined in a loaded module's viewmodel?

The scenario is this:
I have an application developed in WPF, MVVM, Prism 6.3.0. This app considers
Shell.xaml (the shell)
UserControl1.xaml (inside a module, independient Class Library)
When I click on the Button defined in the Shell, I expect the command being executed and the condition defined in CanExecute be verified. But, this command is defined into the ViewModel class that's the view model of the module (in this application will be more than one module, loaded as Prism implements this traditionally).
To be called, I tried to define a localviewmodel into the resources collection of the Shell. This triggers the command, but doesn't triggers the condition CanExecute.
If I put the XAML code defined in the user control inside the module, (in other words, If I code a simple app with no use of Content region manager) a single-page XAML app, the command verifies the CanExecute without a problem. My best guess is a problem of binding the command defined in the module viewmodel, into a button in the Shell. Having read a lot about it, I just don't get the right way of declaring the binding.
Here's an example:
<Telerik:RadRibbonView x:Name="BarraHerramientas" Grid.Row="0"
DockPanel.Dock="Top"
ContentHeight="130"
Height="160"
Template="{DynamicResource RadRibbonViewStyle}"
MinimizeButtonVisibility="Visible"
HelpButtonVisibility="Visible" Background="Red">
<Telerik:RadRibbonTab Header="1"
Style="{DynamicResource RadRibbonTabStyle}"
IsSelected="True"
TabIndex="0">
<Telerik:RadRibbonGroup Header="RibbonGroup1"
DialogLauncherVisibility="Visible">
<Telerik:RadRibbonButton CollapseToMedium="Never"
CollapseToSmall="WhenGroupIsMedium"
IsAutoSize="True"
LargeImage="Agregar.png"
Size="Large"
SmallImage="Agregar_16x16.png"
Text="Add..."
Command="{Binding AddRecordCommand, Source={StaticResource LocalViewModel}}"
CommandTarget="{Binding ElementName=MyRecords}"
CommandParameter="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}" "
Telerik:ScreenTip.Description="..." />
</Telerik:RadRibbonGroup>
</Telerik:RadRibbonTab>
</Telerik:RadRibbonView>
Where MyRecords is the name of the GridView that holds the data, and LocalViewModel is the key of the instance of the ViewModel defined in the loaded module. So, there's two instances in a point of time of LocalViewModel, the one defined as a resource in Shell.xaml, and the one defined into the loaded Module that holds the "MyRecords" GridView.
Using a one-page example, this works flawless, of course. And because I need to write a few modules, each one defines it's own ViewModel, or more than one. So, thats because I need to use commands defined into the different module's viewmodels, from the Shell. If I declare each ViewModel in the shell (a LOT of data coming in) probably will be a performance issue.
So, how can I call a command (from the shell) that is defined in a ViewModel as a resource in a module being loaded "on demand"?
Thanks everyone.
There are a number of ways to accomplish what you want. One approach would be to use a CompositeCommand. If your ShellViewModel will always contain the data you use as a parameter this will work for you. Each ViewModel can subscribe the the parent CompositeCommand and whenever the CompositeCommand is invoked all registered VM Commands will be invoked too. You can see a sample of CompositeCommands here:
https://github.com/PrismLibrary/Prism-Samples-Wpf/tree/master/12-UsingCompositeCommands
Another approach would be to have your ribbon buttons injected with each view you inject into the shell. So each view will know which tabs need to be added/removed to the ribbon as you add/remove the views in the shell region. You can see how to do that in my Pluralsight course Prism Problems & Solutions: Loading Dependent Views:
https://www.pluralsight.com/courses/prism-problems-solutions

Handle UIElements with MVVM

Im building an app trying to follow MVVM as far as possible. Its an windows 8 store app and Im using MVVM-light. Now let say I have a MainPage using MainViewModel and I also have a UserControl using UserViewModel.
Now I want to be able to communicate between MainPage and UserControl without using any code-behind. In this simple scenario i would like to "click" a button in my UserControl from a button in MainPage
MainPage.Xaml:
Here I have a button with which I wish to click a button in the Usercontroll
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Button Content="MyButton"
Command="MyCommand"></Button>
</Grid>
MainPageViewModel:
public class MainViewModel : ViewModelBase
{
//
}
UserControll.Xaml
Here is the button I wish to click from MainPage
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Button Content="UcButton"/>
</Grid>
UserControlViewModel:
public class UserControlViewModel : ViewModelBase
{
//
}
Im interested in knowing about how you can interact with UI-Elements following the MVVM-pattern. Any links or tips appreciated.
The 'clean' (MVVM way) of doing this is by having the ViewModels interact.
So if View1 should invoke something in View2 have View1 trigger a Command on its ViewModel1. ViewModel1 should then contact ViewModel2. ViewModel2 would executed the required action and show the result through its properties. View2 will bind to the properties and show the effect of the action.
Interaction between ViewModels can be done through a publisher-subscriber model (message broker, bus, ...) In MVVM-light there is a Messenger class to keep the dependencies clean.
I can't comment since I don't have enough points, and the question seems a bit weird. Do you want to click the button so that it shows in the GUI, like the button turns blue or do you want to invoke a command in usercontrolViewModel. If it is the latter you could maybe do a fix like this:
MainPage.xaml
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Button Content="MyButton"
Command="{Binding UserControlViewModel.SomeUserControlCommand, Source={StaticResource Locator}}"></Button>
</Grid>
Remember to check what name you gave UserControlViewModel in the ViewModelLocator and you will of course have to create the command: SomeUserControlCommand in UserControlViewModel.

Lifetime for the view / viewmodel when using data templates

I am defining a strategy where a main view will use data templates to switch between the views. Currently it can switch between 3 Views:
ApplicationView: it is actually the view that consists of lots of
different views, mostly layered out using tabs / docking. this is a
view that deals with application data.
LogInView: it is used for logging the user in.
DialogView: it is used to display dialog views. This view will also use data templates to select a proper view that is required.
The idea is that when a dialog view needs to be displayed, it is set as current view on the main view. After the selection is done, this information is passed to ApplicationView, or a view that is part of ApplicationView. While DialogView is shown, ApplicationView, must not be released from memory, since it ApplicationViewModel will still be manipulating with data (it needs to constantly work in the background).
I am thinking of achieving this using DataTemplates, and binding ContentControl's Content to CurrentView:
// in MainView
DataTemplate DataType="{x:Type vm:ApplicationViewModel}">
<vw:ApplicationView />
</DataTemplate>
.....
// in MainViewModel
public ViewModelBase CurrentView { get; set; }
Basically I am trying to avoid using modal windows for dialogs.
1) Is this strategy OK, or there are some problems that I am not aware with it?
2) When I switch to DialogView (I am actually switching viewmodels), what happens with the ApplicationView/ApplicationViewModel? Do I need to store ApplicationViewModel's reference somewhere, so it doesn't get garbage collected? I haven't tested this, but probably when I set CurrentView a new instance of ViewModel/View will be created.
3) Connected to second question, when using DataTemplates, what happens to View/ ViewModel that was previously used, and is now replaced with different view/viewmodel?
I don't see anything wrong with the way you're switching views, although typically you don't want to get rid of the application when you're displaying a dialog.
What I've done in the past is to put both the CurrentView, and the DialogView in a Grid so they are positioned on top of each other, then have the ApplicationViewModel contain an IDialogViewModel and IsDialogVisible properties, and when you want to display the dialog simply populate those two fields. (see below for an example)
You will have to store the ApplicationViewModel somewhere if you want to go back to it and avoid creating a new ApplicationViewModel
WPF disposes of UI objects that are no longer visible, so switching the CurrentView from Login to Application will get rid of the LoginView and create an ApplicationView
The ContentControl's Content is getting set to your ViewModel, so the ViewModel is actually being put in the applications VisualTree. Whenever WPF encounters an object in its VisualTree that it doesn't know how to draw, it will draw it using a TextBlock containing the .ToString() of the the object. By defining a DataTemplate, you are telling WPF how to draw the object instead of using its default .ToString() method. Once the object leaves the VisualTree, any visual objects that were created to render the object will get destroyed.
Although I would keep using what you currently have for switching Views, I would not use that method for the Login, Application, and Dialog views.
Typically the LoginView should only be displayed once when logging in, although it might get displayed again in a Dialog if you allow users to switch logins once logging in. Because of this, I typically show the LoginView in the startup code, then display the ApplicationView once login is successful.
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
var login = new LoginDialog();
var loginVm = new LoginViewModel();
login.DataContext = loginVm;
login.ShowDialog();
if (!login.DialogResult.GetValueOrDefault())
{
Environment.Exit(0);
}
// Providing we have a successful login, startup application
var app = new ApplicationView();
var context = new ApplicationViewModel(loginVm.CurrentUser);
app.DataContext = context;
app.Show();
}
Like I said earlier, I wouldn't want to hide the Application when I display a Dialog, so I would make the Dialog part of the Application
Here's an example of the DataTemplate I would use for my ApplicationViewModel, using my own custom Popup from my blog for the Dialog
<Grid x:Name="ApplicationView">
<ContentControl Content="{Binding CurrentView}" />
<local:PopupPanel x:Name="DialogPopup"
Content="{Binding DialogContent}"
local:PopupPanel.IsPopupVisible="{Binding IsDialogVisible}"
local:PopupPanel.PopupParent="{Binding ElementName=ApplicationView}" />
</Grid>
Personally, I would find it easier to use ZOrdering within a standard grid, and put everything in the same view - using the ViewModel to manage visibility.
E.g
<Grid>
<Grid Visibility="{Binding IsView1Visible,
Converter={StaticResource BoolToVisibilityConverter}}">
<!-- view 1 contents -->
</Grid>
<Grid Visibility="{Binding IsView2Visible,
Converter={StaticResource BoolToVisibilityConverter}}">
<!-- view 2 contents -->
</Grid>
<Grid Visibility="{Binding IsView3Visible,
Converter={StaticResource BoolToVisibilityConverter}}">
<!-- view 3 contents -->
</Grid>
<Grid Visibility="{Binding IsDialogVisible,
Converter={StaticResource BoolToVisibilityConverter}}">
<!-- dialog contents contents -->
</Grid>
</Grid>

How do I navigate from a ViewModel?

I am writing a small contrived WPF application for a university project, and i'm taking the opportunity to learn the MVVM pattern. I've implemented my initial start up window which will be a login page.
I have bound the login button to a command that I have derived from ICommand, which is injected with the LoginViewModel. The LoginViewModel then validates the customer through a WCF service I have created.
My question is, once the viewmodel receives notification that the validation is correct, how should I navigate to the next page/window from the viewmodel? I don't want to create an instance of a new window within the viewmodel. Should I be using pages here instead? I'm keen to understand the best practices from the start, I don't want to be wasting my time learning the bad ways of doing this.
Thanks.
Instead of changing views, you can change viewmodels and use a ContentControl to bind the viewmodels to specific views: create a main view on top of the other views which will manage the view changes via commands (in this example set the CurrentViewModel from your command handler):
<UserControl.Resources>
<DataTemplate DataType="{x:Type vm:LoginViewModel}">
<local:LoginView/>
</DataTemplate>
<DataTemplate DataType="{x:Type vm:FirstPageViewModel}">
<local:FirstPageView/>
</DataTemplate>
</UserControl.Resources>
<ContentControl Content="{Binding Path=CurrentViewModel}" />
This way you don't need to mix up the Views and ViewModels, you're not creating views from the VMs. Actually in my case it was the child page which requested the view change with an event.
I've not done pages, but for your login screen I would have my LoginViewModel expose a LoggedInEvent.
You can then have a parent ViewModel create the LoginViewModel and destroy it again when the LoggedInEvent is raised (and create whatever new views you need).

WPF: Composite Application with Page Navigation

I am currently writing an application to which the composite methodology fits like a glove.... almost!
I also need a way to navigate between views, including maintaining a journal for navigation backward and forward.
What is the best way to combine these two methodologies, on one hand the single Window based CAG shell with its UserControl derived views, and on the other hand the convenient NavigationWindow shell with its Page derived views and journal?
Thanks!
You can display anything in a NavigationWindow, not just Pages. A simple way to make it work is to define in the NavigationWindow's resources a DataTemplate for each ViewModel you want to display. Bind the Content property of the NavigationWindow to a property of your main ViewModel, and you're done : changing that property will update the NavigationWindow content, and the appropriate DataTemplate will be picked automatically
UPDATE
I just looked at the code of a project of mine where I used a NavigationWindow. Actually I was mistaken, it doesn't work by binding the Content (or maybe it works, but that's not what I did). Instead I created a INavigationService interface, implemented by my App class, which handles the navigation by calling the NavigationWindow.Navigate method. That way, the navigation history is maintained by the NavigationWindow.
Here's an extract from my project
MainWindow.xaml :
<NavigationWindow x:Class="MyApp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="clr-namespace:MyApp.ViewModel"
xmlns:view="clr-namespace:MyApp.View"
Title="{Binding Content.DisplayName, RelativeSource={RelativeSource Self}, FallbackValue=The Title}"
Height="600" Width="800">
<NavigationWindow.Resources>
<DataTemplate DataType="{x:Type vm:HomeViewModel}">
<view:HomeView />
</DataTemplate>
<DataTemplate DataType="{x:Type vm:CustomerViewModel}">
<view:CustomerView />
</DataTemplate>
</NavigationWindow.Resources>
</NavigationWindow>
App.xaml.cs :
...
private void Application_Startup(object sender, StartupEventArgs e)
{
LoadConfig();
MyApp.MainWindow window = new MainWindow();
INavigationService navigationService = this;
HomeViewModel viewModel = new HomeViewModel(navigationService);
this.MainWindow = window;
window.Navigate(viewModel);
window.Show();
}
When I need to navigate to another view, I just call the Navigate method with the ViewModel as a parameter, and WPF automatically picks the appropriate DataTemplate from the resources.

Resources