WPF MVVM Advice on Pop up Searches - c#

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;

Related

Wpf MVVM: What exactly needs to be a view?

I'm new to WPF and MVVM and attempting to write a firmware programmer so I can update stuff via USB and save and upload setting/state data. MVVM seems like it could work for this. I currently have pages and can navigate around the app (although the nav service is in code behind for now) but I'm stuck on how to implement things that aren't in the standard 'customer'/'person' examples.
On a couple of pages, there are subsections that I can see being sub-divided into separate views hosted in the page, and these subsections are used more than once in the app.
For instance, I want to have a TextBlock that displays the connection status and updates based on signposts in the connection process, firmware update, backup, etc. Progress bars with the % are another. Sections that are used to display errors, data or a selection box depending on what happens connecting would be another.
Having a sub-section house 3 completely different outputs all stacked on top of one another and shown based on the situation seems messy. I can see that section being a ViewBox and creating a unique view for each case being a better solution there (and possibly the other examples above).
Or take the 'status display', I can see implementing it as it's own view and the page's view model will use a messenger to pass the current status back to the 'status display' view model. I can also see it all just handled by the page's view model via calls to it's own methods. I can also see potentially using a global model to hold the status strings (maybe an enum?) and the view model can be made to pull the correct string into a 'currentStatus' variable.
What is the proper way to approach this? Keep it all a single page? Subdivide the dynamic/changing parts from the static parts?
OP:
Obviously the pages themselves are views, but would it be best to have the 'Status:'display TextBlock and it's value, and the Error/selector section be views also?
If you are asking whether the status and error displays should be UserControls then yes they can be "a view" irrespective of whether the control is using a view model or not.
Incidentally, it is generally better to use DependencyPropertys instead of view models in a UserControl otherwise you will end up having duplicate properties in both the view (so that MainWindow can databind to it) and in your control's view model (purely for the benefit of the user control).
If your UserControl uses DependencyPropertys then both users of the control and your view can both databind to the same set of properties without duplication. In this way you will realise that UserControls have no need for a separate VM.
OP:
...the page's view model will use a messenger to pass the current status back to the 'status display' view model...
Don't do this, this is what data binding is for.

MVVM correct flow of events

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.

Open new View when new ViewModel is available?

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.

MVVM ViewModel-View communication

I have a ListView and a Popup which content(Buttons, TextBoxes etc.) changes dynamically depending what is selected in the ListView. Is it legal, if the View determines what is in the Popup or the ViewModel should do it? I also would like to ask, whether the View can handle the ViewModel's events?
MVVM is not a law - it is a tool. If what you are doing serves your purpose - it works for you. If you are asking what the best practice is - it depends on why you using MVVM in the first place.
Is it because you want to write tests independent of the view? Then put your logical state in the view model.
Is it because you want to be able to display design time data in Blend? Then put your logical state in the view model and define design time data to show in design mode.
Is it because you want to have maintainable code that will be maintained for a long time by people familiar with MVVM? Put your logic in the view model.
Is it because your app is complicated and you would like to have separation of concerns so different people would work on the view and different people would work on the view model. Put your logic in the view model.
If none of these apply - you might be just fine and spend less time trying to figure it out by just putting the code in code behind and not let MVVM to be a drag.
If your problem is how to drive the contents of your Popup with your view model - you can typically do that using a combination of ItemsControls such as ListView, GridView or maybe a custom one and ContentControls with ItemTemplateSelectors or ContentTemplateSelectors that implement the logic of determining which view to display based on the view model state.
Also check my Minimalistic MVVM manifesto post.
Check out 5: Implementing the MVVM Pattern for a detailed explanation of what is valid in the View. It explains it a lot better than I could here in this answer.
I will give a shot at your questions though:
Q: Is it legal, if the View determines what is in the Popup or the ViewModel should do it?
A: The view should determine how the Popup is displayed, the ViewModel should determine what is displayed.
Q: Can the View can handle the ViewModel's events?
A: Absolutely, this is a key part of DataBinding - handling INotifyPropertyChanged events.

SL5 + MEF + PRISM + DataGrid + Edit Rows in Modal Dialog: Is InteractionRequest the Answer?

I am in the process of re-writing one of our large Silverlight apps to use PRISM and the MVVM design pattern.
A very common scenario is a DataGrid in the View. Double clicking a row allows the user to edit the entity represented by the row, using a ChildWindow.
I am tempted just to capture the DoubleClick event in the code behind, create a new ChildWindow of the proper type, and set the DataContext to be DataGrid.SelectedItem.
I know that this is not the proper way to handle this scenario with PRISM and MVVM, however.
I would love advice on what is! (re: my title...it seems like InteractionRequest might be the best way to do this?)
Thanks...
EDIT: We did end up deciding to go with InteractionRequest for our solution. We almost always use "Notification" as the type and pass a new ViewModel (each ChildWindow has its own) as the Content.
In our case the ChildWindow view was complex enough to warrant its own viewmodel. This view isn't too closely coupled with the data grid view.
So, we have an EventTrigger attached to the data grid (we actually use Telerik's data grid) in XAML. The event trigger executes a command in the view model using InvokeCommandAction.
The command publishes an aggregated event that has the selected item as the payload. The event is picked up by the central application controller that is responsible for creating the ChildWindow view and a corresponding view model (using the event payload as the context).
I think that interaction request could potentially be used in your case, but based on my understanding the idea behind an interaction request is a very simple Ok or Yes/No interaction. You might be pushing the boundaries with a bunch of text boxes, validation, etc.

Categories