UserControl, ContentControl and MVVM - wpf

I'm kind of a beginner with the WPF platform. I've done a few Windows Forms apps that were very basic and now I'm working on a much more complex app for my current work.
I'd like to implement an MVVM model, but I'm getting lost in the different articles on how to do so.
Here's a screenshot of the app interface:
The section on the left is a ListView containing 6 sections that correspond to different UserControl. I would like the section on the right to display the UserControl that corresponds to the selected Item.
Each UserControl is stored in a separate XAML file.
By looking online, I found that I should use a ContentControl in my MainWindow, but every attempt I've made has been unfruitful. I know there's more than one way to skin a cat.
Which method would you use? Do you have any concrete examples or sources where I can find how to make it work?

The only difference between UserControl and ContentControl is the OnCreateAutomationPeer method. This method is responsible for UI automation.
Although this method is rarely used in practice, it is customary to use UserControl to represent some complex data types.
A typical use for a UserControl is to retrieve data (in normal CLR types) through the data context.
And the Content property specifies (using XAML) a visual tree to represent that context.
And each such UserControl is declared as a derived type (just like a Window).
If necessary, additional properties (CLR and DP) and other members (event handler methods, for example) are added to such a UserControl.
The base class ContentControl itself and others of its successor are used somewhat differently.
The data in them goes directly to the Content property.
And their visualization is set by the data template in the ContentTemplate property.
While DataTemplate's can be quite complex, they are usually much simpler than the XAML markup of the UserControl.
And, besides the task of visualization, you cannot add additional logic (properties, fields, methods).
Here's a photo of the app interface: ...
In this UI, I don't see where the ContentControl can be applied.
On the left is a navigation bar consisting of buttons.
It is usually implemented directly in the Window from an ItemsControl or ListBox.
On the right (while an empty area) is the region for the page content.
Usually, when you click on a button in the navigation bar, the corresponding UserControl is set to this region.
At a lower level, deeper in the visual tree, for smaller elements, it is quite possible that a ContentControl is needed.
P.S. Just in case, I will clarify, in view of the comment below.
By area for pages, I do not in any way mean the use of Frame + Page.
This is a very specific pair and it is extremely rarely justified to use it.
In combination with MVVM, its use is even more difficult, since this pair does not support DataContext inheritance.
And this is the main way for the View to interact with the ViewModel.

Related

Mvvm light wpf navigation

What I want to achieve. A navigation service for MVVM Light WPF where I can easily say NavigateTo(View URI or ViewModel and a string name of the framework element that should present the View or ViewModel).
I first tried the View First approach from here.
https://stackoverflow.com/a/28968560/5272185
Problems I found with it was that the Frame frameworkelement seems to run sandboxed, and even though I set the view's datacontext to bind to a viewmodel retrieved from my viewmodellocator I seem to get a different instance inside the Frame. Also styles seem not to be inherit down into the Frame and the MVVM Light Messenger's messages do not seem to bubble up from the View shown inside a Frame.
Also the code to find the Frame element failes if the Frame is located within a ViewBox. I have no clue why but if I just remove the ViewBox it finds it correctly.
I ignored the ViewBox issue (removed the ViewBox) and changed the NavigationService to store ViewModelBase types instead of URI, and I changed the code to find a FrameworkElement of type ContentControl instead of Frame, set the ContentControl's content instead of a Frame's source. This so I should not run into the sandbox problem. This worked, but I now have no good way of switching to a detail view (using the same ViewModel as datacontext for multiple views).
Questions
When using a ViewModel first NavigationService as explained above, is there an easy way to show a detail view (sharing a ViewModel) without code duplication?
In case of a View first navigationservice exactly like the code in the link. Is there a way to make the Frame inherit the correct datacontext and styles (not running sandboxed at all)?
Can someone explain why the VisualTreeHelper code in the provided link does not find a framework element if the element is inside a ViewBox?
I should also mention that I have tried the approach to make a CurrentVM property and CurrentDetailMode property on the ViewModel and bind a ContentControl's content to the CurrentVM property and used DataTemplates with DataTriggers (bind to CurrentDetailMode) to set the corresponding View(s) to the ViewModels. According to this post.
https://rachel53461.wordpress.com/2011/12/18/navigation-with-mvvm-2/
It works well and supports detail views. My goal with the navigationservice is that I want to add all views or viewmodels to the service in one place. Not having to create the CurrentVM and CurrentDetailMode properties on every ViewModel that should be able to show different content / views. And I would prefer an easier solution than manually setting up Datatemplates with special DataTriggers for supporting "Detail" views. Also the navigation service discussed has a history queue so one can go back.
I managed to solve / answer my own questions
Just create a dumb ViewModel class for the Detail view, provide it with the (Main)ViewModel instance and make that ViewModel instance a property so you can bind to it. Done! Easy after all, and with Dependency injection it can become a breath.
See this link page.DataContext not inherited from parent Frame?
I solved this one and tracked it in the following separate question. See answer here
WPF ViewBox blocks VisualTreeHelper search

