How correctly to call Dialog from ViewModel? - c#

I think it is mostly architecture question. I have a View and my ViewModel class set as DataContext. Now, I have a click event binded to View by ICommand.
So what we have, user click button in View I get this event in ViewModel and I would like to open Dialog.
As for me Dialog it is associated with a View and View should open this dialog, so it is means that this method
public void OpenDialog(){...}
should reside in View and I should someway to call this method from my ViewModel where I got click event.
Questions is:
If I understand this MVVM WPF approach in a right way
How to make this connection (best practices)

You can create a property on the view model with following signature:
public Func<string, string, bool> ReportMessage { get; set; }
Then when constructing your view model you can pass it an implementation:
var OKCancelMessage = new Func<string, string, bool>((m, c) => MessageBox.Show(m, c) == MessageBoxResult.OK ? true : false);
new ViewModel()
{
ReportMessage = OKCancelMessage
}
This way your view model will not know about the implementation of the message and separation of view from model has been achieved.
EDIT
Calling the function is simple:
if(ReportMessage("Do you really want to delete this record?", "Question"))
Delete(record);

Related

How to instantiate a dialog and record user input while MVVM pattern is used to create dialog in WPF

I am a C++ developer and new to WPF and MVVM. please bear with me if I choose any wrong word to ask my question
I have my Main application in MFC/C++ which is passing some data to C# library(CLI is used as middle layer).
In C# library, there is a section of code where a dialog is opened , data is filled and user selection is notified to the calling object in below way -
public classA()
{
MyDialog dlg = new MyDialog(param1, param2, param3)
if(dlg.ShowDialog().GetValueOrDefault())
{
var name = dlg.name;
var roll = dlg.roll;
}
else
{
var name = string.Empty;
var roll = string.Empty;
}
}
Now Dialog has been modified and implemented using MVVM pattern.
I have created below files as part of implementation-
1
MyDialogView.Xaml
MyDialogView.xaml.cs
MyDialogViewModel.cs
MyDialogModel.cs
My question is, how to instantiate the new dialog now from my classA so that data is filled using the parameters passed to dialog in same way as previously it was doing and record user selection without loosing any data and safely closing the view.
Standard MVVM approach works like this (at least when using MVVM Light):
You have a VM layer, a Class Library.
You have a View layer, a WPF Controls Library or WPF Application.
View layer adds reference to VM layer. VM layer doesn't know anything about View.
You create a normal public class for your dialog's VM. Call it DialogVM or whatever. Make sure this class inherits from MVVM Light's built-in ViewModelBase. This will get you access to change notification methods provided by MVVM Light. Might look like this in your case:
public partial class DialogVM : ViewModelBase
{
private string _Name;
public string Name
{
get { return _Name; }
set { Set(ref _Name, value); }
}
private string _Roll;
public string Roll
{
get { return _Roll; }
set { Set(ref _Roll, value); }
}
}
VM layer has a global static class called ViewModelLocator. This class performs IoC/DI and provides public static properties to expose different VMs. (In your case your dialog's VM goes to the VM project and the ViewModelLocator looks something like this:
using System;
namespace VMLayer
{
public class ViewModelLocator
{
static ViewModelLocator()
{
SimpleIoc.Default.Register<DialogVM>(true);
}
public static DialogVM MyDialog => SimpleIoc.Default.GetInstance<DialogVM>();
}
}
Your dialog box (a Window) goes to View layer and uses this exposed property MyDialog to provide a DataContext for the dialog:
<Window x:Class="GlasshouseTestingShell.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:vm="clr-namespace:VMLayer;assembly=VMLayer"
DataContext="{x:Static vm:ViewModelLocator.MyDialog}"
d:DataContext="{d:DesignInstance Type=vm:DialogVM}">
</Window>
Look how cleanly we have created View layer's DataContext without writing a line of C# code in the View layer. This is also elegant in the sense that you get all design-time Intellisense in Binding expressions and elsewhere.
You now bind all your UI stuff (textboxes, buttons etc.) to the public properties and commands exposed by your dialog's VM. Still no lines in the code-behind. Might look like this in your case:
<TextBox Text="{Binding Name}" />
Rest of the stuff is in C++:
You add reference to your View and VM DLLs in your C++ project.
Create an object of your dialog. It will automatically instantiate its VM and perform binding. You call ShowDialog() to bring it to screen.
Use takes actions in the dialog and finally presses OK or Cancel.
You capture dialog result and then access your dialog object's DataContext property, which is an object of DialogVM class. You can access user-supplied values from therein as Binding has updated those properties for you in the VM.
I'm not sure I follow all of your requirements but this is roughly how I'd approach such a task:
Instantiate the view and viewmodel in class A.
Set whatever parameters you want on your viewmodel. Either as properties or via constructor injection.
Set the datacontext of the view to the viewmodel.
Everything you need to bind should then bind between them.
showdialog the view.
The user edits in the view and changes persist to the viewmodel properties.
They finish editing and you then work with the viewmodel properties. Maybe one of them is the model you mention. Maybe the model is instantiated by the viewmodel to get data or by classA if that is more convenient. In the latter case you probably have to pass that model to the viewmodel.
Bearing in mind the above.
Some rough code:
public class ClassA
{
MyDialogViewModel vm = new MyDialogViewModel { Name = "X", Roll = "Y" };
MyDialog dlg = new MyDialog();
dlg.ShowDialog();
var name = vm.Name;
var roll = vm.roll;
// Do something to persist your data as necessary. Either here or in a model within the viewmodel
}
Name and Roll presumably bind to some textboxes Text properties in the view or some such.
If it's as simple as obtaining two string values then I see no advantage to actually having a model at all. On the other hand, if processing is more involved then of course the viewmodel might instantiate a model.
MyDialogViewModel should implement inotifypropertychanged and anything you need to bind should be a public property. Not sure if you'll need propertychanged notification but always implement it. Optionally raise propertychanged from property setters.

wpf mvvmlight passing data to viewmodel

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).

