What is the right way to approach the TabControl with complex views? - wpf

My application is for medical clinics and I select a patient and then there are several Tabs to choose from, Patient Info, Family History and so on. I started out with all tabs in the MainView but have come to the conclusion that each tab needs to be on separate Views because of the complexity of the screens.
How then do I share the Patient info across multiple views? All screens relate to the Patient in some way and I have gone down the road of MainViewModel hosting all the data but that seems like a bad idea because I manipulate data on separate views and need to get the updates back to the MainViewModel. What is the right approach, I have searched the internet for days looking at different architecture and had little to no luck finding any type of similar architecture.
What I did so each ViewModel could access the Patient data is use the code below.
<TabControl Grid.Row="2" x:Name="TC">
<TabItem Header="Patient Info" IsEnabled="{Binding IsPatientSet}" DataContext="{Binding}">
<view:TabPatientView DataContext="{Binding ElementName=TC, Path=DataContext}" />
</TabItem>
<TabItem Header="Clinical Worksheet" IsEnabled="{Binding IsPatientSet}">
I bound each ViewModel to the MainViewModel since each ViewModel was just one aspect of Patient Info. Works great!

The architecture of a program depends on different varibles and there is no one correct answer which I can give you.
However, I can propose some ways to handle your issue:
Keep a Master ViewModel (which is what you are doing now). Create a ViewModel for each View and use a common class (e.g Service) to share patient info between the View models. In this case, service may also expose an event which can be raised when patient info is changed.
Use a light weight WPF infrastructure like MVVM Light Toolkit to share changes in patient info by using the Messenger instance. (Like option 2, but using free 3rd party infrastructure)
Use PRISM with DI.
If all of yours View-ViewModel code resides in the same DLL, you might consider using option 2, or 3.
If some of the View-ViewModel resides in different assemblies you might consider using option 4 which presents the EventAggregator design pattern.
Hope this help to get you on the right path.

Related

Add control to logical-tree parent

My Main Window is a relatively simple DockPanel:
<DockPanel>
<!--Bottom row-->
<Border DockPanel.Dock="Bottom">
<DockPanel DockPanel.Dock="Bottom">
<!--Detector Indicator-->
<views:DetectorIndicatorView DataContext="{Binding DetectorViewModel}" DockPanel.Dock="Left"/>
<!--Logo-->
<Image DockPanel.Dock="Right" HorizontalAlignment="Right" Source="/Resources/Images/Logo.png"/>
</DockPanel>
</Border>
<!--Main display-->
<views:TabControlView DataContext="{Binding TabsViewModel}"/>
</DockPanel>
Inside the TabControl's SelectedContent there's a viewmodel for each tab, and each one of those can host a variety of viewmodels.
At one point, the tree basically looks like:
<MainWindow>
<TabControl>
<ExamTab>
<EditExam/>
</ExamTab>
</TabControl>
</MainWindow>
And on the EditExam page, I want a toolbar to appear in the Bottom row of the MainWindow. The toolbar buttons will be bound to commands ont he EditExamViewModel.
Is there a way I can "inject" an inner control like this into the "outer template" (i.e., the main window)?
The only way I can think of is to take the bottom row out of the MainWindow and paste it into each individual view, identical except for the one instance in EditExamView. Is that the only way?
Is there a way I can "inject" an inner control like this into the "outer template" (i.e., the main window)?
No, there is not, at least not in XAML.
You could it programmatically by for example getting a reference to the parent window in the view using the Window.GetWindow method, or by raising an event or send a message from the "tab" view model to the window view model or window using an event aggregator or a messenger.
What you describe is common in modern applications. Even menus are often swapped depending on the active view. However, there is no built-in way of doing this in XAML or WPF. The easiest way to solve this is
[...] to take the bottom row out of the MainWindow and paste it into each individual view, identical except for the one instance in EditExamView. [...]
The hardest way is to develop all that is needed for this scenario yourself including
Creating views and view models as data context dynamically
A concept to define areas where your views should be injected
Swapping in and out dependent views including their view models
Communication between views for synchronization or updating
Managing activation and deactivation of your exam tabs
Custom commands that you can wire accross views to your tab view
...
This is a lot of code that you might not be able to write on your own. But you are not alone, because there are application frameworks intended to bridge the gap between UI frameworks and your code like Caliburn Micro or Prism. These frameworks enable you to skip a lot of boilerplate code and provide services and mechanisms for various issues, but at the cost of learning them and some customization to fit your needs.
Prism as an example provides the following solutions to the issues above
A dependency injection container
Regions that define areas within your application
A rich region navigation service
An event aggregator for communication
Region adapters with active awareness
Composite Commands that can be used across views
...
This looks overwhelming at first, but the more you become familiar with a framework of your choice, the easier application development gets, as you will reuse concepts and components. At this point I recommend you not to re-invent the wheel. At first, you could use the easy solution and start getting familiar with any framework and gradually migrate your application.
So actually I’ve come up with a pretty good and simple solution, which is simply:
<ContentControl Content=“{Binding TabControlViewModel.Tabs[SelectedIndex].CurrentViewModel.ToolbarViewModel}”/>
If the current view model doesn’t have a toolbar view model then it’ll just evaluate to null and show nothing. Then I can define a data template for ToolbarViewModel and we’re all set.
If I wanted to enable various pages to have different things in the bottom bar, I could have ToolbarViewModel be some more general type (and probably a different name), possibly whatever my view model base is, and then any page’s VM could be any type of view model I want, and I could set the DataTemplate. The only caveat would be I don’t think I could have more than one template for any given VM, but I could get around that by creating simple subclasses for each way I’d want it to render so I could have different templates.

