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
}
}
Related
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);
I just can't figure this out. So I will try describe my problem best I can.
I am building application using MVVM pattern. I have user control AAAView with viewmodel AAAViewModel that is used to fill data class CDataClass. I also have main window MainView and its viewmodel MainViewModel. Next I have window DialogView with DialogViewModel.
So now MainViewModel (that has its own user control) creates DialogViewModel (with another instance of user control). How can I transfer data in CDataClass between these two user controls? I tried to create property in AAAViewModel that would hold instance of MainViewModel or DialogViewModel so I can pass data to it but I get stuck because I couldn't make it as dependency property.
My goal is to make user control that can be used in different views which can have different data in underlaying CDataClass.
Just to clarify... I am using user control as <views:GeneralInfoView Grid.Row="0" /> and don't know how to share data between two different instances of the same user control in different views. Any point to some pattern or method would be much appreciate.
Thank you for help.
I don't think it's ideal that you've got your application architecture diagrammed as relationships among views; I think a better way to think about it is as a set of relationships among viewmodels, with the views hanging off that tree as needed. When you think about it that way, "how does data get passed" gets a lot simpler. A view is just a conduit between a viewmodel and the user. You don't design a house as a set of windows and telephones and then try to figure out the floor plan from that. You start with what the house does and how people will live in it.
So this is easy:
Some viewmodels have an AAViewModel property. There may be all kinds of simple or complicated views on those viewmodels; if a view wants to let the user edit the viewmodel's AAViewModel stuff, then it includes an AAView bound appropriately to the viewmodel's AAViewModel. Your MainViewModel and DialogViewModel are both big complicated interactive views that want to let somebody edit their vm's AAViewModel stuff.
If MainViewModel is DialogViewModel's parent, or created a temporary instance of DialogViewModel just to put in a modal dialog, then MainViewModel would show the dialog, and have a look at dialogVM.AAVM.CData.IsDirty to decide what to do with it. Or maybe it gives dialogVM.AAVM a new CDataClass instance before showing the dialog (maybe a clone of its own instance), and if ShowModel() returns true, then it does something with dialogVM.AAVM.CData.
The point is that once your viewmodels are driving everything, it becomes relatively simple for them to communicate with each other. Parent-child is easy: Parent gives stuff to the child and looks at what the child brings back. A viewmodel can subscribe to another viewmodel's PropertyChanged event; a parent viewmodel can monitor its children; when something happens on a child, the parent can decide whether to update a sibling. In general, children should not know anything at all about their parents; this makes it much easier to reuse child viewmodels in disparate contexts. It's up to parents to decide what to do with that information.
All AAViewModel knows is that somebody handed him a copy of CDataClass; he updates his public properties accordingly. Then somebody else (probably AAView, but he doesn't know) hands him some changes by setting his properties; he updates his CDataClass instance accordingly. After a while, unknown to him, one viewmodel or another comes and looks at that CDataClass.
And communication between views and viewmodels happens via bindings.
UPDATE
It turns out that you're creating viewmodels in your views, and as a result you have no idea how the parent can get to them. And now you know why it's not a good idea to create child view viewmodels that way.
Here's how you do child view/viewmodels in the viewmodel-centric design I described above:
First, get rid of whatever you're doing to create the child viewmodels inside the view.
Second, create a DataTemplate for the child viewmodel type. This should go in a resource dictionary which is merged into the resources in App.xaml, but it's so simple that it won't kill you if you get lazy and just paste it into the Resources of the two different views where it's used.
I don't know what your namespace declarations look like. I'm going to assume that views are in something called xmlns:view="...", and viewmodels are in something called xmlns:vm="...".
<DataTemplate DataType="{x:Type vm:AAAViewModel}">
<view:AAAView />
</DataTemplate>
Now, you can assign an AAAViewModel to the ContentProperty of any control that inherits from ContentControl (and that's most of them), and the template will be instantiated. That means that XAML will create an AAAView for you and assign that instance of AAAViewModel to the DataContext property of the AAAView that it just created.
So let's create a child AAAViewModel next, and then we'll show it in the UI.
public class DialogViewModel
{
// You can create this in DialogViewModel's constructor if you need to
// give it parameters that won't be known until then.
private AAAViewModel _aaavm = new AAAViewModel();
public AAAViewModel AAAVM
{
get { return _aaavm; }
protected set {
_aaavm = value;
OnPropertyChanged(nameof(AAAVM));
}
}
And now we can display AAAVM in DialogView:
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<ContentControl
Content="{Binding AAAVM}"
Grid.Row="0"
/>
<StackPanel Orientation="Vertical" Grid.Row="1">
<!-- Other stuff -->
</StackPanel>
</Grid>
Now how does MainViewModel get in touch with a DialogViewModel? In the case of dialogs, since they have a finite lifespan, it's not actually a big deal to let them create their own viewmodels. You can do it either way. I generally lean towards having it create its own as in the second example below.
Not quite the same, but close. First, once again, get rid of whatever you're doing where the dialog creates its own viewmodel.
MainViewModel.cs
public CDataClass CDC { /* you know the drill */ }
public void ShowDialog()
{
var dvm = new DialogViewModel();
// Maybe this isn't what you want; I don't know what CDataClass does.
// But I'm assuming it has a copy constructor.
dvm.AAAVM.CDC = new CDataClass(this.CDC);
if (DialogView.ShowDialog(dvm).GetValueOrDefault())
{
CDC = dvm.CDC;
}
}
Note that this next one is view codebehind, not viewmodel.
DialogView.xaml.cs
public static bool? ShowDialog(DialogViewModel dvm)
{
var vw = new DialogView() { DataContext = dvm };
return vw.ShowDialog();
}
Now, you could let the dialog continue creating its own viewmodel; in that case you would give it a public property like this:
public DialogViewModel ViewModel => (DialogViewModel)DataContext;
And a ShowDialog method like this:
DialogView.xaml.cs
public static bool? ShowDialog(CDataClass cdc)
{
var dlg = new DialogView();
dlg.ViewModel.AAAVVM.CDC = cdc;
return dlg.ShowDialog();
}
And then the parent could interact with it like this instead:
MainViewModel.cs
public void ShowDialog()
{
var cdcClone = new CDataClass(this.CDC);
if (DialogView.ShowDialog(cdcClone).GetValueOrDefault())
{
CDC = cdcClone;
}
}
Nice and tidy.
If that dialog isn't modal, make the dialog viewmodel a private member of MainViewModel, and have MainViewModel subscribe to events on the dialog viewmodel to keep abreast of what the dialog is doing. Whenever the user updates the dialog's copy of CDataClass, the dialog would raise DataClassUpdated, and MainViewModel would have a handler for that event that sniffs at _dialogViewModel.AAAVM.CDC, and decides what to do with it. We can get into example code for that if necessary.
So now you can see what I mean by building everything in terms of parent/child viewmodels, and stuffing them into views when and as appropriate.
How to open a new View(Dialog) from a view model command and also set the new view's data context with its view model. this question is bothering me a lot, although there has been so many question on this but I could not get satisfied with any of the answer so far.
So, suppose:
I have a start up dialog called MainView and I show this dialog and set its data context in App.xaml.cs (OnStartUp) method. In MainView, there is a button called "open a new dialog" and this button's command is bind with a delegate command in MainViewModel. So, when user hits this button, then command calls the execute method.
Let's say command in MainViewModel which is bind with button in view is as following
public ICommand ShowNewDialogCommand
{
if(this._showNewDialogCommand == null)
{
this._showNewDialgoCommand = new DelegateCommand(ShowDialogFromVM);
}
}
private void ShowDialogFromVM()
{
}
And let's say the new dialog that I want to show is ListAllStudentsView and its ViewModel is StudentsViewModel. So, what are the various approaches of showing this dialog without breaching the MVVM pattern? And what are the merits and demerits of each approach?
First, we need to create a view (somewhere) with its datacontext set. Easy enough, we instantiate the view and either pass it the view model (assuming the view sets its data context in its constructor) or set it manually. The view could also have declared the view model in XAML if we so desired.
Method 1:
Window dialog = new ListAllStudentsView(new StudentsViewModel());
Method 2:
Window dialog = new ListAllStudentsView();
dialog.DataContext = new StudentsViewModel();
Method 3:
<Window.DataContext>
<local:StudentsViewModel/>
</Window.DataContext>
Now we need to put this code (and the associated dialog.ShowDialog() somewhere). I see two options, right in the command's execute function, or in the view's code-behind (triggered by an event raised by the command's execute function like "RequestDialog").
I prefer the first, even though it doesn't adhere as rigidly to MVVM because it is a lot simpler, less code, and easier to manage. If you want to be very strict about adhering to MVVM however, I would have the ViewModel raise an event like "RequestDialog" in the command function that the view listens to and runs the constructor and ShowDialog() function.
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">
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.