Right Way to access a View Model from an existing View Model - c#

I am somewhat new to MVVM. I am not sure what the best way is to do what I am trying to do.
Here is the scenario:
I have a VM that is going to show another window. I can call myNewWindowView.Show(), but first I need to set some data in the VM of my new window.
Should I expose both the myNewWindowView and the NewWindowViewModel to the calling ViewModel?
Here is an example:
class MainVM
{
public void FindCustomer(string nameParial)
{
List<Customer> customers = ServiceCall.GetCustomers(nameParital);
// This is the part I am not sure how to do. I am not sure if this
// View Model should have a reference to a different view model and
// the view too.
myNewWindowViewModel.CustomerList = customers;
myNewWindowView.Show();
}
}

I would keep the viewmodel separate from any view. I tend to think of them as layers, but they are only interchangeable in one direction.
So a model of type foo can have any view model layered on top of it, and it never expects or cares about the viewmodel type.
A viewmodel can only be for one type of model, but it doesn't care or expect what type of view will use it.
A view will be for a particular type of viewmodel.
What you seem to have is a viewmodel the cares about what views are doing, which seems wrong to me.
If it were me, I'd get the view for the MainVM to display the new window, getting the MainVM to pass out the appropriate viewmodel for the new window.
This is the code I would put behind the view for the main viewmodel
class MainWindow : Window
{
public MainWindow()
{
Initialize();
DataContext = new MainVM();
}
public void FindCustomerClick(object sender, RoutedEventArgs args)
{
CustomerListView clv = new CustomerListView();
clv.DataContext = (DataContext as MainVM).FindCustomer(search.Text);
clv.Show();
}
}
As you can see, the viewmodel has a method that takes a string and returns a CustomerListViewModel, which is then applied to the DataContext of a CustomerListView.

Don't reference views inside your view model. Have views create views and view models create view models.
A simple way to accomplish this separation of concerns is with events. The quick and dirty way to do it is to create a ChildViewModel property on your parent view model, and then handle PropertyChanged in the view, e.g.:
ParentViewModel vm = (ParentViewModel)DataContext;
vm.PropertyChanged += delegate(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == "ChildViewModel")
{
MyChildWindow w = new MyChildWindow();
w.Show(vm.ChildViewModel);
}
};
Now every time the parent view model changes the ChildViewModel property, the parent view will open a new child view.
A less quick, and less dirty, approach is to create a CreateViewEventHandler delegate and CreateViewEventArgs class, and make the event handler, and a protected OnCreateView method, part of your base view model class (assuming you have one). This allows the view model to be a lot more explicit about when a child window should be created.
Note that if it's important for the parent view model to know when the child view model has closed, the child view model can expose an event that the parent can subscribe to (or, again, use a property and the PropertyChanged event).
Note that in both cases, you can write unit tests that verify that the view model opens the window (i.e. raises the event) when it's supposed to without involving the views.
I haven't used any MVVM frameworks myself, but the ones I've looked at have messaging tools that are designed to facilitate this kind of thing.

Related

Consultation about the best design for MVVM

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.

How to manage multiple windows in MVVM

I am aware there are a couple of questions similar to this one, however I have not quite been able to find a definitive answer. I'm trying to dive in with MVVM, and keep things as pure as possible, but not sure how exactly to go about launching/closing windows while sticking to the pattern.
My original thinking was data bound commands to the ViewModel triggering code to start a new View, with the View's DataContext then set to it's ViewModel via XAML. But this violates pure MVVM I think...
After some googling/reading answers I came across the concept of a WindowManager (like in CaliburnMicro), now if I was to implement one of these in a vanilla MVVM project, does this go in with my ViewModels? or just in the core of my application? I'm currently separating out my project into a Model assembly/project, ViewModel assembly/project and View assembly/project. Should this go into a different, "Core" assembly?
Which leads on a bit to my next question (relates somewhat to the above), how do I launch my application from an MVVM point of view? Initially I would launch my MainView.xaml from App.xaml, and the DataContext in the XAML would attach the assigned ViewModel. If I add a WindowManager, is this the first thing that is launched by my Application? Do I do this from the code behind of App.xaml.cs?
Well it mainly depends on how your application looks like (i.e. how many windows opened at the same time, modal windows or not...etc).
A general recommendation I would give is to not try to do "pure" MVVM ; I often read things like "there should be ZERO code-behind"...etc., I disagree.
I'm currently separating out my project into a Model assembly/project,
ViewModel assembly/project and View assembly/project. Should this go
into a different, "Core" assembly?
Separating views and ViewModels into different assemblies is the best thing you can do to ensure you won't ever reference something related to the views in your viewModel. You'll be fine with this strong separation.
Separating Model from ViewModel using two different assemblies could be a good idea too, but it depends on what your model looks like. I personally like 3-tier architectures, so generally my model is the WCF client proxies and are indeed stored in their own assembly.
A "Core" assembly is always a good idea anyway (IMHO), but only to expose basic utility methods that can be used in all the layers of your application (such as basic extension methods....etc.).
Now for your questions about views (how to show them...etc), I would say do simple. Personally I like instantiating my ViewModels in the code-behind of my Views. I also often use events in my ViewModels so the associated view is notified it should open another view for example.
For example, the scenario you have a MainWindow that should shows a child window when the user click on a button:
// Main viewModel
public MainViewModel : ViewModelBase
{
...
// EventArgs<T> inherits from EventArgs and contains a EventArgsData property containing the T instance
public event EventHandler<EventArgs<MyPopupViewModel>> ConfirmationRequested;
...
// Called when ICommand is executed thanks to RelayCommands
public void DoSomething()
{
if (this.ConfirmationRequested != null)
{
var vm = new MyPopupViewModel
{
// Initializes property of "child" viewmodel depending
// on the current viewModel state
};
this.ConfirmationRequested(this, new EventArgs<MyPopupViewModel>(vm));
}
}
}
...
// Main View
public partial class MainWindow : Window
{
public public MainWindow()
{
this.InitializeComponent();
// Instantiates the viewModel here
this.ViewModel = new MainViewModel();
// Attaches event handlers
this.ViewModel.ConfirmationRequested += (sender, e) =>
{
// Shows the child Window here
// Pass the viewModel in the constructor of the Window
var myPopup = new PopupWindow(e.EventArgsData);
myPopup.Show();
};
}
public MainViewModel ViewModel { get; private set; }
}
// App.xaml, starts MainWindow by setting the StartupUri
<Application x:Class="XXX.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
...
StartupUri="Views/MainWindow.xaml">

