How to have multiple Views using the same ViewModel in MVVM? - c#

I am new to both WPF and MVVM and a came across an issue when attempting to set the DataContext to the same instance of my ViewModel in two separate views.
This was because:
<Window.DataContext>
<local:ViewModel/>
</Window.DataContext>
would create a new instance of the view model for each view.
To get around this I decided to create a class that stored static instances of each ViewModel I used. Then in the cs file of each view I would then set the DataContext to the appropriate ViewModel from this static class.
This works but doesn't seem the best idea for larger programs where the multiple instances of the ViewModel may be needed simultaneously.
What are better approaches to this problem - Are there sound ways to have multiple Views using the same instance of a ViewModel?
Or is this approach bad practice - Should I be designing a program with one View for every ViewModel?

You can instantiate that view model in App.xaml so that it is accessible to the whole application.
<Application.Resources>
<local:ViewModel x:Key="sharedViewModel" />
</Application.Resources>
Then in your views when you want to use that datacontext, you do the following...
DataContext="{StaticResource sharedViewModel}"

I had this same question and I couldn't find a good answer. After thinking about it for a while I came to the conclusion that in most cases it's best to create a one to one mapping between view model and view. So in this situation I would create two separate view models that inherit from a base view model. That way you can put whatever is common in the base view model and add any fields or methods that might be different to the more specific view model. If the view models truly are equivalent then you might want to ask yourself why you have two separate views in the first place. You may consider merging them into one view. It's possible that having two separate views is what you want, but it's just something to consider.

Simple and easy as well as one of the recommended approach is implementing ViewModelLocator.
Idea is having defined all the ViewModels in ViewModelLocator class and access the ViewModel wherever needed. Using Same ViewModel in different View will not be a problem here.
public class ViewModelLocator
{
private MainWindowViewModel mainWindowViewModel;
public MainWindowViewModel MainWindowViewModel
{
get
{
if (mainWindowViewModel == null)
mainWindowViewModel = new MainWindowViewModel();
return mainWindowViewModel;
}
}
private DataFactoryViewModel dataFactoryViewModel;
public DataFactoryViewModel DataFactoryViewModel
{
get
{
if (dataFactoryViewModel == null)
dataFactoryViewModel = new DataFactoryViewModel();
return dataFactoryViewModel;
}
}
}
App.xaml
xmlns:core="clr-namespace:MyViewModelLocatorNamespace"
<Application.Resources>
<core:ViewModelLocator x:Key="ViewModelLocator" />
</Application.Resources>
Usage
<Window ...
DataContext="{Binding Path=MainWindowViewModel, Source={StaticResource ViewModelLocator}}">
refer : So Question codes copied from there.. as i cannot rip the codes from my project..

I am using the prism framework and was also looking for a solution of using one viewmodel for many (child) views. In prism there are two possible solutions:
Define the viewmodel as singleton (code smell) or
Using the RegionContext (for full description see the prism doc)
According to the prism doc my solution looks like this.
In the prism bootstrapper:
Container.RegisterTypeForNavigation<MyView>("MyView");
In the viewmodel:
private void DisplayView(string viewName)
{
RegionManager.Regions["ContentRegion"].Context = this; // set viewmodel
RegionManager.RequestNavigate("ContentRegion", viewName);
}
In the code behind of each view:
public MyView()
{
InitializeComponent();
ObservableObject<object> viewRegionContext = RegionContext.GetObservableContext(this);
viewRegionContext.PropertyChanged += this.ViewRegionContext_OnPropertyChangedEvent;
}
private void ViewRegionContext_OnPropertyChangedEvent(object sender, PropertyChangedEventArgs args)
{
if (args.PropertyName == "Value")
{
var context = (ObservableObject<object>)sender;
DataContext = context.Value; // get viewmodel as DataContext
}
}