Split view into two separate views

I'm building a WPF application which very simplified looks something like this:
I have an ApplicationView which holds the menu and a ContentControl.
The ContentControl binds to the property CurrentViewModel which is set by the menu and rendered by its related View (Views and ViewModels are coupled by DataTemplates defined in the App.xaml).
I found this approach on Rachel Lim's blog
So in this example my View contains a list of duties as well as a "Details" window of the currently selected duty.
This setup works fine, but I think my ViewModels are getting too fat!
The non-simplified version of this ViewModel is up at around 500 lines of code, for handling:
Initializing filters
Logic for filtering list
Displaying duty details
Add/Update/Cancel/Delete logic
Now I'm very new to WPF but that seems like too much code, yea?
And it will be even bigger before I'm finished with it.
Anyways, I was thinking that I could split the ViewModel into two separate ViewModels; one for holding list and filters and one for showing the details. But how is this best accomplished?
I have thought of two approaches, but don't know which is preferable:
Create a DutyMasterView whose sole purpose is to hold two ContentControls for the actual Views (ie DutyListView and DutyDetailView each with their own ViewModel)?
I'm using MVVM Light as my framework so I suppose I could use the messaging service to tell the DutyDetailViewModel which Duty to display, right?
Alternately create a DutyMasterViewModel which exposes the selected duty.
Ditch the DutyMasterView and nest the DutyDetailView in the DutyListView.
Does it make sense to split my ViewModel into two or should I just stick with my fat ViewModel?
If splitting the ViewModel is recommended which of my suggestions makes most sense?
Are there other approaches that I should consider?
If you're still looking for opinion, I'd do it almost like you mentioned in point 1 but you don't need any messaging.
You create two VMs. Let's say DutiesVM and DutyDetailVM. DutyDetailsVM contains just some string properties for ID and Name.
In DutiesVM you create two properties:
ObservableCollection<DutyDetailVM> DutiesList
DutyDetailVM SelectedDuty
Your DutiesView can look like this:
<DockPanel>
<v:DutyDetailV DockPanel.Dock="Right" DataContext="{Binding SelectedDuty}">
<ListBox ItemsSource="{Binding DutiesList}" SelectedItem="{Binding SelectedDuty}"/>
</DockPanel>
Now you can create ListView ItemTemplate that binds to DutyDetailVM Properties.
is usercontrol that defines the DutyDetail view. Selecting the item in the list updates the details control automatically.
That's just the sketch but I think you can get the point from it.

MVVM, The best way to structure the View

