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
Related
I have the following views and viewModels View: Staff, VM: StaffViewModel and View: Notes, VM: NotesViewModel.
My StaffViewModel has a SelectedStaffMember property of type SelectedEmployee.
The Staff view has a button that launches another view (Notes).
When the user enters a note I need to save it against the SelectedEmployee, which means the NotesViewModel needs to know the currently selected employee ID.
Right now Im doing this via my ViewModelLocator but this seems wrong, what is the corret way to pass data to a VM???
I'm using MVVM Light.
Relevant code - StaffViewModel
public Employee SelectedEmployee
{
get { return _selectedEmployee; }
set
{
if (value == _selectedEmployee) return;
_selectedEmployee = value;
HolidayAllowance = _staffDataService.GetEmployeeHolidayAllowance(_selectedEmployee.Id);
RaisePropertyChanged();
RaisePropertyChanged(nameof(HolidayAllowance));
}
}
NoteViewModel
public RelayCommand SaveNoteCommand { get; private set; }
private void SaveNote()
{
var note = new Note
{
NoteContent = NoteContent,
EmployeeId = ViewModelLocator.Staff.SelectedEmployee.Id,
NoteDate = NoteDate
};
_dataService.SaveNote(note);
}
I'm using MahApps Flyouts to show the view for add note:
This is where the view is shown, it is launched from MainView.xaml NOT Staff.xaml, which I think is going to be another issue of getting SelectedEmployee ID:
MainView.xaml
<controls:Flyout Name="AddNoteFlyout"
Header="Add Note"
IsModal="True"
IsOpen="{Binding IsAddNoteOpen}"
Opacity="85"
Position="Right"
Width="450">
<views:AddNote VerticalAlignment="Top" Margin="0,30,0,0"/>
</controls:Flyout>
Im considering firing a message on the button click that launches the View, which my staff view would register against. The message would contain the selectedEmployeeId. Would that be a better way?
The simple way
The simple way is what you are doing, but maybe a bit better solution is to create a static or singleton class like a NavigationParameterContainer and store the selected StaffMember in a public property. Then you can retrieve it in your NotesViewModel
The best practice
The better solution for passing data between ViewModels is using a custom navigation service, and navigation aware ViewModels.
MVVMLight don't support this, so either you use a different framework like Prism or write yourself an architecture that you can use for making parameterized navigationt.
The base idea is that you create an INavigationAware interface that support navigation lifecycle callbacks like OnNavigatedTo, which receives an object representing the NavigationParamter (the selected StaffMember).
Then you create some kind of NavigationService with a Navigate method, that accepts some parameter to determine the Page you want to navigate to, and an object wich is the NavigationParamter.
When you navigate you call the Navigate method on your Service and pass the selected item as parameter. Then you need to make the actual navigation inside your service, and after the navigation is finished, you call the OnNavigatedTo callback on your ViewModel if it is implementing the INavigationAware interface. (You can retreive the VM from the Page.DataContext and cast it to INavigationAware if it is not null you can call the OnNavigatedTo).
Finally in your NotesViewModel you just need to implement the INavigationAware interface, and handle the parameter you received in the OnNavigatedTo method.
This is just the basic idea but I strongly recommend you to see some MVVM framework that already implements this. (like PrismLibrary).
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>();
}
}
little question. I'm progrraming in MVVM design pattern (C#).
the View is an Excel add-in, and I want to run from the View_Model a method that found in the View. I thought about 2 ways:
Hold in the View Model a pointer to the View (the pointer type is an interface that the View and the View_Model inherit from, otherwise I would get a circular independence because the View hold the View_Model and the View_Model hold the View), and then run the method directly from him.
Create an event in the View_Model and sign the method in the View to that event in the View_Model pointer which I already have in the View, and raise the event when I want to run this method.
Somehow, way number 2 feel little like screaming in the street to only one man instead off go straight and talk to him normally. On the other hand, the advantage in way number 2, is that in the future if I would have other method that relate to this operation I could just sign them to that event and not call them specially.
what would you do?
Method 1 breaks the MVVM principle since the view model should not know about the view. Your approach is a bit like the MVP design pattern.
I would say method 2 is the better approach, it's perfectly fine for the view to know about the view model. It also gives you the flexibility to do whatever you want when that event is raised, say for example, if you wanted to use a different view and call a different method.
Also, this kind of thing can be done using a good messaging framework. The view model publishes a message, the view subscribes to it. The mvvmlight frame work has a good implementation of this, but I am sure there are others too.
take an Interface, implement the view from it. And instantiate the viewmodel by passing this view.
lets say -
Interface
public interface IFooView
{
...
}
View
public Class FooView : IFooView
{
private FooViewModel _viewModel;
public FooView()
{
_viewModel = new FooViewModel(this);
}
}
ViewModel
public class FooViewModel
{
private FooView _view;
public FooViewModel(IFooView view)
{
_view = view;
}
}
Now, you can call the ViewModel from View, also View from ViewModel.
I am trying to close a window from its ViewModel. I am using the MVVM pattern. I have tired to get the window using;
Window parentWindow = Window.GetWindow(this);
But I cannot do this, how do I get the window of the ViewModel so I am able to close the window. I want to be able to do this in code.
Can you find the parent window in code?
ViewModels should not be referencing the View in any way, including closing windows, in MVVM.
Instead, communication between the View and ViewModel is typically done through some kind of Event or Messaging System, such as Microsoft Prism's EventAggregator, or MVVM Light's Messenger
For example, the View should subscribe to listen for event messages of type CloseWindow, and when it receives one of those message it should close itself. Then the ViewModel simply has to broadcast a CloseWindow message anytime it wants to tell the View to close.
There's a brief overview of event systems in MVVM, and some examples, on my blog post about Communication between ViewModels if you're interested
yes referencing view in viewmodel isn't best practice. WHY? because when you unit test your viewmodel it is require you to instantiate view, for small view will not difficult to do that, but for a complex view with complex tree of dependency? that wont be good.
for me, the easiest way to do communication with view is by passing IInputElement on viewmodel constructor. the bennefit of IInputElement is Routed Event backbone, it has RaiseEvent and AddHandler method required for routed event. thus you can bubble/tunnel/direct event to any view or viewmodel on your application freely without any additional library.
here is my the simplified code on viewmodel but remember this technique only work for view first approach
public class MyViewModel : INotifyPropertyChanged
{
public static readonly RoutedEvent RequestCloseEvent = EventManager.RegisterRoutedEvent("RequestClose",
RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(MyViewModel));
private IInputElement dispatcher;
public MyViewModel(IInputElement dispatcher)
{
this.dispatcher = dispatcher;
}
public void CloseApplication()
{
dispatcher.RaiseEvent(new RoutedEventArgs(RequestCloseEvent));
}
}
on your View simply
DataContext = new MyViewModel(this)
//notice "this" on the constructor
and the root view (Window) of your application simply
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
AddHandler(MyViewModel.RequestCloseEvent, new RoutedEventHandler(onRequestClose));
}
private void onRequestClose(object sender, RoutedEventArgs e)
{
if (MessageBox.Show("Are you sure you want to quit?", "Confirmation", MessageBoxButton.YesNo) == MessageBoxResult.Yes)
{
Close();
}
}
}
and because IInputElement is interface rather than class, you easily create a mock class for your unit test
var target = new MyViewModel(new DispatcherMock)
or you can use mock library like RhinoMocks
for further reading, you can learn more about how to use Routed Event
Let the ViewModel do this, if really in need.
The Models says for example, that there are no longer valid data
pass that information to the ViewModel
the ViewModel recognizes, that it can no longer display anything
and then closes the window.
An empty view is the normal way of expressing that there are no more data
You can define an action in your ViewModel
public Action CloseAction { get; set; }
then, in your window (for example in the DataContextChanged) you can set this action :
((IClosable)viewModel.Content).CloseAction = () => System.Windows.Application.Current.Dispatcher.Invoke(Close());
Well, all this is part of a bigger dependency injection pattern, but basic principle is here...
Next, you juste need to call the action from the VM.
There is a useful behavior for this task which doesn't break MVVM, a Behavior, introduced with Expression Blend 3, to allow the View to hook into commands defined completely within the ViewModel.
This behavior demonstrates a simple technique for allowing the
ViewModel to manage the closing events of the View in a
Model-View-ViewModel application.
This allows you to hook up a behavior in your View (UserControl) which
will provide control over the control's Window, allowing the ViewModel
to control whether the window can be closed via standard ICommands.
Using Behaviors to Allow the ViewModel to Manage View Lifetime in M-V-VM
http://gallery.expression.microsoft.com/WindowCloseBehavior/
In a MVVM WPF application.
How do you set a second windows parent from the ViewModel?
example:
view1 -- viewModel1
viewModel1's command calls:
var view2 = new view2
view2.Owner = <----This is the problem area. How do I get view1 as the owner here from the viewModel?
view2.Show()
EDIT:
See accepted answer below, then read the following edit.
I'am using MVVM light -> http://mvvmlight.codeplex.com/ (awesome btw)
The baked-in messaging system is great. I am now sending a message from the viewmodel to my view telling it to show another window.
For the message I'am currently using a string with a switch statement in the main view to determine what view to open; however I may tinker with the tokens that also are part of MVVM light toolkit.
Thank you!
In my opinion, opening a new window is the responsibility of the View, not of the ViewModel. Personally, I would use the same approach as used for displaying a dialog box (this was discussed in this forum already):
Have the ViewModel send a Message to the View requesting that it opens a new Window.
(alternatively) use an IDialogService or whatever you want to call it which you pass to the ViewModel's constructor. This service will be in charge of opening the Window (or of delegating this task to the View).
This way, you keep a clean separation of concerns and your VM remains testable (you can unit test that the request to open the new WIndow has been sent, but you couldn't test that the window has been, indeed, open).
Does that make sense?
Cheers,
Laurent
From your viewmodel call
Messenger.Default.Send<NotificationMessage>(new NotificationMessage("Open Window"));
And from your view's codebehind (a view that call the second
view) easily write this in the constructor:
Messenger.Default.Register<NotificationMessage>(this, ReplyToMessage);
And also write this method in the view's codebehind:
private void ReplyToMessage(NotificationMessage msg)
{
if (msg.Notification == "Open Window")
{
SecondWindow win = new SecondWindow();
win.ShowDialog();
}
}
I don't have an answer of my own but here's a few links to things I've been looking at lately that might help. I'll also be interested in anything others suggest.
As I understand it, the key thing is, you shouldn't be creating Views from within a View Model if possible, so you need a means of communicating what you need in a loosely coupled fashion.
http://www.codeproject.com/KB/WPF/XAMLDialog.aspx
http://www.codeproject.com/KB/architecture/MVVM_Dialogs.aspx
Handling Dialogs in WPF with MVVM
You can do in this way like you need to create some events and register those in view and call these in view model.and open that pop up window.
Like This example
public class Mainclass : MainView
{
public delegate abc RegisterPopUp(abc A);
public RegisterPopUp POpUpEvent;
public RelayCommand ShowCommand { private set; get; }
public void ShowCommand()
{
ShowCommand("Your parameter");
}
}
inside the view
MainView mn = new MainView();
Register the event here like mn.POpUpEvent += then click on tab button double time and in registers popup method write the code for opening the pop up window.
Prism-Event Aggrigator is good approach, where we can create independent module without dependency. first viewmodel will publish event and then another view or view or viewmodel can subscribe that event from event aggrigator.
in this case Unity container can also use to inject one viewmodel in to another with dependency injection.