How to perform View navigation using MVVM? Win RT - c#

I've started a Windows Universal project following the MVVM pattern, and came to the situation where I need to navigate to another page with a button click.
Normally I would do this in code behind using the button's click event like the below:
private void AppBarButton_Click(object sender, RoutedEventArgs e)
{
// Navigation Without parameters
this.Frame.Navigate(typeof(SecondPage));
}
But since I need to follow the MVVM pattern with this app, I'm wondering how should I set up the navigation to a new View on button click?
I've come across ICommand for this task in WPF solution after a Google search, but not 100% on how it should be implemented for this Windows Universal Framework.

Basically you got two options
1. use a navigation service
You can define an INavigationService interface and pass it to all your ViewModels in your viewmodel assembly (assuming you are using different assemblies which is important to keep ensure you are not referencing to the view from your viewmodel and hence violate MVVM pattern).
public interface INavigationService
{
void Navigate(string page, object parameter);
}
In your viewmodels you can simply call it with navigationService.Navigate("UserEditPage", selectedUser.Id);.
Implementation could be as simple as
public class WinRtNavigationService : INavigationService
{
public void Navigate(string page, object parameter)
{
Type pageType = Type.GetType(string.Format("YourCompany.YourApp.ViewModels.{0}", page));
((Frame)Window.Current.Content).Navigate(pageType, parameter);
}
}
You use this, if you have the need to navigate from ViewModels.
2. use behaviors
You can use behaviours to add reusable navigation support to XAML directly, hence completely avoiding the code behind.
For this, Blend offers Interactivity Triggers and a NavigateToPageAction behavior.
<Page
xmlns:i="using:Microsoft.Xaml.Interactivity"
xmlns:c="using:Microsoft.Xaml.Interactions.Core" >
....
<Button Content="Edit">
<i:Interaction.Behaviors>
<i:BehaviorCollection>
<c:EventTriggerBehavior EventName="Tapped">
<c:NavigateToPageAction TargetPage="YourCompany.YourApp.ViewModel.UserEditPage" Parameter="{Binding Path=SelectedUser.Id}" />
</c:EventTriggerBehavior>
</i:BehaviorCollection>
</i:Interaction.Behaviors>
</Button>
...
</Page>
Blend Behaviors/Interaction Triggers are generally used to bind navigation functions to Buttons or other UI elements (i.e. click on a picture which doesn't have to be a button), as it doesn't require any code within the Code Behind or ViewModel.
If a navigation is to occur after some validation, i.e. you have a multi-page form for user registration and you have a "Send" Button binded to a RegisterCommand and the RegisterCommand does an online validation and you're required to go back to previous page, you'd want to use the INavigationService.

Related

UWP - Pass event to control (tunneling)

I have a UWP question about inheriting/ passing a event to a user control from the parent view to child.
I created a user control to display text overlays (see code below). We had a parent view that would display an overlay when the window is resized (see code below). The overlay would display the dimensions of the window when this even is triggered.
I moved the overlay to a user control and now I'm trying to pass that resized event to the overlay control. The hope is that we can register more events to the overlay control so it can display more then the resize
information. However, I'm not sure the best way to do this. My first idea was inheriting from the view, so i could just listen to the event from the overlay control, but that resulted in errors.
I believe due to the fact that the parent view has a ViewModel (i also created one for the overlay, not sure if its actually needed yet).
I have been reading about a lot of possible ways to do this, but I'm not sure which would be the best way to do this. Does anyone have any insight on this issue ? I would be open to suggestions, links, or just a general answer of
what is the best way to achieve this in our project.
Parent view
User Control
Parent Event
Control class
Some information i have been reading about:
https://documentation.devexpress.com/WPF/17449/MVVM-Framework/ViewModels/ViewModel-relationships-ISupportParentViewModel
https://learn.microsoft.com/en-us/dotnet/framework/wpf/advanced/preview-events
https://social.msdn.microsoft.com/Forums/windows/en-US/742077f6-e875-44d1-8bc4-6e6516db9eda/passing-the-parent-control-event-to-child-controls?forum=winforms
https://learn.microsoft.com/en-us/windows/uwp/xaml-platform/events-and-routed-events-overview
http://irisclasson.com/2013/12/10/passing-event-arguments-from-xaml-in-windows-store-apps-inputconverter-inputconverterparameter-etc/
https://learn.microsoft.com/en-us/windows/uwp/launch-resume/how-to-create-and-consume-an-app-service
Update
Adding the viewModel to the parent viewModel (terminal), and passing it to the control via the Datacontext did not work
As you're already using MVVM, I'd recommend going the full route utilizing "Interactivity", "Commands", and "child ViewModels". This is a commonly used patter in MVVM WPF applications, and can be applied to UWP apps as well.
Using "Interactivity" and interactions
The interactivity / behaviors library from Microsoft allows you to bind events in XAML to an ICommand in the ViewModel. You can get the managed NuGet package here.
From the official examples on GitHub, shortened:
<Button x:Name="button1" Content="Increment">
<Interactivity:Interaction.Behaviors>
<Interactions:EventTriggerBehavior EventName="Click" SourceObject="{Binding ElementName=button1}">
<Interactions:InvokeCommandAction Command="{Binding UpdateCountCommand}"/>
</Interactions:EventTriggerBehavior>
</Interactivity:Interaction.Behaviors>
</Button>
Forward command data to child ViewModel
Having this event now routed to your command in your parent ViewModel, you can now either call your overlay ViewModel and pass the info directly to it:
private readony IOverlayViewModel _overlayViewModel;
public ICommand UpdateCountCommand { get; set; }
ctor(IOverlayViewModel overlayViewModel)
{
_overlayViewModel = overlayViewModel;
UpdatedCountCommand = new MyICommandImplementation(UpdatedCountCommand_Executed);
}
private void UpdatedCountCommand_Executed(/* Add correct method signature */)
{
// If needed, retrieve data from parameter...
// Update overlay ViewModel text
_overlayViewModel.Text = ""; // Whichever text was calculated before
}
Or you use a messenger (mediator pattern) to send it to an overlay.
I was misusing the bindings. x:Bind and Binding are using different types of context. For this binding to work we would need to set the parent's element Datacontext to 'this'. x:Bind on the other hand does this implicitly.
<views:OverlayView DataContext="{x:Bind ViewModel.Overlay}"></views:OverlayView>

WPF MVVM calling ViewModel methods from code behind

First off I am very new to WPF MVVM and a bit confused. People say that generally in MVVM the best practice is not to have any code behind. I have found that some methods are way easier to achieve in code behind than in viewmodel (e.g MouseMove) and that led me thinking between the differences of these 2 following examples:
1) Using RelayCommand:
View
<Button Command="{Binding AlertCommand}"></Button>
ViewModel
public RelayCommand AlertCommand { get; set; }
public void Alert()
{
MessageBox.Show("message");
}
2) calling ViewModel methods from code behind:
View
<Button PreviewMouseLeftButtonUp="OnMouseLeftButtonUp"></Button>
View code behind
private void OnMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
var ctx = (MainViewModel) this.DataContext;
ctx.Alert();
}
Is using code behind here wrong? What are my benefits of not using code behind?
The MVVM pattern is a best practice when building UWP, WPF, and Xamarin.Forms apps. The main advantage is that you are able to decouple the logic from the presentation and potentially could present a single view model by multiple different views and could switch views without having to modify the view model significantly. This is a great advantage when building cross-platform apps with native UI, which MvvmCross framework uses to a great extent for example.
Having an empty code-behind is however definitely just an ideal, which is usually not easy and not always necessary to achieve. Sometimes you need to use code-behind for purely view-related manipulation like changing layout for different window sizes, controlling animations, etc. For such actions, code-behind is a much better fit than trying to force this somehow into the VM.
Comparing your two approaches, using the RelayCommand-based one over the direct method call is preferable, because it has less direct tie to the method itself. If you wanted, you could switch the RelayCommand instance in the VM for a different implementation (calling different method) at runtime, and thanks to binding the button could now perform a different action. This can be used in editor-like apps where some tools may have different functionality based on current context the app is in.
Also, for controls which do not offer a Command you can use EventTrigger hand in hand with InvokeCommandAction (both defined withing Expression Blend SDK) which will allow you to "convert" an event to a command call, even with your defined transformation of EventArgs.
Both are valid methods. The first is preferable if possible. I generally use the second method on events where no command binding is available. The notion of "absolutely no code-behind in MVVM" is debatable. Any code that belongs directly to the view (and is not business logic) and is not reusable in a VM can be put in the code-behind, such as wiring up events in the second example.

