Updating a viewmodel from another viewmodel - c#

I have two ViewModels one is attached to a main window and the other is attached to child window that is opened by clicking on a button on the main window. The child window contains a list of items and I want to select an item and display it in the main window by updating the main window viewmodel. What is the best way to accomplish this. Thanks!

There are any number of ways to do this.
Pass a reference to the main/parent view model into the child and have the child call the main view model.
Have the child view model fire an event that the parent subscribes to.
Use a messenger/mediator to communicate between the two. The parent subscribes, the child posts the message. This provides loose coupling.
Set the main view model up as a global service. Register it somehow. Have the child look up the service (requiring global services is a pretty common problem) and then call something on the global/common interface.
From my experience, the simplest method would be #2. Define an event on the child view model. The parent will have to look up the child (I don't know if it contains it or how your view models are constructed) and subscribe to the event.

The standard way to communicate between ViewModels is to use Messaging of some sort. One good implementation of this is the MVVM Light Toolkit
Here's some (random) code using the default messenger therefrom:
//Registering:
Messenger.Default.Register<IEnumerable<BookViewModel>>(this, true, fillSourceWith);
Messenger.Default.Register<DisplayModeEnum>(this, ChangeMainTemplates);
//with a specific "token"
Messenger.Default.Register<object>(this, MessageTokens.ClearList, o => BookSource.Clear());
//Sending
Messenger.Default.Send<List<BookViewModel>>(newBooks);
Messenger.Default.Send<DisplayModeEnum>(DisplayModeEnum.MediumLayout);
Messenger.Default.Send<object>(null, MessageTokens.ClearList);

Best way to do it is to have some kind of reference from child to parent and to update this parent when closing the child.
Or you can have some kind of event on child and let parent listen on this event. You then raise this event when child closes.
Messaging is used when both ViewModels are logicaly unrelated.
[rant] Dont people even know basic principles of OOP or what?? [/rant]

I had the same problem a few days before ;-)
Finally I used a mediator to communicate both view-models. On fact I used the Messenger from MVVM Light.
public void Search(object parameter)
{
ChildWindow window = new ChildWindow();
SearchWindow pageSearch = new SearchWindow();
window.Content = pageSearch;
window.Show();
Messenger.Default.Register<Messages.CloseWindowMessage>(this, action => this.closeWindow(action));
}
After that I defined the Message with everything I needed to know from main window:
public class CloseWindowMessage : MessageBase
{
public bool Result { get; set; }
public Model.Selected Selected { get; set; }
}
Finally the message back from the childwindow you only have to register the message with result and the object you want to get back.
You should register from the code-behind of your view to close the window.

I think best way to pass the message between two view models is event programming.

Related

Prism: ViewModelLocator creates the view model instance(s) but how can I target it?

Is there a way whereby I can identify a ChildViewModel instance, which was created by prism's ViewModelLocator, upon opening it's corresponding window?
I would like to trigger that the ChildViewModel should load it's data, based on parameters originating from the MasterViewModel.
In code, in MasterViewModel has an ICommand in a which is responsible for requesting opening a new child window by publishing an event, and there is a corresponding subscriber.
public ICommand OpenNewChildWindow()
{
Publish(new OpenNewChildWindowPubSubEvent());
// Maybe I can publish a new PubSubEvent here
// but how can I target just the recently created ChildViewModel?
}
Notice that the MasterViewModel knows nothing about the UI implementation.
The the subscriber calls ShowWindow method on a custom WindowManager which basically resolves the View (Window in this instance) which corresponds to the ViewModel which was passed in.
public void ShowWindow(Type viewModelType)
{
Type view = ResolveView(viewModelType);
Window w = (Window)Activator.CreateInstance(view);
w.Show();
}
The xaml for the window the appropiate
ViewModelLocator.AutoWireViewModel="True"
Go for a view model-first style of navigation. If you pass a (child-)view model instance (instead of a type) to ShowWindow, you can create that with whatever data you need.
Probably, you pass the data around as payload of the OpenNewChildWindowPubSubEvent, and the subscriber then creates the view model. Or you create the view model immediately in the command and pass that on as payload of the event.
Anyway, don't resolve the view type from the view model type just to resolve the view model type from the view :-)
BTW, the ViewModelLocator is great and really simplifies things, but you don't want to use it here, because you're not navigating within one shell, but creating new windows. If you would, your view model would implement INavigationAware and you'd pass the data to the child view model as parameter to RequestNavigate...

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)

How do I clean up the views inside a TabControl when the parent view is unloaded?

