Open/Close View from ViewModel - c#

I have an AddClientViewModel which is referenced by 2 views (AddClientView and SuggestedAddressesView). The AddClientView is a form which has a field for an address. The form has a validate button which validates the entered address using Geocoding. If more than one address is returned, the SuggestedAddressesView opens.
Here is how I am currently doing it:
AddClientViewModel:
private void ValidateExecute(object obj)
{
SuggestedAddresses = new ObservableCollection<DBHelper.GeocodeService.GeocodeResult>(GeoCodeTest.SuggestedAddress(FormattedAddress));
....
if (SuggestedAddresses.Count > 0)
{
var window = new SuggestedAddressesView(this);
window.DataContext = this;
window.Show();
}
}
Here is the SuggestedAddressesView constructor where AddClientViewModel inherits from ViewModelBase
public SuggestedAddressesView(ViewModelBase viewModel)
{
InitializeComponent();
viewModel.ClosingRequest += (sender, e) => this.Close();
}
The other problem I am having is when I call OnClosingRequest() from the AddClientViewModel...both the AddClientView and SuggestedAddressesView closes. I know this happens because both views reference the same ViewModel. This is not the behaviour I want. I would like to be able to independently close either Window.
Is opening a View from the ViewModel proper MVVM structure and how would I go about being able to close windows independently?

As soon as you refer to UI elements(In this case the View) from the VM, you're going against suggested MVVM Guidelines. With just that we can know creating the Window object in the VM is wrong.
So now onto rectifying this:
Firstly try to keep a 1 View <-> 1 VM in your application. It's cleaner and allows you to switch out View implementations with the same logic very easily. Adding multiple View's to the same VM even if not "ground-breaking" just makes it clumsy.
So now you got AddClientView and SuggestedAddressesView with their own VM. Great!
Implementing a View Open/Close from the VM:
Since we cannot access the View directly from our VM(to comply with standards), we can use approaches such as using a Messenger(MVVM Light), EventAggregator(PRISM) and so on to send a "message" from the VM to the View when you need to open/close a View and do the actual operation in the View.
This way the VM just initiates the message and can be unit-tested fine for the same operation and does not reference any UI elements.
Using a "Messenger" approach to handle View open:
As per your Logic, it is the AddClientViewModel which would have to ask for the SuggestedAddressesView to be opened.
Thus when you detect SuggestedAddresses.Count > 0, you would send a message to the AddClientView asking it to open up the SuggestedAddressesView.
In AddClientView.xaml.cs upon receiving this message, you would do what you're currently doing in the VM. Create an object of SuggestedAddressesView and call .Show() on it.
One extra step you would add in the above step's process is to assign the DataContext of SuggestedAddressesView as SuggestedAddressesViewModel.
That's it. Now what you have is, when AddClientViewModel wants SuggestedAddressesView shown, it sends a message to it's own View and the View in-turn creates and shows the SuggestedAddressesView. This way the VM does not reference any View's and we keep to holding MVVM standards.
Using a "Messenger" approach to handle View close:
Closing a View is pretty simple. Again when you need to close the View from the VM, you send a message to it's own View asking for it to be closed.
On receiving this message, the View pretty much closes itself via .Hide() / .Close() or however else you want to get rid of it.
In this each VM handles it's own View's closing and you don't have any inter-connected dependencies.
You can use this as a start point to guide you in handling "messages" for this approach. it has an attached download you can get and see how the Messenger works. This is with MVVM Light, if you do not use it or use something else/ your own MVVM implementation, use it as a guide to help get to what you need.

you can use RelayCommand so that you send the parameter as follows:
Command="{Binding CloseWindowCommand, Mode=OneWay}"
CommandParameter="{Binding ElementName=TestWindow}"
By using this you can close the individual views.
Example:
public ICommand CloseCommand
{
get
{
return new RelayCommand(OnClose, IsEnable);
}
}
public void OnClose(object param)
{
AddClientView/SuggestedAddressesView Obj = param as AddClientView/SuggestedAddressesView;
obj.Close();
}