Opening a second Window from MainWindow following MVVM and loose coupling

At first: This App and Question is for learning purpose
I'm on a new application and facing the problem that I want to open a Window when the user clicks on a Button in the MainView. In the past I'd have designed a Command which just creates the new Window and displays it
new RelayCommand((x)=>new SecondWindow().Show());
Now with this new Project I'm trying to fully decouple all classes from each other. To achieve this my App consists of 4 Assemblies (BL, COM, DAL and UI).
As in each WPF Application, the App starts with the MainWindow.xaml. The MainWindow.cs will create it's instance of MainWindowViewModel:
public ViewModel VM {get; private set;}
public class MainWindow(){
VM = new ViewModel();
InitializeComponent();
}
(which already violates loose coupling) (Any tips on how to make it better?)
My last attempt is to create an instance of my second Window inside my main window
<Window.Resources>
<local:SecondWindow x:Key="sw"/>
</Window.Resources>
and pass it as a CommandParameter to my Command
CommandParameter="{StaticResource sw}"/>
new RelayCommand((x)=> ((Window)x).Show());
This solution works but has one big disadvantage - the second window get's created immediately after the app starts - and so does it's ViewModel which starts some heavy processes (DB Connections etc.)
I've heard something abour IoC principle but I really don't know how to use it with an wpf application.
You are thinking along the right lines.... you basically have to create a List of ViewModels as your application starts up, then you can switch between them as the user presses buttons and pass the name of the ViewModel as a CommandParameter to your Command handler....
You might find this link to Rachel Lim's Blog
https://rachel53461.wordpress.com/2011/12/18/navigation-with-mvvm-2/
Also, I'm not going to post any code here coz it simply gets too complicated. So here is a download to just about the simplest example I could come up with
http://www.mediafire.com/download/3bubiq7s6xw7i73/Navigation1.rar
Download and un-RAR it (with win RAR) You will need to step though the code, figure out what its doing and how its doing it then modify it to suit your needs... Or modify your needs to suit the code.....
The example is a modification of Rachel Lim example. It simply contains Views and ViewModels, there are no Models or data. It demonstrates switching between two different Views.
UPDATE 1
With specific reference to the demo code.... Your VMs are added to a static collection of VMs (see AddViewModel function), each View ( the DataTemplate associates View with ViewModel) is selected when you click a button for example, by calling 'SelectViewCommand' which in turn sets Current_ViewModel to the selected ViewModel... the corrisponding ContentControl is then updated to display that currently selected View...
I know is confusing and very difficult to explain
When you press a button to 'change Views' you are actually changing the value of the property that your ContentControl is bound to, so you have to call the correct SelectViewCommand in the SAME instance of the class that your ContentControl is bound too...
In the demo you'll see that in the 'LogOn_View' I call
Command="{Binding DataContext.SelectViewCommand, ElementName=Base_V}"CommandParameter="Main_ViewModel"
Here I am calling the SelectViewCommand in the Base_ViewModel (x:Name="Base_V" in Base_View XAML), That's because I want to change the View that is displayed in the Base_View's 'ContentControl'
In Main_View I call
Command="{Binding SelectViewCommand}" CommandParameter="MainV1_ViewModel"
Here I am calling the SelectViewCommand in the Main_ViewModel, That's because I want to change the View displayed in the MainView's 'ContentControl'....
I typically create a WindowService class for managing window changes/dialogs in MVVM. Having "View" code in the ViewModel (i.e. Window.Show()) goes against MVVM principles. For example:
public class WindowService : IWindowService
{
public void ShowDialog<T>(ViewModelBase viewModel) where T : IApplicationDialog
{
IApplicationDialog dialog = (IApplicationDialog)Activator.CreateInstance(typeof(T));
dialog.Show();
}
}
And then your call from the ViewModel would look something like:
windowService.ShowDialog<SecondWindow>(new SecondWindowViewModel());
If you're using DI, you can pass a reference to the IoC container to the window service and create the window instances from that rather than using Activator.CreateInstance (i prefer the DI approach personally)