I have a view (I'll call it MainView) that contains a TabControl. The views that make up the TabItems are created using prism view discovery in MainView's ViewModel. Each of the views that are "tabs" have some cleanup that needs to be done (detaching event handlers, etc.) when I'm done with the tab control (i.e. during the MainView's Unloaded event). However, I can't do the cleanup with the Tab views' Unloaded event, as this is called when just switching tabs.
MainView is calling a method on its ViewModel when Unloaded fires, but that ViewModel does not have a reference to the Views or ViewModels that make up the tabs due to the way those views are registered. What is the proper way to clean up after my "discovered" tab views?
I have a similar situation, but we are using a Dock control where the views are loaded using Prism. So, in the Shell Views code behind unloaded event, we loop over the open Views and get the ViewModel for each view. All of our ViewModels inherit from a base ViewModel that has a virtual bool CanClose method that returns whether the view can close or not. The base ViewModel just returns true. This method is used to check if there are validation errors, unsaved changes, etc. So, then you would override this method and perform the clean up your talking about. If all of the views return true, then you could call the main shell viewmodel unload, if not then you can cancel the main view from unloading.
foreach (var doc in dockManager.Documents)
{
if (!doc.CanClose())
{
e.Cancel = true;
return;
}
}
We ended up using a message via the EventAggregator to clean up the sub-views.
I use Prism navigation in my application, and faced the same issue.
To address the problem, in the parent view model, in the OnNavigatedFrom method, I close all the views in the TabControl's region:
public class ParentViewModel : INavigationAware
{
...
public void OnNavigatedFrom(NavigationContext navigationContext)
{
var region = RegionManager.Regions["TabsRegion"];
foreach (var view in region.Views)
region.Remove(view);
}
}

MVVM and control creation

Imagine a simple scenario with a WPF window containing a button and some clear space. Clicking the button creates a new custom/user control and places it somewhere randomly on the window.
Clicking one of these controls will remove it from the window.
So now I have a ViewModel ala MVVM which exposes an ICommand for the "create new" button, but where does the code to create the new control live? Each control will probably have its own ViewModel which will handle its deletion and positioning I guess.
Can it be achieved with no code behind on the window AND no real knowledge of the View by the ViewModel?
The code that causes the controls to be created lives inside your "main" ViewModel.
The code that actually creates the controls is the container.
So it would go something like:
void AddControlCommandExecuted() {
var container = // resolve your DI container here
// Now use the container to resolve your "child" view. For example,
// if using UnityContainer it could go like this:
var view = container.Resolve<ChildView>();
// Of course you can also resolve the ViewModel if your program is
// "ViewModel-first" instead of "View-first".
// Does the ChildViewModel need some properties to be set?
var viewModel = (ChildViewModel)view.DataContext;
viewModel.StringProperty = "blah";
// Now get a reference to the region in your View which will host
// the "child" views.
var regionManager = container.Resolve<IRegionManager>();
var region = regionManager.Regions["MyRegionName"];
// Finally add the view to the region. You can do it manually, you
// can use the concept of "navigation" if your MVVM framework has one
// (I 'm using Prism, which does), etc etc.
region.Add(view);
}
Update: When writing the answer, I forgot that not all MVVM frameworks have Regions as Prism does. So excuse the specificity of the code above, as it doesn't really change anything. You simply need to build something like the Region abstraction yourself. Let's see:
class MyViewModel {
public event EventHandler<ChildViewModelAddedEventArgs> ChildViewModelAdded;
}
MyView would then attach an event handler to this event, and pick up the ChildView instance from inside ChildViewModelAddedEventArgs so that it can be added to an ItemsControl it is the parent of without your ViewModel messing with such details.
Of course this means that you now need some code-behind, but this cannot be helped unless you are using a framework that provides such services itself.
This SHOULD be doable with some very careful databinding on an ItemsControl, not sure how you would achieve the layout, but you will have a parent view model containing a collection of child view models, layout would then be preformed by the ItemsControl. When the parent ViewModel created the child ViewModel, it should inject a RelayCommand as a lambda expression to remove and cleanup the child ViewModel from the parents collection.

Having trouble deciding how to wire up a UserControl with MVVM

