I am relatively new to WPF/MVVM. Our current WPF project is still a prototype application, which means we haven't come to designing of main layout. But we do have designed some reports (by reports, I mean some quite complicated UserControl each of which has some configuration controls such as ComobBoxor TextBox, and some DataGrid, Graph controls for the data we want to present) as Viewand their ViewModel. It is still prototype, so we just need to open a new Window which displays any of these UserControl. In the future, we might change it to locate different UserControl in different regions inside the main window, like the layout of Visual Studio. The MAIN point is, our application would include almost a hundred of such UserControl or what we call reports. So it is different from my previous working projects which had static layout/MainView.
I still haven't figured out a good architecture for it. Our classic usage scenario is to let the user to select in the menu report to open, and so we have Command (either in MainViewModel, or in any ViewModel of report) objects to open a new report. So basically the Command is generating a new ViewModel (ViewModel first case) and then a corresponding View should be generated (by whom?), and finally a new Window should be opened including the newly-generated UserControl.
I guess I need two services:
a service which subscribes to the new-ViewModel-generated event, and resolve the UserControl when such event happens.
(for our current prototype application) a window manager, which subscribes to the new-UserControl-generated event published by the 1) service, and then opens a new Window to display it.
And in the future for our actual application, we can change the 2) service and put them into different regions. For the second, it is simple and only temporary, I can just have one object in the code-behind of MainView, which subscribes to the event using EventAggregator, and generate a new Window. Is it correct ?
Can somebody tell me how I should achieve this?
Data binding can already handle this for you. In the container where you want to display the reports add a ContentControl and bind it to a property that holds the VM for the report that you want to display currently.
<Window>
<ContentControl Content="{Binding Path=CurrentReport}" />
</Window>
To display the different reports wrap each of the UserControls in its own DataTemplate that can be injected into the ContentControl. To actually resolve the view you have a few choices. You can create a DataTemplateSelector to map them or just specify the VM types on your templates. In either case, make sure the templates are in scope at the ContentControl (in Resources in the same file or a parent, or merged from standalone ResourceDictionary).
<DataTemplate DataType="{x:Type viewModels:FirstReportViewModel}">
<views:FirstReportViewControl/>
</DataTemplate>
John Bowen beat me to it, by I thought I'd still post, maybe it helps.
For associating views with view models you can use data templates in a resource dictionary.
<DataTemplate DataType="{x:Type vm:AllCustomersViewModel}">
<vw:AllCustomersView />
</DataTemplate>
As you probably already know, you can set namespaces within your resource dictionaries. In this example vw and vm reference the folders containing viewmodels and views respectively.
Now you can use content control to generate the views by binding to the view model.
<ContentControl Content="{Binding SomeViewModel}" />
The code above has been shamelessly stolen from Josh Smith btw.
So, you should not need a service for resolving the association of view to viewmodel. Let the framework do the work for you.
I actually do not recommend opening new windows. If you must, using a "Window Controller"-Service of some sort will be unavoidable. However, I advise you to stick to a single window containing multiple viewmodels and exchanging them upon receiving certain events.
Related
I'm somewhat new to MVVM but I have had my hands on it for some time now. Unfortunately, perusing the internet there seems to be no solid consensus on the best way for an application to process events in MVVM. I have a situation pictured below in which I am combining two controls - each in a self contained xml that i'm going to call two separate views: ListBox and ButtonPanel.
How should the direction of events flow then? Let's take a scenario where upon clicking my button in the button panel, a file is created in the list box control. Would the flow of events in image one or in image two be more appropriate?
Event Flow One - Communicating ViewModel to ViewModel:
Event Flow Two - All Front end classes don't communicate. All messages are passed via the Application Layer (whatever is hooked up to the back end: db, processing functions, etc..)
Please note - i didn't picture it but I am using ICommand to pass the click event from my Button Panel View to my Button Panel View Model.
And lastly once again my question is: How should events be processed in an MVVM flow. Are either of my graphics appropriate?
* **EDIT IN RESPONSE TO Ed Plunkett's ANSWER ***
Is this then the sort of event flow you would expect:
Is this closer to the correct architecture?
In this image the << >> between ViewModels are actually properties being exposed to the views, and the majority of the events are being handled as bindings there in the view.
My concerns with this are:
The Master ViewModel is now responsible for collecting information from both views. I'm not sure how to handle this without using mediator/messenger - that is without tightly coupling my parent view model to the child. Either i'd have to register events by name in all view models, or make specific callbacks by name for each of the viewmodels, so a mediator would still be necessary to achieve loosely coupled design (which is the entire advantage of using MVVM).
Even if i'm wrong in point 1 - what is the advantage of using a parent child ViewModel relationship?
Concerning the point of ViewModels don't know what a button or a panel is:
I agree. You can ignore my naming of classes here. I've just done it this way to make the question easy to understand - named classes so that an onlooker knows what control is being handled in each. My actual classes are named based on the data they handle, not the component.
Simplest case: One viewmodel.
The button invokes a command that it gets from a property of the viewmodel.
The viewmodel also has a property something like
public ObservableCollection<SomeFileViewModelClass> Files { /* INPC stuff */ }
The button has no idea what the command does. Presumably the command tells the viewmodel to create a file, and add a new instance of SomeFileViewModelClass to Files.
Meanwhile, back in the XAML, the ListBox is using Files as an items source:
<ListBox
ItemsSource="{Binding Files}"
...
...so the listbox will automatically update to show the new file.
If you want your button panel to have its own viewmodel (you almost certainly don't, and even if you do, don't call it a "button panel viewmodel"; viewmodels don't know what buttons or panels even are) and your file list to have its own viewmodel (maybe), make those kind of things child viewmodels of the main viewmodel. The main viewmodel creates them and manages relationships between them.
public FilesViewModel FileList { /* INPC stuff */ }
Maybe FileList now owns ObservableCollection<SomeFileViewModelClass> Files -- but if that's all it owns, it isn't a viewmodel. Just put that Files property on the main viewmodel.
<Button Content="Create File" Command="{Binding CreateFileCommand}" />
<!-- ... snip -->
<ListBox ItemsSource="{Binding FileList.Files}" />
Maybe CreateFileCommand should be a property of FilesViewModel, if that even exists.
Keep it as simple as possible, but no simpler.
Do not design a UI and then create one viewmodel for each thing in the UI. That's backwards. Controls don't have viewmodels.
Design viewmodels to model the things your application needs to deal with. Parent and child viewmodels, based on the actual relationships between the actual stuff (personally, I prefer to model the smallest "leaf node" things first, then work my way into the center). Then add views to display them to the user. This takes a little getting used to, but it pays off.
Your viewmodels are "The Program". You write views to display the program to the user and let the user talk to the program. You write models/database/file access/whatever to let the program store and retrieve state.
Your "All messages are passed via the Application Layer" idea is not MVVM.
Your second thing with the yellow "mediator/messenger" box is close to correct, but there's no "mediator/messenger"; parents talk to children. If a child will need to know something about a sibling, you would manage that by having the children expose events. The parent can wire up event handlers among the children -- to do it quick and dirty, you can always handle INotifyPropertyChanged.PropertyChanged on a child, but specialized events are often preferred. Dependency injection is another option but it's easy to end up writing classes with a maze of dependencies. DI can turn into an antipattern, and multi-colored block diagrams of the information flow in user interfaces is a leading indicator that it's about to.
Your case is simplified, but you'd really have a "ListBox View" that contains a ListBox and a "Button View" that contains the button. Both of those views would be child views of the "Main Window View".
I'd have to disagree with Ed here and say I would never combine VMs... that breaks the single responsibility paradigm that MVVM tries to install.
The standard MVVM way of having VMs communicate is through the messenger / event aggregator paradigm.
So i'd vote for workflow #1. ALMOST...
VM's should NOT send a message to another VM to write to a DB, etc. That's what models are for.
If you want to abstract away the DB, the standard way to do that is to define a IDataProvider type interface and use DI to inject it into VMs that need it. You'll find as your app gets larger and more complicated, DI and MVVM are a match made in heaven.
MVVM + DI + Messenger all work together beautfully and keep everything seperate and organized.
Personally I like to keep V <-> VMs 1:1 with no code behind, but that's the super purist definition of MVVM.
EDIT: Just to clarify, I don't think you really mean you are just plopping 2 controls on a form and making each a VM, I wouldn't do that. Break the UI into logical "regions" and each "region" would have a VM and a V.
I am using WPF with Prism and MEF for my application. There was a need to create controls dynamically. And so here is my problem!!
As far as I know I should not be having code in my code behind (SomeFile.xaml.cs) to keep my code easily testable. And so the code should be actually moved to ViewModel.
But my code generates UI controls dynamically. And I dont think that the ViewModel should know anything about the Controls.
So where and how should I go about writing this code?? What would be the right approach?
Hope I made myself clear!
Thanks
When working with WPF/MVVM, your data layer is your application (the DataContext), and you use things like Templates to tell WPF how to draw your application components to the UI.
For example, suppose you're given the task to dynamically render a bunch of controls.
The WinForms way might have been to loop through your objects, create a UI control for each object, then add the UI control to the screen.
However with WPF/MVVM, you would instead create a class representing each object (a Model), and give WPF a list of those classes to display.
There are many different controls WPF can use to draw a list of objects, but the most basic of them is probably an ItemsControl. I have some examples of a simple ItemsControl on my blog if you're interested.
Now even though you've given WPF the list of your objects to render, and told it what control to use to render the list, it still probably doesn't know how to draw your object. The usual solution for this is to include an XAML template of some kind to tell it how to draw your object.
Here's a simple example template that is used to tell WPF how to draw an object of type MyClassObject:
<DataTemplate DataType="{x:Type local:MyClassObject}">
<StackPanel Orientation="Horizontal">
<TextBlock Text="Name:" />
<TextBox Text="{Binding Name}" />
</StackPanel>
</DataTemplate>
I hope that helps get you going in the right direction :)
From my perspective, the view has the responsibility to render the data/model provided by the viewmodel. While it is ideal to keep as much of the model->view translation logic in the viewmodel, creation of controls adds significant complexity to move to the viewmodel, because of the coupling or infrastructure that it could add.
While you could:
1) Give the viewmodel direct access to the view, allowing it to create controls directly
This adds coupling...
2) Create an interface on the view to manipulate controls
This means the view must be passed (not simply bound) to the viewmodel
3) Create an abstraction of controls to bind to the view, and have the view render based on the abstraction created by the viewmodel
This adds a maintenance nightmare as your control set expands.
I suggest exposing the model (either directly or wrapped in micro-viewmodels) via a property on the main viewmodel, and binding that to a property on the view via xaml, such that any PropertyChanged on the viewmodel will send an update to the view. Use the model information and your control creation logic inside the view to clear and re-create the controls and bind the model dynamically to the controls you create. This will simplify both the creation and binding of the controls, and you won't have to practice any voodoo to get information entered into the controls back into your model.
Note that even if you created an intermediary class (an attached behavior, adorner, or something else), it will still need to be able to access the view's control structure directly to attach the generated controls to the visual tree, which may cause other problems.
I am pretty new to the WPF MVVM model so please bear with me. I am trying to write a stock management system. I have a maintenance page that I successfully link to my viewmodels. However I would like to enable a search control that I can trigger from the maintenance page to popup a grid where a user can search for an item, select it and return to the maintenance screen with the selected item being showm.
How would it be best to implement this type of functionality in MVVM ? The search button on the maintenance screen can be linked to a search ICommand but the Viewmodel has no knowledge of the UI so it would not know to show what the name of the search control is or how to show it. The only think I can think of is to write the search button event in code behind on the UI but doesnt this break the MVVM pattern ?
Thanks in advance and apologies if this is a stupid question.
A good way to solve your problem is using the User Interaction Patterns.
In terms of the MVVM pattern, the view model is responsible for initiating an interaction with the user and for consuming and processing any response, while the view is responsible for actually managing the interaction with the user using whatever user experience is appropriate. Preserving the separation of concerns between the presentation logic implemented in the view model, and the user experience implemented by the view, helps to improve testability and flexibility.
There are two common approaches to implementing these kinds of user interactions in the MVVM pattern. One approach is to implement a service that can be used by the view model to initiate interaction with the user, thereby preserving its independence on the view's implementation. Another approach uses events raised by the view model to express the intent to interact with the user, along with components in the view that are bound to these events and that manage the visual aspects of the interaction.
This is a MVVM pattern for doing DialogServices etc. so it will fit your requirement, too.
The main problem here is how to display the search popup in an MVVM friendly way. I have an example on my github account of a custom control that is designed for this very purpose (full source code is available to download).
The control can be used like this:
<c:ModalContentPresenter IsModal="{Binding DialogIsVisible}">
<!-- This is the main content e.g. your maintenance screen -->
<TabControl Margin="5">
<Button Margin="55"
Padding="10"
Command="{Binding ShowModalContentCommand}">
This is the primary Content
</Button>
</TabItem>
</TabControl>
<c:ModalContentPresenter.ModalContent>
<!-- This is the modal content e.g. your search popup -->
<Button Margin="75"
Padding="50"
Command="{Binding HideModalContentCommand}">
This is the modal content
</Button>
</c:ModalContentPresenter.ModalContent>
</c:ModalContentPresenter>
The modal content is displayed directly over the primary content (in your case the maintenance screen) and its visibility is controlled by the IsModal property which can be bound to a property in your viewModel. This property would be set to true by the search command and your search grid would be displayed in front of the maintenance screen.
Your search screen 'view' would have a close button which is bound to another ICommand object which simply sets the property to false and hide the popup content.
Note that there is no need to 'pass' any information around as both the primary and modal content is managed by the same control so they both share the same DataContext which in your case will be a reference to your viewModel.
To avoid direct coupling between the UI and viewmodels I've previously used a Mediator to forward the creation of the new UI element back to the UI classes. However, I've never been entirely convinced by this approach so would be interested to see if anyone has any better solutions.
if you mean Dialog when writing popup then i would use a Dialog Service like this one.
in your maintenance viewmodel opencommand:
var result = this.uiDialogService.ShowDialog("Dialogwindow title goes here", dialogwindowVM);//in your case the viewmodel for your search
//check the result and just take the SelectedItem from your dialogwindowVM
if(result)
this._selected = dialogwindowVM.MySelectedItem;
I have a viewModel which is used to bind to a user control. The user control is basically a AdRotator. One of the feature of AdRotator is that it can be used in multiple positions on same screen. Seperate set of ads will be displayed on these multiple adRotators. The single view model exposes 4 observable collections which is deputed for adRotators on various locations . My problem is that since user controls are 'drag n drop' use i am a looking for a identification method that will let me determine which observablecollection(of the 4) should the an adRotator bind to. Please let me know what are the approaches for this.
Will it be a good approach if i retrieve the name of the user control and bind the collection depending on the name?
ViewModels are for logical parts of your application. For reusable controls, I would create a regular UserControl with code behind. This Control can expose API through properties and events, and in your case would expose a Dependency Property that would get the list of "ads".
This way you can position them all in a View, where each AdRotator control is bound to a different collection. This way the logic would sit in only one place - the ViewModel, and the reusable UI would sit in a dedicated control.
View+ViewModels are for logical seperation (Single Responsibility Principle, SRP) where are reusable controls are for reuse (or DRY: don't repeat yourself). It's very important to identify which type of control you are using. regular reusable controls should not contain ViewModel.
I don't know the specifics of your view models so I will offer one possible approach expressed in general terms.
Suppose you have a view model with four child view models which are all variations that have properties that the bindings in your user control are looking for and are named Vm1, Vm2, etc. which in your case could be your ad collections. You could bind the DataContext of each instance of you user control to each of the child view models.
<my:SampleUserControl DataContext="{Binding Path=Vm1}" />
<my:SampleUserControl DataContext="{Binding Path=Vm2}" />
<my:SampleUserControl DataContext="{Binding Path=Vm3}" />
<my:SampleUserControl DataContext="{Binding Path=Vm4}" />
This way each instance of your user control can bind to and display different data.
I use AvalonDock to have a vs-like docking style, and I use prism to inject views in my application.
Basically the user will have a panel on the side with one button per view loadable, and each time he presses one, I want to create a new tab on my AvalonDock's DocumentPane, and inject the right view in it.
Now I was under the impression that prism is good for this kind of scenario, but I can'f figure it out.
Also, I stumbled accross DocumentPane and DockablePane region adapters by Markus Raufer, have added it to my project and registered the mappings in the bootstrapper.
This allows me to compile the solution (so I'm guessing I'm getting close).
So far, I do this:
In my Shell I have a place with:
<avadock:DocumentPane avadock:ResizingPanel.ResizeWidth="500" Background="Transparent"
prism:RegionManager.RegionName="{x:Static inf:RegionNames.MainTabControlRegion}">
</avadock:DocumentPane>
In my code-behind, after Bootstrapping etc, I have a place that does:
m_RegionManager.Regions[RegionNames.MainTabControlRegion].Add(oneOfMyUserControls);
Executing this line doesn't show anything more to the user, but if i execute it twice, it will make an error saying a view is already registered (so it did add something!).
Furthermore, when i put a regular ContentControl, mark it with prism:RegionManager.RegionName="{x:Static inf:RegionNames.MainTabControlRegion} then add a UserControl in it the very same way I add it to AvalonDock's DocumentPane, it works. freaky
What am I doing wrong?
Thanks,
bab.
EDIT:
If define the MainTabControlRegion like this, the first view actually gets added, now id i add another one (different UserControl) it doesn't show it, but WPF Inspector says it's there.
From my point of view, it is indeed adding UserControls to a region, but it doesn't know how to create a new tab for each UserControl I add to the DocumentPane.
Since I'm not specifying anything about that, I'm probably missing a piece of code?
<avadock:DocumentPane avadock:ResizingPanel.ResizeWidth="500" Background="Transparent">
<avadock:DocumentContent Title="" prism:RegionManager.RegionName="{x:Static inf:RegionNames.MainTabControlRegion}">
</avadock:DocumentContent>
</avadock:DocumentPane>
I want it to create a new tab each time i add a usercontrol and insert it in there.
EDIT 2: Solution --> Here
This problem can be caused for many reasons.
Or the RegionAdapter is not well suited for the user controls you are trying to add, therefore I would recommend you to have a look at RegionAdapter implementation so you can have a good understanding of how it adapts your view in the region and customize it to fit your needs.
Or the user control you are adding has a particular behavior and is bugging the RegionAdapter when it tries to add your view to the DocumentPane items. Therefore you can customize the RegionAdapter or fix the user control behavior.
Or it is simply a data binding problem. The fact that nothing shows up on first execution is kind of strange. Try to inspect your DocumentPane property Binding with WPF Inspector. It saved my life more than once when dealing with custom RegionAdapters.