Extending user controls in WPF

I have built a User Control composed, as usual, of a XAML part and a code-behind part. Now, I need to create another User Control which shares some of the functionalities of the former, but which has different looks and might also have additional features in the code-behind.
My first idea was to create an interface to gather the common functionalities of the two types of controls. Is this the right way to go? And how would I manage the different XAML parts I should have? Any advice is welcome.
I would like to add another contribution which I think might be useful to others encountering my same situation.
One other possible solution, which in my opinion is suitable if the controls you are going to create are not particularly complex, is to create a base User Control containing the common features that you want to share: such control will be written entirely in C#.
This, in fact, allows inheritance in User Controls composed of both XAML and code behind. In the XAML of the inherited control, rather than having
<UserControl> ... </UserControl>
You will have
<MyProject: MyBaseControl x:Class="MyProject.MyExtendedControl"> ... </MyProject: MyBaseControl>
and then in the code behind you will also need to specify the following:
class MyExtendedControl : MyBaseControl
Hope this helps.
User controls don't lend themselves well to inheritance in WPF, you can do some hacks to make it work, but overall you'll probably be better off using a Custom Control instead. It's easier to specify different XAML for inherited controls using this technique. The easiest way to get started with a custom control is to add a new item to your project of type "Custom Control (WPF)" and it will add the control and the XAML gets added to Themes/generic.xaml for you.
To inherit from this control, the easiest way is to add another new Custom Control (WPF) and then change the code behind to inherit from your parent control instead of Control.
There are a number of ways to split up the pieces to make them easier to work with.
MVVM, as mentioned in a comment, relies upon Data Binding to separate the input controls from logic. By setting the appropriate class which implements INotifyPropertyChanged into the DataContext of you control, you can change the behaviour. Your DataContext class or ViewModel could implement Visibility properties to show and hide different parts of the input if the differences are not too great between the uses.
Another way to break apart functionality is by Aggregation. See StackOverflow 269496
You could build smaller controls that implement common functionality and then build larger special purpose controls by combining the smaller controls (i.e. aggregating them) to make the larger control. Aggregation will work for both the code-behind and the Data Binding approaches.

Multiple "sibling" controls, one-at-a-time visible, MVVM