Prism region view display on demand, how to refresh controls in active view

I am writing an application using WPF, MVVM, and PRISM regions.
I have two regions with one view (A and B) per region. I add views to my regions in my regionmodules class as follows:
// I use the following private properties in both regionmodules.cs class
private readonly IUnityContainer _container;
private readonly IRegionManager _regionManager;
--A--
private void RegisterViews()
{
var controlsView = _container.Resolve<EmulatorControlsView>();
_regionManager.Regions["ControlsRegion"].Add(controlsView, "A");
}
--B--
private void RegisterViews()
{
var templatesView = _container.Resolve<TemplatesView>();
_regionManager.Regions["TemplatesRegion"].Add(templatesView, "B");
var view = _regionManager.Regions["TemplatesRegion"].GetView("B");
_regionManager.Regions["TemplatesRegion"].Deactivate(view);
}
I deactivate view "B" during initialization of my app because view B overlays view A in my shell.xaml.
A click command from view A will activate view B and deactivate view A per the following code:
private void LoadTemplateExecute()
{
var controlView = _regionManager.Regions["ControlsRegion"].GetView("A");
_regionManager.Regions["ControlsRegion"].Deactivate(controlView);
var templateView = _regionManager.Regions["TemplatesRegion"].GetView("B");
_regionManager.Regions["TemplatesRegion"].Activate(templateView);
}
Other button commands exist in my Templates region view model that Deactivates view A and Activates view B.
This code works great, and allows me to display my views on demand.
Problem: View B has a ListView bound to an ObservableCollection in the view model via dependency property. I currently add string items to my ObservableCollection by way of the view model constructor:
private void InitializeTemporaryLists()
{
TemplateList.Add("Dog");
TemplateList.Add("Cat");
TemplateList.Add("Horse");
}
However, when view B is activated, my ObservableCollection does not show the items in its list.
Question: Is there an event available in the region view or view model to notify me when the view has become active?
What is the best way for me to populate data to my view from the view model each time my view becomes active?
Any ideas and code example are very much appreciated!
* Update *
My ListView was not updating because I am a silly programmer. I had not declared the ObservableCollection<> type in my dependency property.
However, I am still curious to know how you smart programmers out there might handle a prism region view activation? I wish I could subscribe to an event like the following:
_regionManager.Regions["YourRegion"].OnActiveView() += HandleActiveViewEvent;
You can implement the IActiveAware interface to receive notifications when a view is activated and deactivated. This is called by the framework, if you implement it on on the view or the viewmodel (but not both).
If you want activation notification from outside the view or viewmodel, you can also monitor the region's ActiveViews collection for changes. This supports the INotifyCollectionChanged interface.

WPF: Close a Window from Model MVVM

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 C#, how can I set DataContext on a View from the ViewModel?

I"m trying to wrap my head around MVVM. I understand a lot of it, but I'm having difficulty grasping one aspect: Setting DataContext.
I want to show an view with a particular object. The user doesn't get to decide what is visible, so I need to create the view in code. Then, I want to set the DataContext of the view to an object (for binding). I'm trying not to put code in the View for this, but View.LayoutRoot.DataContext isn't public.
What am I missing?
trying to avoid this:
public class View
{
public View(object dataContext)
{
InitializeComponent();
LayoutRoot.DataContext = dataContext;
}
}
with something like this:
public class ViewModel
{
...
public UIElement GetView()
{
UIElement *element = new View();
element.LayoutRoot.DataContext = element;
return element;
}
}
Don't forget that the View should know about the ViewModel, and not the other way around.
So in your case puting code in the ViewModel to create the view isn't the best way around.
Josh Smith's article on MVVM has a section on applying the View to the ViewModel. He recommends using WPF's DataTemplates to select your View in XAML.
If you use a XAML control or Window (which should be the case if you use MVVM), LayoutRoot (Grid by default) is public. In your example, you use just a plain class for View, so it is hard to tell what is going on.
Also, I second Cameron's opinion - nor View or ModelView should deal with assigning DataContext. It can be done in different ways (DataTemplate, dependency injection, special builder class, plain code) but normally it happens on the application level.

Categories