The staticresource/singleton approach (is this what it's called? I'm bad at programming terminology) by #Fabulous is great because it's simple code that's easy to implement, but it also means that your code can execute even when you didn't explicitly want it to and that can cause some headaches.
In my case, it would execute e.g. when rebuilding (not too bad, but not great), when re-opening the solution (ugh), and when editing parts of app.xaml (which would for some reason softlock VS2019 i.e. very bad). As I understand it, people who Know What They're Doing probably already knew this would happen, but it was a pain in the ass as a novice programmer to figure out what was going on haha.
One way to keep the simplicity benefits without ruining your day with phantom-running code is to implement a simple check on whatever your main code is to see if you're running in designtime or not (basically a simplified version of #WPFUser's link).
Example:
using System.ComponentModel;
using System.Windows;
public MainCode()
{
if (IsInDesignMode() == false)
{
//execute code normally
}
else
{
//do nothing (or maybe display an info message or something)
}
}
private DependencyObject dummy = new DependencyObject();
private bool IsInDesignMode()
{
return DesignerProperties.GetIsInDesignMode(dummy);
}
(plus still use all the stuff noted in Fabulous' answer)
I haven't tested this extensively yet so be aware that it may not be 100% foolproof, but so far for me it's worked great in the ways I've tested it.

I was having the same case, just I was using the both user control one below another in stack panel. so use the common DataContext which I set in StackPanel worked for me for both User controls, as below
<StackPanel>
<StackPanel.DataContext>
<vm:RegisterViewModel/>
</StackPanel.DataContext>
<local:Register />
<local:Users />
</StackPanel>

Related

wpf application mvvm how to structure properly

Im trying to learn MVVM, but I'm having some trouble. Im new to xaml and c#.
What I have so far:
A person class, that define a person object: name, age and other info
A model class people, owns a private linkedlist (list of person), which also contains methods such as get, remove, add and do some calculations
a viewmodel class, doing casting/parsing/transforming between xaml behind code and the model.
A xaml behind code file mainWindow.xaml.cs, that listen to button click and such, and invoke methods from the viewModel class, and do some simple binding such as total.Content = objModelView.getTotal().
I didnt use INotifyPropertyChanged ObservableCollection, still trying to wrap my head around it. While my program does what I want I'm not sure how to structure it better.
Basically I have 2 main questions:
I see examples online where people store/initiate the list of items in viewmodel, shouldn't I keep the list in model instead, that should be where all the data be stored right?
Let's say I'm suppose to display all the items (in the list of the model class) onto a dataGrid. Right now in my program: mainWindow.xaml.cs will detect the button click, then it ask viewModel to store it in model, if no error then xaml behind code will do
people_dataGrid.Items.Add(new person { name = newName, age = newAge, address = newAdd }); Is this bad practice? Dont get how to use ObservableCollection here, can it somehow detect a change in the list of my model class, and then remove and add the rows to the datagrid?
I've been reading for the whole day but I'm struck here, hope I can get some direction
The model stores data, the view display it, the viewmodel is the bridge between the two.
That doesn't mean that the view have a direct access to model data because you don't always need to display all data in model layer. So we have a viewmodel layer that makes only useful information accessible.
The viewmodel is very useful when you want to display the same data multiple times but displayed differently: you don't have to replicate data, you only need to define twice what information you need from those data and how to display them.
What you're doing in your second question is using model in view : This is not MVVM. What you want to do is bind the ItemsSource DP of Datagrid to a list of PersonVM which will fetch information from Person.
You code could be structured like that:
public class Person {
public String Name {get; set;}
public int Age {get; set;}
}
public class PersonVM {
public PersonVM(Person model) {
_model = model;
}
private readonly Person _model;
internal Person Model {get {return _model;}}
public String Name {
get { return _model.Name; }
set { _model.Name = value; }
}
public int Age {
get {return _model.Age;}
set { _model.Name = value; }
}
}
//PersonV.xaml
<StackPanel>
<TextBlock Text="{Binding Name}"/>
<TextBlock Text="{Binding Age}"/>
</StackPanel>
public class People : ObservableCollection<Person> {
}
public class PeopleVM : ObservableCollection<PersonVM> {
public PeopleVM(People model) {
_model = model;
foreach(Person p in _model) {
Add(new PersonVM(p));
}
_model.CollectionChanged += CollectionChangedHandler;
}
private void CollectionChangedHandler(Object sender, NotifyCollectionChangedEventArgs args) {
switch (notifyCollectionChangedEventArgs.Action) {
case NotifyCollectionChangedAction.Add:
foreach(People p in args.NewItems) {
if(!this.Any(pvm => pvm.Model == p)) {
this.Add(new PersonVM(p));
}
}
break;
case NotifyCollectionChangedAction.Remove:
foreach(People p in args.OldItems) {
PersonVM pvm = this.FirstOrDefault(pe => pe.Model == p);
if(pvm != null) this.Remove(pvm);
}
break;
case NotifyCollectionChangedAction.Reset:
Clear();
break;
default:
break;
}
}
private readonly People _model;
}
//PeopleV.xaml
<ItemsControl ItemsSource={Binding}>
<ItemsControl.ItemTemplate>
<DataTemplate DataType="{x:Type PersonVM}">
<PersonV/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
public class AppVM {
public AppVM() {
People p = ServiceLayer.LoadPeople(); //load people
People = new PeopleVM(p);
}
public PeopleVM People {get; set;};
}
//MainWindow.xaml
<Window ...
>
<Window.DataContext>
<local:AppVM/>
</Window.DataContext>
<PeopleV/>
</Window>
Answer to your post can be as long as one wishes to explain, perhaps a whole lengthy blog itself. I will try to just answer 2 of your specific questions here. I am not going to show the code for each sub-answer, you have to take it as home work. :)
I didnt use INotifyPropertyChanged ObservableCollection, still trying
to wrap my head around it. While my program does what I want I'm not
sure how to structure it better.
Why? If you don't use these magic sticks, it's better you write a WinForms app and not a WPF one. Forget everything and dive into these two. You must (no escape) understand and use them in MVVM/WPF. You can even defer reading my further answer for that matter.
I see examples online where people store/initiate the list of items in
viewmodel, shouldn't I keep the list in model instead, that should be
where all the data be stored right?
They are not wrong. Person class in model layer represents a real world entity and is must, however, I would not bother about having People class in model. It's just a collection that could easily be accommodated by ViewModel. I personally would prefer that way always.
Let's say I'm suppose to display all the items (in the list of the
model class) onto a dataGrid. Right now in my program:
mainWindow.xaml.cs will detect the button click, then it ask viewModel
to store it in model, if no error then xaml behind code will do
people_dataGrid.Items.Add(new person { name = newName, age = newAge,
address = newAdd }); Is this bad practice? Dont get how to use
ObservableCollection here, can it somehow detect a change in the list
of my model class, and then remove and add the rows to the datagrid?
That's not MVVM, trust me. At the maximum what you should be required to write in view code behind, is initializing view model and setting it as view's data context.
To handle view events (Button.Click for ex) you should use ICommand implementation that will be bound to Button.Command property in XAML. This way you decouple control's event handler from the code behind.
You need to have a ObservableCollection<Person> in your viewmodel which will be bound the DataGrid in view. So when click a button to add person, button's command object will update this collection and view will be refreshed automatically without having you to add it manually to data grid.
You aren't using MVVM at all. It sounds like you are using MVP, which is a completely different pattern.
Before you continue, you need to understand what MVVM was designed for, because its a highly complicated (seemlying over engineered pattern) with a huge number of abstractions just to write the ubiquitous To-Do list.
But you must do all of it, otherwise its not MVVM.
The Zen of MVVM
MVVM grew out of the realisation that writing good, bug-free, safe UI code is hard. Testing UI code is harder, and involves hiring human testers, that are slow and can get it wrong.
So the solution that they came up with was simple.
DON'T WRITE ANY CODE IN YOUR UI
Done.
Except, not. Now, your UI doesn't do anything, it just looks pretty. So they added an extra layer between the UI and the Program/Business Logic/Model, and they called it the ViewModel.
The job of the ViewModel was to tell the UI what to do. But the trick was to get it to tell the UI what to do, without the ViewModel knowing about the UI at all.
(MVP has a similar concept, but the Presenter DOES know about the UI)
By having the ViewModel not know about the UI at all, we can write clean code that can easily be debugged and tested using our usual bag of tricks. Such as unit testing, refactoring, static code analysis, dependency injection etc etc...
Times are good!
Except the View Model still doesn't know about the UI. So we know what the UI should look like, but the UI doesn't know, because no one is telling it...
So they added the Binding class. The binding class's job is to watch the ViewModel, and then update the UI whenever something changes in the ViewModel.
In the world of MVVM there have been two different approaches to how the Binding class works.
You have the WPF way of doing things, which is to implement an event that tells the Binding class that the ViewModel has been updated. This is fast and flexible, but really annoying to write.
And you have the AngularJS way of doing things, which is to poll the ViewModel for updates. This is ridiculously slow and buggy.
If you have been following me thus far, you will note that MVVM defines a flow of data from your Model to your View. A break in any part of this chain will make it "not work".
It all so COMPLICATED, why bother?
The only reason I've found that justifies MVVM's excessive complexity is that you can write a GUI which you can have 90% test coverage, as the view only covers a tiny part of your program.
If you think automated testing is overrated, then you shouldn't use MVVM.
I am also pretty new to WPF, C# and MVVM. I have read quite a fair bit for these two to three months, so maybe I'll share what I understood.
You seem to have the same question that I had a week or two ago. Data should not be stored in the model. Models are just data structures. Databases (or simulated alternatives like Lists) are the actual storages that store these data. Some people would simply keep them in ViewModel, while some would move them to something outside of MVVM (e.g. a "Repository" class).
You are doing it all wrong. In MVVM, Views do not interact with ViewModels in this manner - they interact via Commands and Bindings. The fact that your View is directly manipulating the list means that it's definitely wrong.
Example:
View (Window):
<Window.Resources>
<vm:MyViewModel x:Key="MyVM" />
</Window.Resources>
<Window.DataContext>
<StaticResourceExtension ResourceKey="MyVM" />
</Window.DataContext>
<DataGrid ItemsSource="{Binding PeopleList}" ..... >
<Button Command="{Binding StoreCommand}" .... >
ViewModel:
public static readonly DependencyProperty PeopleListProperty =
DependencyProperty.Register("PeopleList",
typeof(ObservableCollection<Person>),
typeof(ViewModel));
public ObservableCollection<Person> PeopleList
{
get
{
return GetValue(PeopleListProperty) as ObservableCollection<EmissionEntry>;
}
set
{
SetValue(PeopleListProperty, value);
}
}
private ICommand _storeCommand;
public ICommand StoreCommand
{
get
{
if (_storeCommand == null)
_storeCommand = new MyCommandImplementation();
return _storeCommand;
}
}
Person is your model class with name/age etc. The list is kept in ViewModel, unless you want to have a repository somewhere.
You probably haven't really read anything about ICommand, so I suggest reading it up first. It is too long to give a tutorial here, but you can ask questions after you have read up some.