I've been doing the best I can to try to stay true to the separation recommended by the MVVM pattern. One thing I haven't figure out how to do correctly has to do with initializing my UserControls.
My most recent example of this has to do with a library that I wrote to talk to some low-level hardware. That assembly happens to have a UserControl that I can simply drop into any GUI that uses this hardware. All that is necessary for it to work is to set a reference to the object that has access to the low level methods.
However, that's where my problem lies -- currently, the UserControl is added to the GUI via XAML, where I define the namespace and then add the UserControl to my window. Of course, I have no control over its creation at this point, so the default constructor gets called. The only way to set the necessary reference for hardware control involves calling a method in the UC to do so. The ViewModel could feasibly call a method in the Model, e.g. GetController(), and then call the method in the UserControl to set the reference accordingly. The GUI can pass a reference to the UserControl to the ViewModel when said GUI creates the ViewModel, but this violates MVVM because the ViewModel shouldn't know anything about this control.
Another way I could deal with this is to not create the UserControl in XAML, but instead do it all from code-behind. After the ViewModel gets initialized and retrieves an initialized UserControl (i.e. one that has the low-level object reference set), it can set the Content of my Window to the UserControl. However, this also violates MVVM -- is there a way to databind the Content of a Window, TabControl, or any other element to a UserControl?
I'd like to hear if anyone has had to deal with this before, and if they approached it the first or second way I have outlined here, or if they took a completely different approach. If what I have asked here is unclear, please let me know and I'll do my best to update it with more information, diagrams, etc.
UPDATE
Thanks for the responses, guys, but I must not have explained the problem very well. I already use RelayCommands within the UserControl's ViewModel to handle all of the calls to the hardware layer (Model) when the user clicks in the control in the UserControl itself. My problem is related to initially passing a reference to the UserControl so it can talk to the hardware layer.
If I create the UserControl directly in XAML, then I can't pass it this reference via a constructor because I can only use the default constructor. The solution I have in place right now does not look MVVM-compliant -- I had to name the UserControl in XAML, and then in the code-behind (i.e. for the View), I have to call a method that I had added to be able to set this reference. For example, I have a GUI UserControl that contains the diagnostics UserControl for my hardware:
partial class GUI : UserControl
{
private MainViewModel ViewModel { get; set; }
public GUI( Model.MainModel model)
{
InitializeComponent();
ViewModel = new MainViewModel( model, this.Dispatcher);
ViewModel.Initialize();
this.DataContext = ViewModel;
diagnostics_toolbar.SetViewModel( ViewModel);
user_control_in_xaml.SetHardwareConnection( model.Connection);
}
}
where the outer class is the main GUI UserControl, and user_control_in_xaml is the UserControl I had to name in the GUI's XAML.
Looking at this again, I realize that it's probably okay to go with the naming approach because it's all used within the View itself. I'm not sure about passing the model information to user_control_in_xaml, because this means that a designer would have to know to call this method if he is to redo the GUI -- I thought the idea was to hide model details from the View layer, but I'm not sure how else to do this.
You will also notice that the main GUI is passed the Model in the constructor, which I assume is equally bad. Perhaps I need to revisit the design to see if it's possible to have the ViewModel create the Model, which is what I usually do, but in this case I can't remember why I had to create it outside of the GUI.
Am new to MVVM myself but here's a possible solution:
Create a property in your VM that is of the object type (that controls the hardware) and bind it to an attached property on your UserControl. Then you could set the property in your VM using dependency injection, so it would be set when the VM is created. The way I see it, the class that talks to the hardware (hardware controller) is a service. The service can be injected to your view model and bound to your UserControl. Am not sure if this is the best way to do it and if it is strict enough to all the MVVM principles but it seems like a possible solution.
if your question is: How do i show my viewmodel in the view? then my solution is always using viewmodelfirst approach and datatemplates.
so all you have to do is wire up your viewmodel via binding to a contentcontrol.content in xaml. wpf + datatemplates will do the work and instantiate your usercontrol for your viewmodel.
You are right, the ViewModel shouldn't know about anything in the View - or even that there is such a thing as a View, hence why MVVM rocks for unit testing too as the VM couldn't care less if it is exposing itself to a View or a test framework.
As far as I can see you might have to refactor things a little if you can. To stick to the MVVM pattern you could expose an ICommand, the ICommand calls an internal VM method that goes and gets the data (or whatever) from the Model, this method then updates an ObservableCollection property of the data objects for the View to bind to. So for example, in your VM you could have
private ICommand _getDataCommand;
public ICommand GetDataCommand
{
get
{
if (this._getDataCommand == null)
{
this._getDataCommand = new RelayCommand(param => this.GetMyData(), param => true);
}
return this._getDataCommand;
}
}
private void GetMyData{
//go and get data from Model and add to the MyControls collection
}
private ObservableCollection<MyUserControls> _uc;
public ObservableCollection<MyUserControls> MyControls
{
get
{
if (this._uc == null)
{
this._uc = new ObservableCollection<MyUserControls>();
}
return this._uc;
}
}
For the RelayCommand check out Josh Smiths MSDN article.
In the View you could either call the ICommand in the static constructor of your UC - I am guessing youwould need to add an event in your class for this - or call the ICommand from some sort of click event on your UC - maybe just have a 'load' button on the WPF window. And set the databinding of your UC to be the exposed observable collection of the VM.
If you can't change your UC at all then you could derive a new class from it and override certain behaviour.
Hope that helps a bit at least, like I say, have a look at Josh Smiths MVVM article as he covers the binding and ICommand stuff in there brilliantly.
If you set the DataContext of the Window or UserControl containing thisUserControl to the main view model, the user control can call SetHardwareConnection() on itself in its Loaded event (or DataContextChanged event handler).
If that's not possible because you're saying the UserControl is 'fixed', you should derive from it or wrap it up in another UserControl, which would serve as a MVVM 'adapter'.
(In order to bind the window: you could make the MainViewModel a singleton with a static Instance property and use DataContext="{x:Static MyClass.Instance}". A nice way to get things going quickly)
Note; this is based on my understanding that MVVM works because of Bindings.. I always bind the control to a ViewModel, not pass a ViewModel as a parameter.
Hope that helps!

Categories

Resources