I've got what I think is a fairly simple problem in Silverlight, and I'd like to solve it using MVVM principles, largely as a way of enhancing my own understanding.
Let's say I have a simple LOB app that's designed to let me load up and edit a single Employee (just an example, for the sake of explanation). In my example, Employee is made up of a number of complex objects - an Employee has a ContactInfo, a Skillset, an EmploymentHistory, an AwardsEarned, etc. The idea is that I can have this app load up a single employee and get access to a number of different editor screens. Each component of an Employee has its own editor screen.
Visually, the app just ha a left-hand nav bar and a main view on the right side. The nav bar just lets me type in an employee number and load it into memory as the "active" employee. It has a simple list of links - clicking a link should load the appropriate editor screen on the right side.
There are a couple concepts that I don't think I understand well enough, and I'm having trouble proceeding. I know there's always more than one way to skin a cat, especially when it comes to WPF/Silverlight/XAML/MVVM, but I'm having trouble thinking through all the different concepts and their repurcussions.
View-First or ViewModel First
After much thinking about MVVM, what seems most natural to me is the concept of viewmodel composition that Josh Smith seems to promote in his often-quoted article. It seems like the idea here is that you literally model your UI by composing viewmodels together, and then you let the viewmodels render themselves via typed DataTemplates. This feels like a very good separation of concerns to me, and it also makes viewmodel communication very direct and easy to understand.
Of course, Silverlight doesn't have the DataType property on DataTemplates, to many complaints: one, two. Regardless, what I see promoted much more often than viewmodel composition is a more view-first design, where the viewmodel for the view is typically instantiated in the view's XAML or via a DI container, meaning that you can't hand it any parameters. I'm having a really hard time understanding this: how is a ViewModel supposed to serve a Model to a View if I never get to tell it what data is in the model? Reaching through a view to get to its viewmodel doesn't seem to make sense either. I'm very hazy in this area but it seems the accepted answer "use a mediator/lightweight messaging framework." I'm just going through some tutorials now on the messaging system in MVVMLight and I'll be looking at similar stuff, if for nothing else than simply to understand the concepts, but if anyone can shed some light on this I'd very much appreciate it. Anything involving Unity/Prism or MEF is valid, but I haven't gotten that far in my quest for knowledge yet :-)
Instantiating Views and Selecting Them
Theoretically (I say that because SL doesn't support DataTemplate DataType), the viewmodel composition approach could make this very simple. I could have the right side of the screen be a content control whose Content property is bound to a property called ActiveEditor. A parameterized command for the hyperlinks would set ActiveEditor to a given viewmodel.
With a more view-first approach, how would I proceed with this? The first thing that comes to mind is instantiating all of the controls in the XAML of the main view.
Is manipulating the Content property of a ContentControl a good way to go for this kind of situation, or am I better off doing something like setting visibility on each individual control?
The ViewModel (VM) is written so that it is 'wired up' to the Model but has no knowledge at all of the View - in fact, this is what makes it very good to unit test (see NUnit) as it has no idea, and less does it care, whether it is being used by a UI or a Test Framework.
The VM exposes public CLR properties which implement the ICommand interface, the View, having instantiated a VM using (generally speaking anyway) its default constructor, then binds its Buttons/Hyperlinks etc to these Command properties like. So, for example, you may have a VM that exposes a CloseCommand to exit the app, the View may contain a MenuItem that binds to that command, such as:
<MenuItem Header="E_xit" Command="{Binding Path=CloseCommand}" />
Now, the VM would also expose a public ObservableCollection of objects that you want to display in your UI. Whether you populate this ObservableCollection as part of the VM constructor, or whether you do it via a UI interaction (say another Command assigned to a Button click) is up to you but the end result is that you bind a View control to this exposed ObservableCollection in XAML like:
<y:DataGrid ItemsSource="{Binding Breakdown}"/>
or equivelant for whatever control you are using to display the data (not sure off the top of my head what elements a DataGrid has in Silverlight as opposed to WPF).
Meanwhile...: The Mediator pattern is used for VM's to interact with each other, not for the View to interact with the VM. For example, you might have a custom TreeView that has its own VM on the same View as the main chart screen. In this case you could use a Mediator for the TreeView's VM to communicate with the Charts VM.
As for the last bit of your question, I think set up a basic framework using Josh Smith way in the article you mentioned and use that method to add additional ViewModels to the right hand side of your silverlight app.
Hope that helps a bit at least.

When to use UserControl vs. Control in Silverlight?

I'm just getting my feet wet in Silverlight, and don't really understand the differences and pros/cons of creating a UserControl vs. creating a Control for the same task (as in when you right click on a selection in Expression Blend, for instance).
It seems like selecting "Make Into Control" just creates a new template for the base type you specify, whereas creating a UserControl creates a whole new base class. Is that correct?
In this particular instance, I'm creating a custom text box control that only takes numbers, and divides itself into 3 sections, storing 3 values into separate properties as pictured below. In this particular case, which would be best?
UserControls are meant to be a composite control - basically a bunch of other "controls" grouped together to work as a single, cohesive unit.
Custom Controls, on the other hand, are intended to be used as a single control. Think of the basic controls in the framework, such as TextBox or Button - if you were implementing something like that, you'd want a Control. (This is less common than UserControls, especially in WPF, since you can use templating on base class controls to accomplish quite a few things where you'd need custom controls in other frameworks). A custom Control is all about defining new behavior for a single "control."
If you consider your control to be a group of three text boxes then a UserControl would be appropriate, but if your control will still essentially be a TextBox then you should extend the existing control with "Make into control."
It sounds like you need a UserControl to me.
Dov, I think you've answered your own question with your update. Custom Controls are most useful when you want to make a control that supports templating. Otherwise they are useful when you are inheriting from other controls to cleanly add functionality (TextBox -> PasswordTextBox).

WPF - CAL - Multiple parents for single instance of control?

I am working on a PRISM / CAL solution, but the problem may be WPF specific:
If I create one instance of an control (e.g. TextBlock) and add it as child to a StackPanel, there is no way to add it as "child" to another StackPanel (parent already set error). I kind of understand the reason (it also occurs when using the RegionManager).
But what is the suggested way if a visual control is very complex and should be created only one time and used in two places? I agree that is does not really make sense to show an identical control 2 times on the screen, but there might be cases where it is useful (e.g. an "Close All" Button).
I know that in the button case, I should just create two buttons both databound to one ICommand. But does this rule also apply with much more complex controls (always create new instances)...
I stumbled on this problem when creating a layout switcher, which creates the button list and the stack panel for each GUI seperately, but uses a static ObservableCollection of buttons as source (which causes strange bugs)..
Any ideas on this topic?
Chris
This is normally handled by templates. That is, you abstract out the data into a particular type, and associate a template with that type. Then you place the instance of that data any number of times into your visual tree and have WPF render it with the template.
In short, don't add a TextBlock to your StackPanel. Instead, add an instance of your data type (eg. Customer) and associate a DataTemplate with the Customer type. There is no way to parent the same UIElement in multiple places.
You can add your control (or collection of controls) as a resource and refer to them via binding in your controls. This will implicitly create a copy (they will be Freezable and WPF will copy them).
Generally you should be using DataTemplates as Kent suggests, but if you have a special case, this will likely work.

Resources