Best practice to design an application that needs a BackgroundAgent using MVVM pattern

While the title may seem too broad, I actually have not found any hint on how to solve this problem.
EDIT:
While I tagged the question properly, I forgot to write that I'm using Caliburn.Micro and this means that I must have both Views and ViewModels in the same project, and this forces me to have a separate library project for the Model, as the Background Agent can't depend on the app's project
Before diving into the question, here's a little example of the situation:
- App Solution
\- Model (C# library)
\- Background agent
\- Real App
\- Views
\- ViewModels
\- Resources and other stuff
where Real App and Background Agent depend on Model.
This seems to me the easiest way to make things work in my scenario.
The problem comes when I need to work with binding. In my previous projects I used to merge the Model and the ViewModel classes into one so that I could just bind the XAML to the VIewModel's properties without any kind of problem.
But now, since I'm forced to keep my Model in a separate project (the Background Agent can't depend on Real App), I have no idea on how this should work.
To make things more complex, my Model uses the async pattern to load data.
And this leads to the first question: since the Model loads data using th async pattern, how can I notify to the ViewModel that the data is ready to be displayed?
To make the question clearer, here's a quick snippet about this question:
namespace Models
{
public class Model
{
private string _neededProperty;
public string NeededProperty
{
get
{
return _neededProperty;
}
set
{
if (value == _neededProperty) return;
_neededProperty = value;
OnPropertyChanged();
}
}
public Model()
{
LoadData();
}
private async void LoadData()
{
NeededProperty = await StuffLoader();
}
private Task<string> StuffLoader()
{
return LoadStringAsync();
}
}
}
namespace ViewModels
{
public class ViewModel
{
public string NeededProperty
{
get
{
// Let's assume that we have a global instance of our model defined in the App.xaml.cs file
return App.Model.NeededProperty;
}
}
}
}
// Page.xaml
...
<TextBlock Text="{Binding NeededProperty, Source={StaticResource ViewModel}}"/>
...
How can I be sure that the TextBlock loads fine once the Model has loaded the string?
Of course the same problem needs to be solved to make the Background Agent work, because it relies on the same loading methods of the Model.
So, basically, the question is: provided that my structure is correct and that this is the best way to organize the project, how can I "listen" on Model's properties to report each change to the ViewModel and the Background Agent?
This could be also useful to show some kind of loading screen, which has to be shown in the Real App part and so I need to know when the Model actually finishes its loading routines.
I hope that the question is clear, I'm a little bit confused now because this requires a big paradigm shift when coming from Java!
ViewModel should not be in the same project as Views.
That way you can re-use Model and ViewModel in a different App if needed (ex: desktop and phone app).
Therefore, I would use your background agent in the viewmodel (maybe through the model depending on what your are doing exactly). That way the background agent is not known from the app and your app is using your ViewModel the same way as with or without the ViewModel
IMO that is the cleanest and easiest way.