Conditionally auto-click button in MVVM using xaml

I am writing an application based on MVVM architecture. The application has a Wizard like workflow. In couple of pages (views) in my application, I need a button to be auto-clicked when a certain condition is satisfied. The views are tied together using the root Wizard view model which has a ClickNextBtn command that is tied to the Next button in the root Wizard view. So, I need something like in the root Wizard view:
<DataTrigger Binding="{Binding Path=CanAutoClickNext}" Value="True">
<Setter Property="ClickBtn" Value="true" />
</DataTrigger>
The property in the above sample is meaningless, but hopefully it helps convey what I am trying to do.
The CanAutoClickNext bool property is available off of the Wizard view model.
On one of the views where I need the Next button auto-clicked, I tried passing the WizardViewModel as an argument to its corresponding view model's constructor when it is first instantiated in the root wizard view model, and then calling the ClickNextBtn off of it in a method therein later when the view is actually loaded. But that did not work, not surprisingly.
I know how to programmatically click a wpf button, but getting it all tied together in the framework I have is proving to a big challenge. Any feedback is appreciated.
UPDATE:
I ended up rewriting the UI design pattern (still MVVM) so that now instead of having to having to move to a next page automatically, the state within a page changes and a different set of controls become active. Users are then prompted to click next.
Like the comment's on your question stated, this should be a concern of the ViewModel to invoke the Click Handler.
How you could go about implementing this is very similar to something like this Question's answer
Now in MVVM, you should have your Button's connected to an ICommand in the ViewModel(If your using MVVM Light toolkit, it will be RelayCommand/RelayCommand<T>).
Now assuming this ICommand variable in your VM is called NextButtonCommand,
what you can do is
public bool CanAutoClickNext {
get {
return _canAutoClickNext;
}
private set {
if (value == _canAutoClickNext)
return;
_canAutoClickNext = value;
RaisePropertyChanged(() => CanAutoClickNext);
if (_canAutoClickNext)
NextButtonCommand.Execute(null);
}
}
with this, when your property in the VM CanAutoClickNext gets set to "True", the Execute function of the ICommand is automatically invoked by the VM. This seperates all the logic handling to the VM and keeps the View dumb as what is recommended by MVVM when it comes to application / business logic.
Side Note
The property CanAutoClickNext seems a waste if it's not being bound to anything from the View. If this is the case, I'd recommend just getting rid of that property and invoke the ICommand.Execute(null) from the place where the logic holds fit than use a property with INPC just for this case.
I'll follow up from a different angle. Let's say you have any message bus ready (IEventAggregator, IMessenger, doesn't matter). I'll use the Caliburn.Micro's IEventAggregator along with the nomenclature 'cause that's what I'm most familiar with. Now you might have a very simple event:
public class MoveNext
{
}
Then your 'host' viewmodel of the wizard:
public class WizardHost : IHandle<MoveNext>
{
private readonly IEventAggregator messageBus
public WizardHost(IEventAggregator messageBus)
{
this.messageBus = messageBus;
this.messageBus.Subscribe(this);
}
/here you might have the 'real' command method, e.g:
public void GoToNextQuestion()
{
// do stuff
}
public void Handle(MoveNext message)
{
GoToNextQuestion();
}
}
public class WizardPage
{
private readonly IEventAggregator messageBus;
private bool shouldMoveToNext;
public WizardPage(IEventAggregator messageBus)
{
this.messageBus = messageBus;
}
public void DoStuff()
{
//at some point, you might want to switch the flag or do whatever you need/want to do and:
if(shouldMoveToNext)
messageBus.Publish(new MoveNext());
}
}
Now when you DoStuff() in your wizard page, you can publish the event and the 'host' page will react and flip the page.
That's of course all nice if you're using any MVVM framework that's out there. MVVM Light has the Messenger, Caliburn.Micro has - as you might have noticed - IEventAggregator.

How to pass information from one WPF UserControl to another WPF UserControl?

I've got a WPF application.
On the left side there is a stackpanel full of buttons.
On the right side there is an empty dockpanel.
When user clicks a button, it loads the corresponding UserControl (View) into the dockpanel:
private void btnGeneralClick(object sender, System.Windows.RoutedEventArgs e)
{
PanelMainContent.Children.Clear();
Button button = (Button)e.OriginalSource;
Type type = this.GetType();
Assembly assembly = type.Assembly;
IBaseView userControl = UserControls[button.Tag.ToString()] as IBaseView;
userControl.SetDataContext();
PanelMainContent.Children.Add(userControl as UserControl);
}
This pattern works well since each UserControl is a View which has a ViewModel class which feeds it information which it gets from the Model, so the user can click from page to page and each page can carry out isolated functionality, such as editing all customers, saving to the database, etc.
Problem:
However, now, on one of these pages I want to have a ListBox with a list of Customers in it, and each customer has an "edit" button, and when that edit button is clicked, I want to fill the DockPanel with the EditSingleCustomer UserControl and pass it the Customer that it needs to edit.
I can load the EditCustomer usercontrol, but how do I pass it the customer to edit and set up its DataContext to edit that customer?
I can't pass it in the constructor since all the UserControls are already created and exist in a Dictionary in the MainWindow.xaml.cs.
so I created a PrepareUserControl method on each UserControl and pass the Customer to it and can display it with a textbox from code behind with x:Name="..." but that is not the point, I need to DataBind an ItemsControl to a ObservableCollection to take advantage of WPF's databinding functionality of course.
so I tried to bind the ListBox ItemSource in the View to its code behind like this:
<UserControl.Resources>
<local:ManageSingleCustomer x:Key="CustomersDataProvider"/>
</UserControl.Resources>
<DockPanel>
<ListBox ItemsSource="{Binding Path=CurrentCustomersBeingEdited, Source={StaticResource CustomersDataProvider}}"
ItemTemplate="{DynamicResource allCustomersDataTemplate}"
Style="{DynamicResource allCustomersListBox}">
</ListBox>
</DockPanel>
which gets a stackoverflow error caused by an endless loop in the IntializeComponent() in that view. So I'm thinking I'm going about this in the wrong way, there must be some easier paradigm to simply pass commands from one UserControl to another UserControl in WPF (and before someone says "use WPF commanding", I already am using commanding on my UserControl that allows the user to edit all customers, which works fine, but I have to handle it in my code behind of my view (instead of in my viewmodel) since I need the parent window context to be able to load another user control when its finished saving:
<Button Style="{StaticResource formButton}"
Content="Save"
Command="local:Commands.SaveCustomer"
CommandParameter="{Binding}"/>
private void OnSave(object sender, System.Windows.Input.ExecutedRoutedEventArgs e)
{
Customer customer = e.Parameter as Customer;
Customer.Save(customer);
MainWindow parentShell = Window.GetWindow(this) as MainWindow;
Button btnCustomers = parentShell.FindName("btnCustomers") as Button;
btnCustomers.RaiseEvent(new RoutedEventArgs(Button.ClickEvent));
}
So how in WPF can I simply have a UserControl loaded in a DockPanel, inside that UserControl a button with a command on it that loads another UserControl and sends that UserControl a specific object to which it can bind its controls?
I can imagine I just do not know enough about WPF commands at this point, if anyone can point me in the right direction from here, that would be great, or if you think this "loading UserControls in a DockPanel pattern is foreign to WPF and should be avoided and replaced with another way to structure applications", that would be helpful news as well. You can download the current state of my application here to get an idea of how it is structured. Thanks.
I've just finished a LOB application using WPF where this sort of problem/pattern appeared constantly, so here's how I would have solved your problem:
1) In the DataTemplate where you create each item in the ListBox, along with it's edit button, bind the Button's tag property to the Customer object underlying that list box item.
2) Create a Click event handler for the button, and set the Button's Click event to fire the handler.
3) In the event handler, set the Content property of the UserControl.
4) Set up a DataTemplate in scope of the User Control (perhaps in the resources of it's immediate container) which describes an editor for that single customer.
Another approach that will work is to declare a Customer dependency property on your EditCustomer class, then set that property (perhaps through a XAML Trigger) when the button is clicked.
I hope this isn't too vague. If nothing else, know that the problem you're facing is very solvable in WPF.
This is where you use the Mediator pattern. There's several blog posts on this topic (for instance), and there's implementations of the pattern in some WPF frameworks (such as EventAggregator in Prism).
I don't have the time to really dig into this (it's an interesting question and I hope you get a good answer-- I can see myself running into a similar situation in the future).
Have you considered getting a little less WPF-y and falling back to firing an event on your source UserControl with an EventArgs that contains the customer, then in the event handler, firing the appropriate command on the target control?

Categories

Resources