Joined
·
19,572 Posts
Understanding what i call the MVVM "BIGPICTURE"
What i call "BigPicture" in MVVM is the ability to keep a good view of how your viewmodels are organized according to the way you design your apps. Each and every single UI control be it Window, button, canvas, grid or whatever you use in your application already contains the "DataContext" property. That property allows you to set a viewmodel for each and every single control no matter which one is but what happen if you have the structure on the above picture?. A rule in WPF is that all embedded objects of an object are going to get the same viewmodel by default. So for example if we set the viewmodel to the main window the child control as well as the child of child control are going to get the exact same viewmodel by default. What if you want different viewmodels for each control? you can easily do that by intializing an instance of your desired viewmodel on each custom control or just by adding the viewmodel to your desired control DataContext property. This is very important because you should keep an eye on both worlds and always keep in mind how you represent your logic in your viewmodels and where they are placed/used. Below i will show an example and while learning from it try to keep an eye were the viewmodels are placed or used to build the logic. With the time you're going to get used to it and you are going to get a better control of what you're doing or want to do.
At the end of the day you can go as hardcore as this in order to completely separate the logic from the view and even handle dynamic view no matter the situation required without affecting the logic. Not to mention making the whole logic re-usable:
Dynamics
Dynamics in a MVVM enviroment is very important so what you need to learn is to forget all the "Control.Child.add(myControl)" or even worst... "Control.Content = MyCustomControl". start doing it properly without worring what you're going to display. Obviously that's easy said than done if you don't know how things works in proper WPF coding and MVVM. Luckily enough WPF provides all the beauties you need to get the job done in a very wonderful way. The magic word is "Templates", templates are holders of a certain look that you can add to a content dynamically or statically. By declaring it directly in your control Template property you are statically defining what template to use. What if you want to fill several controls or a content of a certain container dynamically with different controls? in such a situation all you have to do is use the "ContentControl" which allows you to set content dynamically.
Now let's get to work... let's say we have a custom control where on the left side we have a menu and on the right we want to display different controls according to the tree item selected. Something like this:
To solve that problem we are going to create a viewmodel which is going to fill the Tree menu then create different templates that are going to display different custom controls in our Dynamic content. Now lets start the work!
First let's setup our grid by defining the column definitions and build the structure:
Now that we have the basic stuff setup let's proceed and create 3 custom controls with a lable telling us which control we are using.
Now that we got the controls let's add the namespace to the main window in order to be able to use the newly created custom controls:
Now let's add the different templates to our resource:
As you can see i added 3 different templates with a key that use all 3 newly added custom controls. Now that we have that let's proceed and prepare our structure. First of all we need to create 3 viewmodels for all 3 controls then we create a class that is going to represent an item of our menu:
On that file i created a enum(can be placed whatever you want) and a class with 3 properties(Type, Caption and DataContext). That class is going to represent the item and is going to provide the necessary information to interact with the items. Now that we have the item class let's edit the main viewmodel and add the necessary stuff to fill the menu as well as use the selected item to change the content of the ContentControl:
As you can see in the code i've created a collection to fill the menu and then a property for the selected item. If you check the XAML of the main window you are going to see that the ContentControl content property binds to "Content="{Binding SelectedContent}" " which is no other than the selected item. So far so good but now we need the last step and that's our glorious template selector. Let's create the selector class now:
What i do here is quite simple, i created a class that is derived of the "DataTemplateSelector" and override the virtual property SelectTemplate. By doing that i get the total control of the template. The class itself has 3 properties which are the 3 datatemplates i'm going to use and in the SelectTemplate method i cast the item parameter which should be of type MenuItemClass since is no other than a menu item. With a simple switch i fill the datacontext property if necessary to add the different viewmodels to each template. Now that we have that setup let's add the template selector to our xaml namespaces:
Now that we finally got the selector let's proceed and make the last changes to the resources:
As you can clearly see each control now binds to the "DataContext" property since it automatically use the content used. Remember the 3 properties we added to the TemplateSelector class? if you check the definition you can clearly see that i define all 3 here using the templates i defined in the resources. Now if you check the content of the main window you probably noticed this:
There i defined the template selector key from my resources and now i'm ready to fire the application!!!!
As you can clearly see each time i select an item the content on the right automatically loads the correct control. As a extra i used the eventbehaviour to listen in all 3 custom controls so when you click on them a test method is going to be called in the respective viewmodel... how cool is that!!!
Note:
I know that for a traditional coder this would be much of a trouble as it could be solved easily in the traditional way. However this approach allows you to create your logic separated from the UI and make your code totally re-usable so no matter how many changes you make to your UI or how many stuff you add your code is going to work properly. If you understood this tutorial properly and followed the ViewModel structure then you should know that it looks like this:
This is the end of this tutorial. I wanted to talk about the ViewModelManager but after thinking about it i thought it would be better to talk about this kind of stuff first. The reason for that is quite obvious as this example leads you to a problem that can only be solved easily with the ViewModelManager which is the topic i'm going to talk about in the next tutorial.
Check the sample project on this thread, enjoy and have fun!
Ciao, Ciao!!! {handclaps}
What i call "BigPicture" in MVVM is the ability to keep a good view of how your viewmodels are organized according to the way you design your apps. Each and every single UI control be it Window, button, canvas, grid or whatever you use in your application already contains the "DataContext" property. That property allows you to set a viewmodel for each and every single control no matter which one is but what happen if you have the structure on the above picture?. A rule in WPF is that all embedded objects of an object are going to get the same viewmodel by default. So for example if we set the viewmodel to the main window the child control as well as the child of child control are going to get the exact same viewmodel by default. What if you want different viewmodels for each control? you can easily do that by intializing an instance of your desired viewmodel on each custom control or just by adding the viewmodel to your desired control DataContext property. This is very important because you should keep an eye on both worlds and always keep in mind how you represent your logic in your viewmodels and where they are placed/used. Below i will show an example and while learning from it try to keep an eye were the viewmodels are placed or used to build the logic. With the time you're going to get used to it and you are going to get a better control of what you're doing or want to do.
At the end of the day you can go as hardcore as this in order to completely separate the logic from the view and even handle dynamic view no matter the situation required without affecting the logic. Not to mention making the whole logic re-usable:
Dynamics
Dynamics in a MVVM enviroment is very important so what you need to learn is to forget all the "Control.Child.add(myControl)" or even worst... "Control.Content = MyCustomControl". start doing it properly without worring what you're going to display. Obviously that's easy said than done if you don't know how things works in proper WPF coding and MVVM. Luckily enough WPF provides all the beauties you need to get the job done in a very wonderful way. The magic word is "Templates", templates are holders of a certain look that you can add to a content dynamically or statically. By declaring it directly in your control Template property you are statically defining what template to use. What if you want to fill several controls or a content of a certain container dynamically with different controls? in such a situation all you have to do is use the "ContentControl" which allows you to set content dynamically.
Now let's get to work... let's say we have a custom control where on the left side we have a menu and on the right we want to display different controls according to the tree item selected. Something like this:
To solve that problem we are going to create a viewmodel which is going to fill the Tree menu then create different templates that are going to display different custom controls in our Dynamic content. Now lets start the work!
First let's setup our grid by defining the column definitions and build the structure:
Code:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="30*"/>
<ColumnDefinition Width="70*"/>
</Grid.ColumnDefinitions>
<Grid Grid.Column="0" Background="Red">
<ListBox ItemsSource="{Binding Menu}" SelectedItem="{Binding SelectedContent, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
<ListBox.ItemTemplate>
<DataTemplate>
<Label Content="{Binding Caption}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
<Grid Grid.Column="1" Background="LimeGreen">
<ContentControl Content="{Binding SelectedContent}"
ContentTemplateSelector="{StaticResource TemplateSelector}"/>
</Grid>
</Grid>
Now that we got the controls let's add the namespace to the main window in order to be able to use the newly created custom controls:
Code:
xmlns:controls="clr-namespace:MVVMTutorial.Controls"
Code:
<Window.Resources>
<DataTemplate x:Key="Control1">
<controls:CustomControl1/>
</DataTemplate>
<DataTemplate x:Key="Control2">
<controls:CustomControl2/>
</DataTemplate>
<DataTemplate x:Key="Control3">
<controls:CustomControl3/>
</DataTemplate>
</Window.Resources>
Code:
using AruanTuber_DarkMoon.ViewModels;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MVVMTutorial.ViewModels
{
public enum ControlType
{
Control1 = 0,
Control2 = 1,
Control3 =2
}
class MenuItemClass : ViewModelBase
{
// Type of the control to use(this is just a flag and not tied to the control)
public ControlType Type { get; set; }
// Text to display
public string Caption { get; set; }
// DataContext(viewmodel) to use
public object DataContext { get; set; }
}
}
Code:
class MainWindowViewModel : ViewModelBase
{
private ObservableList<MenuItemClass> menu;
private MenuItemClass selectedContent;
public MenuItemClass SelectedContent
{
get { return selectedContent; }
set { SetProperty(ref selectedContent, value); }
}
public ObservableList<MenuItemClass> Menu
{
get { return menu; }
set { SetProperty(ref menu, value); }
}
public MainWindowViewModel()
{
menu = new ObservableList<MenuItemClass>();
FillMenu();
}
void FillMenu()
{
Menu.Add(new MenuItemClass()
{
Type = ControlType.Control1,
Caption = "My Control1"
});
Menu.Add(new MenuItemClass()
{
Type = ControlType.Control2,
Caption = "My Control2"
});
Menu.Add(new MenuItemClass()
{
Type = ControlType.Control3,
Caption = "My Control3"
});
}
}
Code:
public class TemplateSelector : DataTemplateSelector
{
public DataTemplate Control1 { get; set; }
public DataTemplate Control2 { get; set; }
public DataTemplate Control3 { get; set; }
public override System.Windows.DataTemplate SelectTemplate(object item, System.Windows.DependencyObject container)
{
var itemClass = item as MenuItemClass;
if(itemClass != null)
{
switch(itemClass.Type)
{
case ControlType.Control1:
if (itemClass.DataContext == null)
itemClass.DataContext = new Control1ViewModel();
return Control1;
case ControlType.Control2:
if (itemClass.DataContext == null)
itemClass.DataContext = new Control2ViewModel();
return Control2;
case ControlType.Control3:
if (itemClass.DataContext == null)
itemClass.DataContext = new Control3ViewModel();
return Control3;
}
}
return base.SelectTemplate(item, container);
}
}
Code:
xmlns:selector="clr-namespace:MVVMTutorial.Templates"
Code:
<Window.Resources>
<DataTemplate x:Key="Control1">
<controls:CustomControl1 DataContext="{Binding DataContext}"/>
</DataTemplate>
<DataTemplate x:Key="Control2">
<controls:CustomControl2 DataContext="{Binding DataContext}"/>
</DataTemplate>
<DataTemplate x:Key="Control3">
<controls:CustomControl3 DataContext="{Binding DataContext}"/>
</DataTemplate>
<selector:TemplateSelector x:Key="TemplateSelector"
Control1="{StaticResource Control1}"
Control2="{StaticResource Control2}"
Control3="{StaticResource Control3}"/>
</Window.Resources>
Code:
<ContentControl Content="{Binding SelectedContent}"
ContentTemplateSelector="{StaticResource TemplateSelector}"/>
As you can clearly see each time i select an item the content on the right automatically loads the correct control. As a extra i used the eventbehaviour to listen in all 3 custom controls so when you click on them a test method is going to be called in the respective viewmodel... how cool is that!!!
Note:
I know that for a traditional coder this would be much of a trouble as it could be solved easily in the traditional way. However this approach allows you to create your logic separated from the UI and make your code totally re-usable so no matter how many changes you make to your UI or how many stuff you add your code is going to work properly. If you understood this tutorial properly and followed the ViewModel structure then you should know that it looks like this:
This is the end of this tutorial. I wanted to talk about the ViewModelManager but after thinking about it i thought it would be better to talk about this kind of stuff first. The reason for that is quite obvious as this example leads you to a problem that can only be solved easily with the ViewModelManager which is the topic i'm going to talk about in the next tutorial.
Check the sample project on this thread, enjoy and have fun!
Ciao, Ciao!!! {handclaps}
Attachments
-
125 KB Views: 276