Driving a MVVM application

Given any intermediate MVVM application which has more than 5 views and viewmodels, are there any recommend design patterns of how to do the scaffolding of such an application?
Right now I usually have a controller which is created in App.OnStartup which:
sets up the main view
injects subviews (typically I have a MainWindow with status bar and navigation, which has "inner windows")
handles the marriage of views and viewmodels.
handles navigation (going from view A to view B)
supports breadcrumb navigation (and things like the typical NavigationService.GoBack())
I believe there are already good design patterns, but non of them I have heard of or read about.
So the question is:
Is there any commonly accepted pattern of how to handle the coupling of viewmodel and view (setting the datacontext) and the navigation between views?
In my opinion both view-first (setting the DataContext in XAML) and ViewModel-First (let the viewmodel get the view injected via DI/IOC) are not that good because they have make up dependencies between view and viewmodel.
Plain MVVM makes no assumptions on how to set up the whole MVVM machine.
I'm just wondering that this quite common problem has no "of-the-shelf" solution.
Controllers are widely used I believe. How do others solve that?
Some design patterns to consider are Inversion of Control (IoC) and Event Aggregator.
For C# / MVVM, the Caliburn Micro Framework (is one of a couple that) makes IoC and Event Aggregator much easier.
You correctly identified a core concern of MVVM in that there is no off-the-shelf solution to truely decouple the ViewModel from the View. It is a core concept that ViewModels are purpose built to be pared with Views. The issue comes down to how to manage instances of ViewModel / View pairings.
View first approach assumes that the View knows about and can instantiate ViewModels as needed - this is a problem for SoC because any View class now has multiple responsibilities; spinning up a ViewModel, and handling UI.
View Model first is difficult because it often leads to breaking one of the main tennants of MVVM - that the VM should be testable without any associated views.
This is where IoC comes in, typically. The IoC typically resides in the View layer (this is to allow it to have access to all View and ViewModel classes as needed) it need not be a View itself. It's often better to think of your IoC manager as a Controller - which kind of leads to a pseudo pattern of MVCVM. The sole purpose of this "controler" becomes providing View and ViewModel instance pairings to whoever needs it.
Event Aggregator pattern really helps with this because classes in the ViewModel and View no longer need to worry about who they are paired with, and can interract only with other classes in their own level. A particular View Model need not care who sent the event "Update Load Progress" all it needs to be responsible for processing the results of the event by setting it's progress property.
Regarding the "link" between the View and the ViewModel, I found the concept of the DataTemplateManager in this post really interesting. Basically, it allows you to do things like
DataTemplateManager.Register<TViewModel1,TView1>();
DataTemplateManager.Register<TViewModel2,TView2>();
DataTemplateManager.Register<TViewModel3,TView3>();
it might not be the best solution, admittedly, but is pretty handy. I already incorporated that into my own homemade MVVM framework.
I have a smallish project that contains a singleton class called ViewFinder that has a couple static methods called MakeWindowFor(vm) and MakeDialogFor(vm), that both take the viewmodel as a parameter. ViewFinder has a Dictionary that I fill that links viewmodels with the windows I've set to correspond to them. More information could be added, because perhaps the view lives inside another instead of simply being a window.
This may not be the best way to accomplish the task, but works for my needs on this project, and keeps the viewmodels unaware of the actual view implementation. The ancestor of all my viewmodels contains events for things like displaying message boxes, and all my windows are descended from a base class that know how to subscribe and react to these common events.
public class ViewFinder {
private static ViewFinder m_Instance;
public static ViewFinder Instance {
get {
if (m_Instance == null)
m_Instance = new ViewFinder();
return (m_Instance);
}
}
/// Maps viewmodels to windows/dialogs. The key is the type of the viewmodel, the value is the type of the window.
private Dictionary<Type, Type> ViewDictionary = new Dictionary<Type, Type>();
/// Private constructor because this is a singleton class.
///
/// Registers the viewmodels/views.
private ViewFinder() {
Register(typeof(SomeViewModel), typeof(SomeWindowsForViewModel));
Register(typeof(SomeViewModel2), typeof(SomeWindowsForViewModel2));
}
/// Registers a window with a viewmodel for later lookup.
/// <param name="viewModelType">The Type of the viewmodel. Must descend from ViewModelBase.</param>
/// <param name="windowType">The Type of the view. Must descend from WindowBase.</param>
public void Register(Type viewModelType, Type windowType) {
if (viewModelType == null)
throw new ArgumentNullException("viewModelType");
if (windowType == null)
throw new ArgumentNullException("windowType");
if (!viewModelType.IsSubclassOf(typeof(ViewModelBase)))
throw new ArgumentException("viewModelType must derive from ViewModelBase.");
if (!windowType.IsSubclassOf(typeof(WindowBase)))
throw new ArgumentException("windowType must derive from WindowBase.");
ViewDictionary.Add(viewModelType, windowType);
}
/// Finds the window registered for the viewmodel and shows it in a non-modal way.
public void MakeWindowFor(ViewModelBase viewModel) {
Window win = CreateWindow(viewModel);
win.Show();
}
/// Finds a window for a viewmodel and shows it with ShowDialog().
public bool? MakeDialogFor(ViewModelBase viewModel) {
Window win = CreateWindow(viewModel);
return (win.ShowDialog());
}
/// Helper function that searches through the ViewDictionary and finds a window. The window is not shown here,
/// because it might be a regular non-modal window or a dialog.
private Window CreateWindow(ViewModelBase viewModel) {
Type viewType = ViewDictionary[viewModel.GetType()] as Type;
if (viewType == null)
throw new Exception(String.Format("ViewFinder can't find a view for type '{0}'.", viewModel.GetType().Name));
Window win = Activator.CreateInstance(viewType) as Window;
if (win == null)
throw new Exception(String.Format("Activator returned null while trying to create instance of '{0}'.", viewType.Name));
win.DataContext = viewModel;
return win;
}
}

