WPF MVVM with multiple nested UserControls/Models - c#

For last two years I started diving into programming with C#. After reading and watching tutorials I tried to make my own app but I'm stuck at the point to get everything glued together.
I have a MainWindow with a TabControl. The first tab is an Overview and works fine with my Model and ViewModel (BaseViewModel implemented with RaisePropertyChanged() calls).
The next tabs are more or less independent to eachother but they have multiple UserControls in there.
The UserControls are not only plain collections of TextBoxes and Buttons but also have (editable) ComboBoxes and ListViews which filled up by other Models.
XAML Pseudo-code:
<MainWindow>
<Grid>
<TabControl>
<TabItem1> Overview ViewModel </TabItem1>
<TabItem2> UserControl1 </TabItem2>
<TabItem3> UserControl2 </TabItem3>
... etc
</TabControl>
</Grid>
</MainWindow>
A UserControl can occur at least once or multiple times inside a TabItem (via Button Command, add/remove to/from ObservableCollection). Inside that UserControl a ListView (or another approach) can be filled with data bound to an underlying model.
I already tried to implement my UserControls with registered dependencies but I don't see how I could register a ListView inside the UserControl to be able to bind to it. So I switched to another approach of having a separate ViewModel for each "parent UserControl". At this point I'm also stuck because I cannot be sure the "child UserControls" or models are a subset of the parent UserControl.
So somehow I'm doing something wrong but I don't see what exactly.
I hope someone can provide some hints.
EDIT:
I've put an overview on how my data should be represented.
General Overview for Models in UserControls
Any ideas on how to implement those structures are welcome.

Related

How to use a button while also using a DataModel in WPF

I'm creating an application in WPF using MVVM. I have a tab called tab1, which is a UserControl and has an associated DataModel (but no View, and thus, no ViewModel). Within tab1's content, there is a ListView, inside which is a button. The problem is that I would like the button to work. It worked when tab1 had an associated Tab1ViewModel, but I am told it must have a DataModel and not a ViewModel. I don't believe DataModels can support commands, so does anyone know of a workaround for this? Or does there really just need to be a ViewModel for any nested control with buttons?
Not having a view-model associated with the view is "kind of" against the guidelines of MVVM.
I can think about two solutions :
If you are "allowed", you can inherit the DataModel and add some
"ViewModel functionality" (Like the button's command)
If you are "allowed", have a view-model that contains the data-model (in this method you lose the ability to listen property changes if bind your view to the nested data-model fields)
Add the event handler in view's code-behind (which is against the MVVM's guidelines)
These two solutions aren't that great so I suggest that you try to solve the issue that leads to the facts that you can't use a view-model with your view and solve that.

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.

WPF, Different UserControl Navigation on the same TabItem

http://s7.directupload.net/images/140511/66z6w2tq.jpg
As you can see on the picture I am explaining everything with a Schema, I have a TabItem and on this TabItem I want to change the DataContext everytime with a Button. What I mean,is that I want to navigate through different UserControls but to stay on the same TabItem from a TabControl. I dont know how to Navigate through different ViewModels and everytime my values not to change from my Elements. I also could not find an Example of the same situation. Any Help with a Code Snippet or a full example will be appriciated.
Thanks in advance.
WPF/MVVM
I think this is a good case for using pub sub with EventAggregator or MvvmLight's Messenger.
You can then loosly coupled notify the MainViewModel to exchange controls or views within a tab.
Basically you can notify another ViewModel with another scope to do certain changes and then subscribe to it.
A possible structure could be:
- A MainViewModel with a collection of TabItemViewModels
- A single TabItemViewModel could contain also Controls and nested ViewModels
If you want to change a tab send a ChangeTabMessage to the MainViewModel
If you want to navigate within a tab use also Messaging or navigate as you do today.
Check out the EventAggregator here: http://msdn.microsoft.com/en-us/library/ff921122.aspx
or check out MVVMligh Toolkit Messenger ...
HTH

Dynamically generating controls in WPF using Prism, MVVM, MEF

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.

Data binding to a UserControl

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.

Categories