Parent and Child View communication under MVVM on WPF

I have been strugglin whit this problem for quite some time now. I'm building my first WPF MVVM application. In this App i have a AppView (with it's corresponding viewmodel). Child views are contained into tabs and represented by separated views (UserControl) and have one viewmodel for each view. So far so good.
In one view, a have a list of costumers, and a Delete button. I also have a correspondig command on the viewmodel to actualy delete the record, and this work fine. Now I want the delete button to create a new view with two buttons, one for confirmation and the other for cancel, and then if user click the "Confirm" button execute the delete.
The problem here is that each view, and its correspondig viewmodel are isolated from the other (as long as I understand) so i cannot access the second view viewmodel to see if the confirm button is clicked.
The only posible solution that i found so far is to add an event on one view and subscribe the other view to that event. But this technic is quite complex for such a trivial task. Is there other alternatives? Can't the two views share the same datacontext or viewmodel?
Thanks!
var dialog = new DialogViewModel();// could be a DialogService if you wish
with in this DialogViewModel or DialogService again your choice how you actually do it.
dialog.Result which in this case would return your confirmation either true or false
var settings = new Dictionary<string, object>();
settings["Owner"] = this;
settings["WindowStartupLocation"] = WindowStartupLocation.CenterParent;
windowManager.ShowDialog(dialog, null, settings);
if(dialog.Result == true)
do the delete on the parent viewmodel.
Or you can do it all with IEventAggregator and a message package. I personally use the first for a lot of things. Sometimes a combination depending on situation.
Most will favor the IDialogService method of things for SoC, and do DI with it to bring it into the viewmodel using it. Then each viewmodel will be responsible its own dialogs. From there you can call ShowDialog since its part of the WindowManager, which you click Yes or No, or what ever you setup for you dialogview. Numerous ways to skin the cat but in the end you want KISS methodology and something that won't break the patterns you are trying to adhere too.. Hell for all it matters you could add it to a viewmodelbase base class for all of your viewmodels to inherit to access globally. All a function how you want your app to behave in the end anyway.
--update--
public class YourViewModel(IWindowManager winMan)
{
private readonly IWindowManager _winMan;
public YourViewModel()
{
_winMan = winMan;
}
public void DeleteCustomer()
{
var dialog= new DialogViewModel(); // not best way but...
var settings = new Dictionary<string, object>();
settings["Owner"] = this; //<< Parent
settings["StartupLocation"] = WindowStartupLocation.CenterParent;
_winMan.ShowDialog(dialog, null, settings);
if(dialog.Result)
//do delete
else
//do nothing
}
}

Right Way to access a View Model from an existing View Model

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.

WPF MVVM Get Parent from VIEW MODEL

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.

Categories