Sharing Collection between ViewModels

I've searched and nothing is helping me get to where I need to get.
Description of my problem(s):
We have a single dialog window which hosts multiple views (usercontrols). Clicking next and back will move you forward and backwards in this dialog similar to a wizard.
Out of the 6 dialogs, 4 of them reference the same core data. For example we will say an ObservableCollection
That being the case, I have 4 viewModels which all need to reference this same ObservableCollection. I don't want to save and then reload the list everytime I move on to a new step in the "wizard" dialog.
My question is what's the best/most practical way to accomplish this.
The following things I've considered:
Static class
Singleton - ehhhh
Passing parameters between views (although this is difficult as the nextlocation and previouslocation are very generic).
Mediator pattern? My problem with the mediator pattern is that I don't want to "communicate" between views. I just want all of the views to share the same data source.
Observer pattern? If I'm using an ObservableCollections and implementing INotifyPropertyChanged, then I shouldn't need to notify anyone of a change should I?
Please let me know additional info you might need to help me with this and I'll happily provide it.
I'm not really looking for code examples as I am design. Although if code examples can help explain the design, I'm all for it.
Lastly, EventAggregator is not an option as I'm not using any frameworks (unless I'm not understanding EventAggregator correctly).
Thanks in advance!!
like Randolf said DI would work. i do this with MEF and contructor injection with CreationPolicy shared. you just have to put the stuff you wanna handle for you 4views in one export class. how easy is that :)
and btw: Mediator pattern is "communicate" between viewmodels not views.
[Export]
public class MyClassWichHasDataSharedFor4Views {}
public class Viewmodel1
{
[ImportingContructor]
public Viewmodel1(MyClassWichHasDataSharedFor4Views shareddata)
{}
}
public class Viewmodel2
{
[ImportingContructor]
public Viewmodel2(MyClassWichHasDataSharedFor4Views shareddata)
{}
}
public class Viewmodel3
{
[ImportingContructor]
public Viewmodel3(MyClassWichHasDataSharedFor4Views shareddata)
{}
}
public class Viewmodel4
{
[ImportingContructor]
public Viewmodel4(MyClassWichHasDataSharedFor4Views shareddata)
{}
}
now the 4 viewmodels has a reference to your core data
Based on the description of your dialog and how it works, I would have one ViewModel that controlled the overall "Wizard".
This DialogWizardViewModel would contain:
ObservableCollection<SomeModel> Data
ObservableCollection<ViewModelBase> DialogWizardViews
ViewModelBase SelectedView
ICommand NextCommand
ICommand BackCommand
Your DialogView would contain something like a ContentControl bound to the SelectedView, and your Next/Back commands would simply switch the SelectedView to the next or previous view in DialogWizardViews
For example,
<DockPanel>
<StackPanel DockPanel.Dock="Bottom"
Orientation="Horizontal"
HorizontalAlignment="Center">
<Button Command="{Binding BackCommand}" Content="Back" />
<Button Command="{Binding NextCommand}" Content="Next" />
</StackPanel>
<ContentControl Content="{Binding SelectedView}" />
</DockPanel>
The Data can can set in your child ViewModels when you first create the DialogWizardViews, or whenever you switch the SelectedView depending on what you prefer.
DialogWizardViews = new ObservableCollection<ViewModelBase>()
{
new DialogViewModel1(Data),
new DialogViewModel2(),
new DialogViewModel3(Data),
new DialogViewModel4(Data),
new DialogViewModel5(Data),
new DialogViewModel6()
};
SelectedView = DialogWizardViews.FirstOrDefault();
Remember, with MVVM your ViewModels are your application while the Views just provide a user-friendly interface for users to interact with the ViewModels. Ideally you should be able to run your entire application from something like test scripts, or a console application, without the View at all.
If you need to pass something around, it should be handled by some parent object within the application hierarchy, or use some kind of communications system such as Prism's EventAggregator or MVVM Light's Messenger. You don't need to use the entire framework to make use of these objects - you can just pick out the parts you're interested in.
I know it has been a long time since this question was asked, but after struggling a lot with the same problem
RayBurns's Response here really helped me come to the really simple and relieving realization that there is no need to feel like you have to make viewmodels for each of your usercontrols. I now use one viewmodel for a bunch of different views over the evolution of a Collection as a family of related information whose states are all relevant to eachother .
Otherwise, if you really must share data between viewmodels, I'd absolutely do this as a parameter to the constructor like #blindmeis demonstrates.
The nature of how DataContexts are set up and shared in the first place (for me!) seems to lie in the code-behind of the View. That's the only thing I put there, but it allows me to share ObjectContexts in Entity Framework, establish Master-Details relationships with one view-many viewmodels, etc.
Forming a repository "higher layer" is also great, but be cautious that you're copying over/transferring the data several times over before it reaches its destination.
Anywho, that's my two cents. Hope it helps. After a lot of discomfort that's what I ultimately found myself converging to.
It sounds to me like your views share the same Model, which should be observable, and your ViewModels should all represent the Model in different ways.
If your Model and ViewModels implement the Observer pattern, that seems to be the cleanest, "rightest" approach.
A simple "dependency injection" should work. Constructor of the second viewmodel depends on that Shared Data parameter injected into his constructor, and so on.