I have made a root AppView, a root AppViewModel for my application as a container for all the things. Within the application view, I have a TabControl that each tab has its own task to do. One tab for imoprting data, one tab for issuing, one tab for management and etc:
App_View[Model] // root
{
TabTask1_View[Model], TabTask2_View[Model], TabTask3_View[Model] // tab items
}
1) In MVVM, Is it standard that I have grouped my whole views and view-models into the main application-view and application-model-view ?
2) In MVVM, The model should be implemented for every view & vm ? Or is it standard if I implement the whole models into one or two class files and share the model between them? I personally think that model part is not specific to a certain view, like the class 'student' that can be used anywhere in the code and is not restricted to a certain view. Based on this, if the models are general and shared, yet, is it good to follow the naming convention Class + 'Model' for it? like StudentModel? Is it helpful/necessary to add 'Model' after the general or shared class name like the thing I said?
3) In WPF, What is the best way to implement the Views? I want to edit and design very easily and without any restriction, and it should be standard enough to cover future needs. There are 4 things to use: Window, Page, UserControl and DataTemplate. Which one is the best option that you go for? UserControl or Page?
4) In WPF, How can I load the UserControl/Page(View) inside a tabItem dynamically at run-time based on the MVVM approach?
You're cheating. That's 4 questions!
1)
In terms of how you group your Views and Viewmodels I've seen people put views and viewmodels in the same namespace/folder and others separate them out into different folders based on functionality. The best option for you is what suits you/your team. There is no "right" way.
2)
Keep it DRY - so don't repeat yourself. It is perfectly sensible to reuse code. If you have common classes keep them common. As for naming, the name of the class should be helpful in explaining what it does: I'm sure you'd be able to figure out what the classes NavigationService, NavigationMenuItem and NavigationMenuView did and probably could put together a good mental model of how they relate. So - if naming a class BlahViewModel or BlahModel is useful to you, do it.
3) Implementing views:
A Window is always shown independently. Pages are intended for use in Navigation applications (usually with Back and Forward buttons, e.g. Internet Explorer). Pages must be hosted in a NavigationWindow or a Frame. If you're looking at dynamically adding/removing content, adding content to ItemsControls (TabControl, etc) then you'll want to be creating user controls. You can put user controls in Page and Window object, into other controls, etc, and are really the workhorse for WPF developers.
4)
You have a number of options here:
1)The quick and dirty way is to create DataTemplate which, on being given a ViewModel of type X, load up and apply the ViewModel to their data context. This will allow you to inject a ViewModel directly into a control and have the correct View render.
An example:
View.xaml
<ContentControl Content="{Binding Error, Mode=OneWay}" />
ViewModel:
private void ReceiveError(ErrorViewModel errorModel)
{
//if (errorModel.AcceptCommand==null || errorModel.AcceptCommand is NoOpCommand)
errorModel.AcceptCommand = new DelegateCommand(ClearError);
Error = errorModel;
}
public ErrorViewModel Error
{
get { return _error; }
set
{
_error = value;
_propertyChangedHelper.NotifyPropertyChanged(this, () => Error);
}
}
Styles.Xaml (ResourceDictionary)
<DataTemplate DataType="{x:Type vm:ErrorViewModel}">
<DataTemplate.Resources>
<conv:CustomisableBooleanToVisibilityConverter x:Key="VisibilityConverter" TrueValue="Visible" FalseValue="Collapsed" />
</DataTemplate.Resources>
<Popup AllowsTransparency="True" PopupAnimation="Fade" Placement="Center" StaysOpen="True"
PlacementTarget="{Binding Mode=OneWay, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type v:ModuleView}}}"
IsOpen="True" Width="400" SnapsToDevicePixels="True"/>
So you can see I'm injecting my viewmodel directly into the content control and it's using the data template that is bound to the viewmodel's type to find a View for it.
2)
A better bet is to use a DataTemplateSelector. This basically allows you to specify which templates are available for a control and then uses logic you code to determine which datatemplate to use. You can find an example of this here.
3)
Use a framework that abstracts the UI controls away. Microsoft has a framework (free) that does this called Prism. Basically, instead of adding your usercontrols directly to a TabControl,ItemsControl, etc you add your control to a named "Region". This region is mapped to an underlying control and an adaptor is put in place to manage how that UserContorl is added/removed when you ask it to be. You can find an in-depth discussion of this here. Beware, though, Prism is an application framework so implementing this isn't 3 hours work.
Here is not an answer, it's my experience that i'll explain to you and how I handle with MVVM. I started with WPF 3 month ago and I handle with it.
For each new theme/menue/option I create a new project file wich contains the ViewModels and the Views. All Business classes are collected in one Project file because I may have to use it in more than one ViewModel.
Yes, it was helpful for me as beginner to name the the classes ...ViewModel and ..View. It maked it easy for me to separate the diffrences and also it was/is easier to explain other people your classes ( for example if u have a problem with your coding )
I am using UserControls for our different views and I load them into ContentControls and TabControls without any problems.
Have a look at Prism for MVVM-pattern.
Point 1:
It depends. There are two widely used approaches AFAIK. First is as you've said group all VM's that constitute the same Window with direct dependencies to form a class structure that shows your actual program structure. Second is where you use an EventAggregator(Prism) / Messenger(MVVM Light) to loosely link the VM's instead of direct dependency.
Now both approaches have their benefit's
With the first one It's pretty easy to identify your program structure cos your VM dependencies show it clearly, which is not so clearly visible from the second approach.
Second approach helps you a lot when unit testing VM's cos you are not forced to either mock / work-around all the dependent VM's, It also helps code re-factoring a bit when changing project structure(think of "Plug in" classes)
oh and these ^^ are by no means exclusive. You can mix these together well and fine.
Point 2:
Models do not have any recommended 1 <-> 1 relation with a View / VM like what Views have with VM. Models just hold your Business Logic. I've had apps that sometimes do not hold a Model at all. Something there is just 1 Model used by the entire Application(when the back-end is say a c++ library and you just interface with it with a C++/CLI Module). yes maintain the naming convention to append Model class names with "Model"
Point 3
How about all of them? use them where applicable. Do not take a partial preference to any. When a View composes multiple other sections that are by themselves a View with VM I'd have a DataTemplate with the Data a UserControl. Your app almost always uses a Window and Page is useful for navigation based apps I think. Think Page's are what I've used least tbh.
Point 4
This is a question of tutorial's. Take a bunch of examples, see how it's implemented, reason it and pick your approach. If your using VS2010 get MVVM In the box (It's great. no 2 ways about that. Really hope this could get to be updated for VS2012 if it's not already). For VS2012 check out Two Views MVVM CodeProject which shows the concept, you can then apply it to any ItemsControl of your choosing.
Finally atleast when you're starting up, PLEASE start off with using a MVVM helper library. I prefer MVVM Light <- that link has a couple videos by the author of the library showing some usages and you can find extensive help here on SO about it. If you want to do things yourself, learn the basics from it and then implement it yourself. if you take the high road from day-one it's just a much longer learning curve(Just my opinion)