To open window from ViewModel:
Create NavigationService.cs class for opening window:
Let NavigationService.cs
Now put following code in that class file.
public void ShowWindow1Screen(Window1ViewModel window1ViewModel)
{
Window1= new Window1();
Window1.DataContext = window1ViewModel;
Window1.Owner = Window1View;
Window1.ShowDialog();
}
then.
Create instance of NavigationService.cs class MainWindowViewModel file.
Then
Window1ViewModel window1ViewModel = new Vindow1ViewModel();
window1ViewModel.Name = MainWindowTextValue;
NavigationService navigationService = new NavigationService();
navigationService.ShowWindow1Screen(window1ViewModel);

Related

mvvmcross IOS: How to callback from a ViewModel to a View

I have a MvxViewController and in the ViewDidLoad i bind the button click to the viewmodel. When the button is clicked I open another view in which I will need to return a string back to my first view
public override void ViewDidLoad ()
{
var set = this.CreateBindingSet<MyView1, MyView1ViewModel>();
set.Bind(myButton).To(vm => vm.MyButtonCommand);
set.Apply();
}
public ICommand MyButtonCommand
{
get
{
_myButtonCommand = _myButtonCommand ?? new MvxCommand(MyButtonCommandClick);
return _myButtonCommand;
}
}
private void MyButtonCommandClick()
{
ShowViewModel<ViewModelNumber2>();
}
After some logic is ran in my second view I want to return the string
private void SomeMethodInViewModelNumber2()
{
//Raise event that will get pickup up in MyView
//Or somehow get "SomeString"
if (OnMyResult != null)
OnMyResult ("SomeString");
}
The problem is that I don't want to send the string back using the messenger. I have my reasons but basically because ViewModelNumber2 can be opened from many different places and works slightly different and managing the different messages that would need to be sent back and where to subscribe to these messages would be a mess
Is there any way that I can do something like the below?
public override void ViewDidLoad ()
{
var set = this.CreateBindingSet<MyView1, MyView1ViewModel>();
set.Bind(myButton).To(vm => vm.MyButtonCommand).OnMyResult((myString) => {Process(myString)});
set.Apply();
}
Or perhaps when I create ViewModelNumber2 I should pass a callBack into the constructor and use that to send the string back from ViewModelNumber2 to MyView1ViewModel
ShowViewModel<ViewModelNumber2>(OnMyResult);
What is the best way to do this?
In short: I don't know what "the best way to do this" is.
The area of ChildViewModel-ParentViewModel messages is complicated - especially because on platforms like Android using Activities and WindowsPhone using Pages you have no guarantee that the ParentViewModel will be in memory when the Child is shown. (Note: this isn't a problem on iOS as its "app suspension" model is simpler)
When I do need one ViewModel returning data to another, then:
Often I try to implement the data collection views as "popup dialogs" rather than as "whole pages" - this makes the parent-child ViewModel relationship more correct - and ensures the parent ViewModel will be in memory when the child closes.
Often I recommend people use a Messenger-based technique like Greg describes in: http://www.gregshackles.com/2012/11/returning-results-from-view-models-in-mvvmcross/
often I've done this messaging via background services rather than via ViewModel-ViewModel messaging (a bit like the way screens are updated in https://github.com/MvvmCross/NPlus1DaysOfMvvmCross/tree/master/N-17-CollectABull-Part6)
Another solution I've used is to:
implement a IDropBoxService singleton - with an API like void Deposit(key, value) and bool TryCollect(key, out value)
allow the closing "child" ViewModels to leave "values" when they close
implement IVisible functionality in my "parent" ViewModel - like in https://github.com/MvvmCross/NPlus1DaysOfMvvmCross/blob/master/N-42-Lifecycles/Lifecycle.Core/ViewModels/FirstViewModel.cs#L10
use the IVisible method to check for messages
To implement anything perfectly, you really should add serialisation code to make sure this all works during "tombstoning" on all platforms... but often this is overkill - for a simple data collection dialog users often don't need "perfect" tombstoning support.

Opening new window from ViewModel

How should I be opening new windows? I'm currently doing the following.
EventArgs:
public class GenericViewRequestedEventArgs : EventArgs
{
public GenericViewModel ViewModel { get; private set; }
public GenericViewRequestedEventArgs(GenericViewModel viewModel)
{
ViewModel = viewModel;
}
}
ViewModel:
public class MainWindowViewModel : ViewModelBase
{
private RelayCommand _viewSpecificCommand;
public ICommand ViewSpecificCommand
{
get
{
if (_viewSpecificCommand == null)
_viewSpecificCommand = new RelayCommand(x => viewSpecific());
return _viewSpecificCommand;
}
}
public EventHandler<GenericViewRequestedEventArgs> GenericViewRequested;
private void RaiseGenericViewRequested(GenericViewModel viewModel)
{
var handler = GenericViewRequested;
if (handler != null)
handler(this, new GenericViewRequestedEventArgs(viewModel));
}
private void viewSpecific()
{
RaiseGenericViewRequested(_specificViewModel);
}
}
View:
public partial class MainWindow : Window
{
private void OnGenericViewRequested(object sender, GenericViewRequestedEventArgs e)
{
GenericWindow window = new GenericWindow(e.ViewModel);
window.ShowDialog();
}
}
This does work, but it seems like a lot of code and I end up with code behind in my view any ways.
What's the logic behind sending the command to the viewmodel at all?
Is it just to optionally use the predicate(if so why not bind to Enabled) and possibly avoid exposing additional viewmodels as properties?
Should I be attaching simple event handlers in the XAML(e.g. Click="btnViewSpecific_Click")?
It depends on how "strict" you want to follow the MVVM pattern. This is just one of the basic pitfalls of MVVM and you can solve it depending on your preferences. One way is to simply use the code-behind, but then how will you handle application-wide commands, keyboard shortcuts, etc? It is a bit too short-sighted IMHO.
I think you should at least consider using existing frameworks that have solved these issues for you years ago and will provide you with a solid base for your application.
For example, Catel has a IUIVisualizerService that can show windows based on a view model. The major advantage is that you can push data into the view model and respond to the result as a service. Another nice advantage is that you can mock the IUIVisualizerService so you can test the reacting code on different results provided by the dialog.
** Disclaimer **
I am the developer of Catel, so I have explained the Catel way here. If anyone else wants to comment on other frameworks, feel free to comment or create a new answer.
Yes, there are a lot of additional codes for MVVM. Building a command that independent of Views is usually for unit testing, such that the command and ViewModel can be unit tested without involving UI components.
However, if the "command" is just opening a window, it is not worth to create a command, and unit test the command to see if the GenericViewRequested is really fired(you can even check if the correct _specificViewModel is returned). The codes are far more complicated and just little value is added. Just open the window in View's button click event handler and it is fine.
If you want to see good example, see how this works in the ViewModel (EmailClient) sample application of the WPF Application Framework (WAF).

How should I communicate between ViewModels?

I am using MVVM Light and have used the packaged messenger system to communicate between view models, however I have hit a bit of a dilemma! Basically when a user clicks on a customer record the corresponding view is opened and with it the CustomerViewModel is instantiated. At this point the CustomerViewModel requires the selected customers ID from the previous view model (ViewAllCustomersViewModel) so that it can get selected customers info which the view binds to (still following?). So initially my thought was too send that ID in a message from the ViewAllCustomersViewModel (where the customer to be viewed is selected) to the CustomerViewModel... HOWEVER, the CustomerViewModel is not instantiated to be able to receive the message until the view is loaded (at which point the message has already been broadcast)!
So, what would be the best way to solve this issue? So far I have considered the CustomerViewModel sending a request to the ViewAllCustomersViewModel once it has been instantiated (basically saying "I am ready to receive the message"), and then the ViewAllCustomersViewModel sending the ID back to the CustomerViewModel... but is this a necessary approach to solve this? It seems a bit ugly to me!
Otherwise, I was thinking is there another way to communicate which can account for the issue I am having? But then isn't this the whole point of the messaging system... to be able to communicate between view models? OR can I force the view model to be instantiated on start up? If so, how would that affect the ViewModelLocator?
I hope I have outlined the issue clearly, I have used fictional view model names for the purpose of the explanation... and please feel free to edit or suggest any additional information that you would like me to add!
Did you try to communicate via your model? I was not able to read your topic until the end but this is how I communicate between ViewModels.
Both View Models have the instance of session.
public ViewModel1(ISession session)
{
_session = session;
}
public ViewModel2(ISession session)
{
_session = session;
}
This way, when you test your application in BDD (behavior driven development), you can test your application without the view. The glue is the model.
As you can see on this picture, you should be able to test your application without the view.
I came across the same situation where two view model is communicating each other. I have used Microsoft PRISM framework to publish and Subscribe.
In your case CustomerViewModel is parent View and ViewAllCustomersViewModel is child view.
Download prism framework "Microsoft.Practices.Prism.PubSubEvents.dll" from https://www.nuget.org/packages/Prism.PubSubEvents/
Add prism reference to your project "Microsoft.Practices.Prism.PubSubEvents.dll"
Create some custom class which is used for communication modem.
class Notifications : PubSubEvent<string>
{
}
Create IEventAggregator eventAggregator singleton instance for your project and initialize it.
public sealed class SessionInfo
{
public IEventAggregator eventHanlder;
private SessionInfo (){
}
private static SessionInfo _instance = null;
public static SessionInfo Instance{
get{
lock (lockObj){
if (_instance == null) {
_instance = new SessionInfo ();
_instance.eventHanlder= new EventAggregator();
}
}
return _instance;
}
}
}
Go to Popover model (ViewAllCustomersViwModel) button events handling and below codes in it.Now it has been published.
In ViewAllCustomersViwModel.cs:
public void OnSelectedItem(Item item)
{
SessionInfo.Instance.eventHanlder.GetEvent<Notification>().Publish(item.id);
}
These event aggregator has to be subscribe where it is needed. So add below code on your Parent View model (CustomerViewModel)
CustomerViewModel.cs
public class CustomerViewModel
{
public CustomerViewModel()
{
SessionInfo.Instance.eventHanlder.GetEvent<Notifications>().Subscribe(OnReceivedNotification);
}
//Handling the notification
public void OnReceivedNotification(string itemId)
{
Debug.WriteLine("Item Id is :" + itemId);
}
}
For more information:
https://sites.google.com/site/greateindiaclub/mobil-apps/windows8/communicationbetweenviewmodelsinwindows8mvvmpattern
I believe that standard way is to pass it through View.
Depending on how you instantiate your views, it could be DependencyProperty to bind in XAML, constructor parameter, or anything else.
Then View passes it to it's ViewModel (pushes it to VM, not the way around: ViewModel should not be aware of View). This way you get a standalone closed component (your View), and external code does not know about it's internal implementation (which is ViewModel).
In XAML it can be something like
<ListBox x:Name="customers" />
<CustomerView Customer="{Binding SelectedItem, ElementName=customers}" />
And then in CustomerPropertyChanged handler you push value to the ViewModel.
Personally, I used to use the MVVM-Light Messenger, but found I had way to many messages flying around, and I didn't like the feeling of using a "magical" messenger. What I did is outlined as the answer to the following link
Best Way to Pass Data to new ViewModel when it is initiated.
Now I warn you, I answered my own question, and nobody verfied it as good or bad practice, however it works for my situation and has elimnated the need for MVVM-Light Messenger. Because my program uses multiple threads in my implementation I changed all the entries in the repository to Dictionarys with the CurrentThread.ManagedThreadId as the Key.
So far I have considered the CustomerViewModel sending a request to
the ViewAllCustomersViewModel once it has been instantiated (basically
saying "I am ready to receive the message"), and then the
ViewAllCustomersViewModel sending the ID back to the
CustomerViewModel...
I would continue with this idea. It keeps the Views, ViewModels and Models all separate and unknowing of the others unlike other answers. Not to say other answers are wrong, or even bad, your option can be defined one or any of: personal preference, team convention, long-term MVVM goal of replacing components/modules, and complexity/ease of coding.
A side-effect to your idea I quoted above, which I prefer, is that you can request at any time as you've already set it up. So if you change when to perform that request very easily, or if you need to request updates, you use the same communication architecture in place.
Lastly, I prefer it because if you change your models or views or viewmodels - you keep the same core concept of communicating information between components.

MVVM Solution structure

I'd like to understand the best practice for structuring an MVVM solution.
Currently I've created a separate project for my View and my ViewModel. My View project references my ViewModel project. So far so good. When it comes to implementing navigation my ViewModel classes need access to the RootFrame in order to navigate around, but the RootFrame resides in App.xaml which is in the View project. So I have a circular dependency issue here.
Is there a recommended structure I should be using? I could just lump this all into one big project but in order to decouple the View and ViewModel I feel like having separate projects is a better approach.
When it comes to implementing navigation my ViewModel classes need access to the RootFrame
This is a false assumption.
You could use a message broker (a single object) that is responsible for distributing messages between publishers (ViewModels) and subscribers (some object that is responsible for opening Views).
Most MVVM frameworks have such a broker.
About the dependencies
The single responsibility of the Broker is to raise events. So, in general, it exposes a couple of methods that can be called by publishers and a couple of events that can be registered to by subscribers.
In MVVM you could use this mechanism to have a ViewModel raise the event that signals that a View should be opened and a View Manager that subscribes to this event. The View Manager should be able to instantiate a View and attach the correct ViewModel.
To prevent the ViewManager from needing references to all Views and ViewModels you could pass to the event logical names (just a string) and have the View Manager look up the matching View(Model) type by using reflection or a static list in a configuration file.
This way you do NOT need any circular references references. In fact, when you find you need references that go against the proper dependencies in MVVM, you should first doubt the setup and after that consider using a base class for the View and/or ViewModels.
There is no best practice for MVVM since it is a design pattern that everybody implements differently according to their preference. I've seen quite a few different implementations but have never seen a views and view models in separate projects. I would recommend just keeping them in different folders in the same project and put them in different namespaces.
e.g. Your View Models can go in a ViewModels folder and be in the namespace MyProject.ViewModels
Your Views can go in a Views folder and be in the namespace MyProject.Views
And the same for Models if you are using them
Check out Rachel's good answer in this post. I also like to separate my Views and ViewModels, because then I know when I screwed up MVVM ground rules.
Your ViewModel should not have any references to the View, but the View must have a reference to the ViewModel. Consider, for example, my custom SplashScreen factory (the two important lines are "var viewModel..." and "var splashScreen..."):
namespace MyCompany.Factories
{
using System.Threading;
using MyCompany.Views;
using MyCompany.ViewModels;
public static class SplashScreenFactory
{
public static SplashScreenViewModel CreateSplashScreen(
string header, string title, string initialLoadingMessage, int minimumMessageDuration)
{
var viewModel = new SplashScreenViewModel(initialLoadingMessage, minimumMessageDuration)
{
Header = header,
Title = title
};
Thread thread = new Thread(() =>
{
var splashScreen = new SplashScreenView(viewModel);
splashScreen.Topmost = true;
splashScreen.Show();
splashScreen.Closed += (x, y) => splashScreen.Dispatcher.InvokeShutdown();
System.Windows.Threading.Dispatcher.Run();
});
thread.SetApartmentState(ApartmentState.STA);
thread.IsBackground = true;
thread.Start();
return viewModel;
}
}
}
The Factories project has references to both MyCompany.ViewModels and MyCompany.Views. The Views project only has a reference to MyCompany.ViewModels.
We first initiate the ViewModel from our caller (in this case, from SplashScreenFactory; or maybe from App.xaml.cs if you want; I prefer using my own Bootstrapper class for reasons beyond this discussion).
Then we initiate the View by passing the ViewModel reference into the View's constructor. This is called Dependency Injection. You may also need to write a Behaviour for the Window class in order to Close the window from your ViewModel. So now I can do the following from my Bootstrapper class:
/// <summary>
/// Called from this project's App.xaml.cs file, or from the Deals Main Menu
/// </summary>
public class Bootstrapper
{
private SplashScreenViewModel _splashScreenVM;
public Bootstrapper()
{
// Display SplashScreen
_splashScreenVM = SplashScreenFactory.CreateSplashScreen(
"MyCompany Deals", "Planning Grid", "Creating Repositories...", 200);
// Overwrite GlobalInfo parameters and prepare an AuditLog to catch and log errors
ApplicationFactory.AuditedDisplay(
Assembly.GetExecutingAssembly().GetName(),
() =>
{
// Show overwritten version numbers from GlobalInfo on SplashScreen
_splashScreenVM.VersionString = string.Format("v{0}.{1}.{2}",
GlobalInfo.Version_Major, GlobalInfo.Version_Minor, GlobalInfo.Version_Build);
// Initiate ViewModel with new repositories
var viewModel = new PlanningGridViewModel(new MyCompany.Repositories.PlanningGridHeadersRepository(),
new MyCompany.Repositories.PlanningGridLinesRepository(),
_splashScreenVM);
// Initiate View with ViewModel as the DataContext
var view = new PlanningGridView(viewModel);
// Subscribe to View's Activated event
view.Activated += new EventHandler(Window_Activated);
// Display View
view.ShowDialog();
});
}
/// <summary>
/// Closes SplashScreen when the Window's Activated event is raised.
/// </summary>
/// <param name="sender">The Window that has activated.</param>
/// <param name="e">Arguments for the Activated event.</param>
private void Window_Activated(object sender, EventArgs e)
{
_splashScreenVM.ClosingView = true;
}
Note that I have control over the SplashScreen's View by subscribing to the View's Activated event, and then closing the view with the "ClosingView" boolean INotifyProperty that sets the View's "Close" DependencyProperty in turn (sounds complicated, but once you get to know DependencyProperties, it becomes simple).
The point is, don't try to drive your navigation from RootFrame. Just view.Show() the RootFrame, and control the rest from RootFrameViewModel.
Hope this helps :-)

Caliburn ShowDialog and MessageBox

I'm making a small demo application for MVVM with caliburn.
Now I want to show a MessageBox, but the MVVM way.
For dialogs I created an event, that is handled in the ShellView (the root view)
and just calls WindowManager.ShowDialog with a Dialogs ViewModel type.
Seems to stick to MVVM for me.
But what is the way to show a messagebox and get its result (Okay or cancel)?
I already saw this question, but it contains no answer either.
Mr Eisenberg hisself answers with
"Caliburn has services built-in for calling custom message boxes."
Can anyone tell what he means with that? I don't see it in the samples.
As you mentioned, you just prepare the view model (e.g. ConfirmationBoxViewModel) and an appropriate view. You'll have to create two actions (after inheriting the view model from Screen, which is necessary to use TryClose. You can always implement IScreen instead, but that would be more work):
public void OK()
{
TryClose(true);
}
public void Cancel()
{
TryClose(false);
}
and then in your other view model:
var box = new ConfirmationBoxViewModel()
var result = WindowManager.ShowDialog(box);
if(result == true)
{
// OK was clicked
}
Notice that after the dialog closes, you can access the view model properties if you need to pull additional data from the dialog (e.g. Selected item, display name etc).
In the article A Billy Hollis Hybrid Shell (written by the framework coordinator) the author showed a nice way to handle both dialog and message boxes, but he used dependency injection (you can go without DI of course but it makes things simpler). The main idea is that you can let your main window, the one used as the application shell implement an interface that looks something like this:
public interface IDialogManager
{
void ShowDialog(IScreen dialogModel);
void ShowMessageBox(string message, string title = null, MessageBoxOptions options = MessageBoxOptions.Ok, Action<IMessageBox> callback = null);
}
and then he registers this interface with the IoC container, I guess you can use your imagination from there on and if you don't have time then you can look at the source code that accompanies the article.
When the root/main/shell view-model implements a kind of DialogService interface, every other view-model needing to show dialogs will end up with a dependency on the root view-model. Sometimes this might not be desiderable, e.g. if it could cause a dependency loop:
DialogService (aka RootViewModel) -> SomeViewModel -> RootViewModel.
A more involved approach to break this dependency chain (and actually invert it) is the following:
Implement a behavior that detects Window.OnSourceInitialized event and attach it to main view Window component. That is the event fired when the window handle is available. Upon event, behavior will notify some handler passed in via attached property:
<my:WindowSourceBehavior InitListener="{Binding WindowListener}" />
public class WindowSourceBehavior : Behavior<Window>
{
// ...
// boilerplate code for IWindowListener InitListener dependency property
// ...
attachedWindow.SourceInitialized += (sender, evt) =>
{
// ...
InitListener.SourceInitialized(sender as Window);
}
}
DialogService exposes a handler - or interface - as requested by behavior:
public class DialogService : IWindowListener
{
// ...
public void SourceInitialized(Window rootWindow) { /* ... */ }
}
In root view-model, (indirectly) get the DialogService injected as a dependency. During construction, sets view-model bound property, WindowListener, to the DialogService handler/interface:
public MainViewModel(IWindowListener dialogServiceInDisguise)
{
WindowListener = dialogServiceInDisguise;
}
public IWindowListener WindowListener { get; private set; }
Doing so, the DialogService is able to get a hold of root Window, and whichever view-model needs to show a dialog does not create a(n indirect) dependency on main view-model.

Categories

Resources