The best approach to create new window in WPF using MVVM

In the neighbour post: How should the ViewModel close the form?
I've posted my vision how to close windows with MVVM usage. And now I have a question: how to open them.
I have a main window (main view). If user clicks on the "Show" button then "Demo" window (modal dialog) should be displayed. What is a preferable way to create and open windows using MVVM pattern? I see two general approaches:
The 1st one (probably the simplest). Event handler "ShowButton_Click" should be implemented in the code behind of the main window in way like this:
private void ModifyButton_Click(object sender, RoutedEventArgs e)
{
ShowWindow wnd = new ShowWindow(anyKindOfData);
bool? res = wnd.ShowDialog();
if (res != null && res.Value)
{
// ... store changes if neecssary
}
}
If we "Show" button state should be changed (enabled/disabled) we will need to add logic that will manage button state;
The source code is very similar to "old-style" WinForms and MFC sources - I not sure if this is good or bad, please advise.
Something else that I've missed?
Another approach:
In the MainWindowViewModel we will implement "ShowCommand" property that will return ICommand interface of the command. Comman in turn:
will raise "ShowDialogEvent";
will manage button state.
This approach will be more suitable for the MVVM but will require additional coding: ViewModel class can't "show dialog" so MainWindowViewModel will only raise "ShowDialogEvent", the MainWindowView we will need to add event handler in its MainWindow_Loaded method, something like this:
((MainWindowViewModel)DataContext).ShowDialogEvent += ShowDialog;
(ShowDialog - similar to the 'ModifyButton_Click' method.)
So my questions are:
1. Do you see any other approach?
2. Do you think one of the listed is good or bad? (why?)
Any other thoughts are welcome.
Thanks.
Some MVVM frameworks (e.g. MVVM Light) make use of the Mediator pattern.
So to open a new Window (or create any View) some View-specific code will subscribe to messages from the mediator and the ViewModel will send those messages.
Like this:
Subsription
Messenger.Default.Register<DialogMessage>(this, ProcessDialogMessage);
...
private void ProcessDialogMessage(DialogMessage message)
{
// Instantiate new view depending on the message details
}
In ViewModel
Messenger.Default.Send(new DialogMessage(...));
I prefer to do the subscription in a singleton class, which "lives" as long as the UI part of the application does.
To sum up: ViewModel passes messages like "I need to create a view" and the UI listens to those messages and acts on them.
There's no "ideal" approach though, for sure.
I was thinking about this issue recently too. Here's an idea I had if you use Unity in your project as a 'container' or whatever for dependency injection. I guess normally you'd override App.OnStartup() and create your model, view model, and view there, and give each the appropriate references. Using Unity, you give the container a reference to the model, then use the container to 'resolve' the view. The Unity container injects your view model, so you never directly instantiate it. Once your view is resolved, you call Show() on it.
In an example video I watched, the Unity container was created as a local variable in OnStartup. What if you created it as a public static readonly property in your App class? You could then use it in your main view model to create your new windows, automatically injecting whatever resources the new view needs. Something like App.Container.Resolve<MyChildView>().ShowDialog();.
I suppose you could somehow mock the result of that call to the Unity container in your tests. Alternatively, perhaps you could write methods like ShowMyChildView() in the App class, which basically just does what I described above. It might be easy to mock a call to App.ShowMyChildView() since it would just return a bool?, eh?
Well, that might not really be any better than just using new MyChildView(), but it's a little idea I had. I thought I'd share it. =)
I'm a bit late, but I find existing answers insufficient. I will explain why. In general:
it's ok to access ViewModels from View,
it's wrong to access Views from ViewModels, because it introduces circular dependency and makes the ViewModels hard to test.
Benny Jobigan's anwer:
App.Container.Resolve<MyChildView>().ShowDialog();
this actually does not solve anything. You are accessing your View from ViewModel in a tigtly coupled fashion. The only difference from new MyChildView().ShowDialog() is that you went trough a layer of indirection. I don't see any advantage over directly calling the MyChildView ctor.
It would be cleaner if you used interface for the view:
App.Container.Resolve<IMyChildView>().ShowDialog();`
Now the ViewModel is not tigtly coupled to the view. However I find it quite impractical to create interface for each view.
arconaut's anwer:
Messenger.Default.Send(new DialogMessage(...));
it's better. It seems like Messenger or EventAggregator or another pub/sub patterns are universal solution for everyhing in MVVM :) The disadvantage is that it's harder to debug or to navigate to DialogMessageHandler. It's too indirect imho. For example, how would you read output form the Dialog? by modifying DialogMessage?
My Solution:
you can open window from MainWindowViewModel like this:
var childWindowViewModel = new MyChildWindowViewModel(); //you can set parameters here if necessary
var dialogResult = DialogService.ShowModal(childWindowViewModel);
if (dialogResult == true) {
//you can read user input from childWindowViewModel
}
DialogService takes just dialog's ViewModel, so your viewmodels are totally independent from Views. At runtime, DialogService can find appropriate view (using naming convention for example) and shows it, or it can be easily mocked in unit tests.
in my case I use this interfaces:
interface IDialogService
{
void Show(IDialogViewModel dialog);
void Close(IDialogViewModel dialog);
bool? ShowModal(IDialogViewModel dialog);
MessageBoxResult ShowMessageBox(string message, string caption = null, MessageBoxImage icon = MessageBoxImage.No...);
}
interface IDialogViewModel
{
string Caption {get;}
IEnumerable<DialogButton> Buttons {get;}
}
where DialogButton specifies DialogResult or ICommand or both.
Take a look at my current MVVM solution for showing Modal Dialogs in Silverlight.
It solves most of the issues you mentioned yet its completely abstracted from platform specific things and can be reused. Also i used no code-behind only binding with DelegateCommands that implement ICommand. Dialog is basically a View - a separate control that has its own ViewModel and it is shown from the ViewModel of the main screen but triggered from the UI via DelagateCommand binding.
See full Silverlight 4 solution here Modal dialogs with MVVM and Silverlight 4
I use a controller which handles all information passing between views. All viewmodels use methods in the controller to request more information which can be implemented as dialogs, other views etc.
It looks something like this:
class MainViewModel {
public MainViewModel(IView view, IModel model, IController controller) {
mModel = model;
mController = controller;
mView = view;
view.DataContext = this;
}
public ICommand ShowCommand = new DelegateCommand(o=> {
mResult = controller.GetSomeData(mSomeData);
});
}
class Controller : IController {
public void OpenMainView() {
IView view = new MainView();
new MainViewModel(view, somemodel, this);
}
public int GetSomeData(object anyKindOfData) {
ShowWindow wnd = new ShowWindow(anyKindOfData);
bool? res = wnd.ShowDialog();
...
}
}
My approach is similar to adrianm’s. However, in my case the Controller never works with the concrete View types. The Controller is completely decoupled of the View - in the same way as the ViewModel.
How this works can be seen in the ViewModel example of WPF Application Framework (WAF).
.
Best Regards,
jbe

Categories