WPF Prism Master Details form

I am creating an application using WPF which is using Prism framework.
I have created Shell form and defined regions in that for Toolbar, Menubar and Workspace.
In the workspace area, i am able to load the modules, but I got one requirement where I have to load Employee Master form showing all the employee list in grid. On double click of that employee row in grid it should navigate to the Employee details form. Here I am not supposed to use the Tab control. On double click of the employee grid the Employee Master form should get closed or unloaded and Employee details screen should get loaded.
Any suggestions from Prism Experts on this.
Thanks and Regards,
Harry
I find that in these scenarios, people are looking for places to "Prism-ify" their solution. Here's my rule-of-thumb for when to use EventAggregator in Prism:
The application is still useful whether or not a subscriber to the event exists
I cannot use a regular .NET event or other mechanism because the subscriber is defined in another module
Those are the only times I would use EventAggregator to solve a problem. Otherwise, I just use the mechanisms built into WPF. Specifically in a master/detail scenario, the two views are likely useful only together, making them logically the same view, rather than seperate views.
This being the case, I typically do something like this (I've omitted the appopritate DataTemplates in this scenario, but hopefully this is enough to illustrate you don't need anything fancy to solve this problem).
<ListBox ItemsSource="{Binding Turtles}" IsSynchronizedWithCurrentItem="True" />
<ContentControl Source="{Binding Turtles/}" />
This uses a simple WPF mechanism that displays a list of items in a collection and when the user selects an item, the value of "Turtles/" is changed to the item selected. Simple. No need to over-complicate things.
If you really feel like your scenario warrants an EventAggregator (fits with rules #1 and #2 above), then do so as simply as possible... listen to an event both raised by a view model and consume it from a view model (you are using MVVM, right?). Anything more is a headache.
I would use an eventaggregator for this or possibly the new VisualStateManager.
One of the traps I fell into with PRISM is overthinking the designs. I eventually stopped using it and ripped it out of my project and things have been going much nicer. Though I still use and love eventaggregator...but theres a big learning curve on the whole regions and viewstate thing...

How to implement menuitems that depend on current selection in WPF MVVM explorer-like application

I am new to WPF and MVVM, and I am working on an application utilizing both. The application is similar to windows explorer, so consider an app with a main window with menu (ShellViewModel), a tree control (TreeViewModel), and a list control (ListViewModel). I want to implement menu items such as Edit -> Delete, which deletes the currently selected item (which may be in the tree or in the list).
I am using Josh Smith's RelayCommand, and binding the menuitem to a DeleteItemCommand in the ShellViewModel is easy. It seems like implementing the DeleteItemCommand, however, requires some fairly tight coupling between the ShellViewModel and the two child view models (TreeViewModel and ListViewModel) to keep track of the focus/selection and direct the action to the proper child for implementation. That seems wrong to me, and makes me think I'm missing something.
Writing a focus manager and/or selection manager to do the bookkeeping does not seem too hard, and could be done without coupling the classes together. The windowing system is already keeping track of which view has the focus, and it seems like I'd be duplicating code.
What I'm not sure about is how I would route the command from the ShellViewModel down to either the ListViewModel or the TreeViewModel to do the actual work without making a mess of the code. Some day, the application will be extended to include more than two children, and I want the shell to be as ignorant of the children as possible to make that extension as painless as possible.
Looking at some sample WPF/MVVM applications (Karl Shifflett's CipherText, Josh Smith's MVVM Demo, etc.), I haven't seen any code that does this (or I didn't understand it).
Regardless of whether you think my approach is way off base or I'm just missing a small nuance, please share your thoughts and help me get back on track. Thanks!
There are some inherent issues with Josh Smith's implementation of MVVM. Take a look at Ward Bell's post on the subject: http://neverindoubtnet.blogspot.com/2010/03/mvvm-josh-smiths-way.html. You may want to take a look at some alternative MVVM frameworks such as Caliburn that take a ViewModel first approach and break this coupling.
The RelayCommand is just a way to get a command in your ViewModel that can be bound to your View.
I think I would be inclined to step back from all of the different MVVM architectural variations and sample apps, and just use good old OOD. Why not have a ViewModel base class of some sort (ie, DetailsViewModelBase) for TreeViewVm and ListViewVm. Put a DeleteCommand in there with CanDelete and Delete methods that have as much implementation as the subclasses share (or abstract if none), and a SelectedItem as well. Then bind the SelectedItem to the controls similar to the xaml below:
<ListView AlternationCount="2" MinHeight="250" MaxHeight="400"
ItemsSource="{Binding Projects.View}"
IsSynchronizedWithCurrentItem="True"
SelectedItem="{Binding SelectedProject, Mode=TwoWay}"
behaviors:SelectionBehavior.DoubleClickCommand="{Binding PickCommand}"
ItemContainerStyle="{StaticResource listingRowStyle}"
>
The key bindings being SelectedItem and IsSynchronizedWithCurrentItem.
HTH,
Berryl
I found a blog post by Kent Boogaart that describes what he calls an ActiveAwareCommand. This seems to do what I was looking for, although I haven't yet tried it. A comment on the post mentions Prism's IActiveAware as having similar behavior.

Resources