Next Generation Emulation banner
1 - 20 of 28 Posts

· Premium Member
Joined
·
19,572 Posts
Discussion Starter · #1 · (Edited)
Understanding what i call the MVVM "BIGPICTURE"
Line


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:

Font


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:
Rectangle Square


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 have the basic stuff setup let's proceed and create 3 custom controls with a lable telling us which control we are using.
Text Line Font Diagram


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"
Now let's add the different templates to our resource:
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>
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:
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; }
    }
}
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:
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"
            });
        }
    }
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:

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);
        }
    }
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:
Code:
xmlns:selector="clr-namespace:MVVMTutorial.Templates"
Now that we finally got the selector let's proceed and make the last changes to the resources:
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>
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:
Code:
<ContentControl Content="{Binding SelectedContent}"
                            ContentTemplateSelector="{StaticResource TemplateSelector}"/>
There i defined the template selector key from my resources and now i'm ready to fire the application!!!!
Rectangle Paper product Square

Text Rectangle Line Square Paper product

Text Rectangle Line Square Paper product


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:
Text Rectangle


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

· Premium Member
Joined
·
19,572 Posts
Discussion Starter · #3 ·
Thanks Ruantec
... Good things come to those that wait eh!
I'll start a new project and play with above.
Once again thanks for all your efforts in sharing your knowledge.
Glad you like it!

This tutorials aren't just to share my knowledges with others but also to improve myself while at it and to show the world that apps can be properly written in a very wonderful way. My actual concept have a flaw since the very beginning which i'm aware of but it didn't had high priority so i didn't took the time to fix it. While writing this tutorial series i just figured out how to fix that so i will also introduce that on my next tutorial. The flaw i'm talking about is the event auto-generation in the EventBehaviour class. I already fixed that problem on my side but if you check the concept closely you will notice that is going to generate more events than required. To prevent that i've made a secondary dependency property tied to the EventType enumeration where you can choose which event to generate while using the current "MethodParam" if you want to call a method :)

I know this may come as a surprise to you but so far we are still covering the basics(basically the end of it), i haven't started with the advanced stuff yet so prepare yourself for more ;)
 

· Registered
Joined
·
65 Posts
Gulp.... Well with your "Basics" Ive managed to reduce my coding by 3/4 not to mention reduce the complexity. Now that I've started migrating my project over, I'm not wanting to re-write anything I do from here one (well maybe a few things ;) ) Just a quick one... while setting up my classes etc. I've discovered that I can reduce query repetition regarding haveing a datagrid populate via DataContext as per past samples (<DataGridTextColumn Header="User ID" Binding="{Binding UserID}" Visibility="Visible" />) and to edit the data I've used the datagrid element property (<TextBox Text="{Binding ElementName=dataGridUsers,Path=SelectedItem.UserID}" />)...

Is this a good way of doing it or should I create other class to hold induvidual user data for display in textbox based on "SelectedItem"?


.... Or should I wait till forthcoming attractions and just do my UI and data population for now?
 

· Premium Member
Joined
·
19,572 Posts
Discussion Starter · #5 · (Edited)
Gulp.... Well with your "Basics" Ive managed to reduce my coding by 3/4 not to mention reduce the complexity. Now that I've started migrating my project over, I'm not wanting to re-write anything I do from here one (well maybe a few things ;) ) Just a quick one... while setting up my classes etc. I've discovered that I can reduce query repetition regarding haveing a datagrid populate via DataContext as per past samples (<DataGridTextColumn Header="User ID" Binding="{Binding UserID}" Visibility="Visible" />) and to edit the data I've used the datagrid element property (<TextBox Text="{Binding ElementName=dataGridUsers,Path=SelectedItem.UserID}" />)...

Is this a good way of doing it or should I create other class to hold induvidual user data for display in textbox based on "SelectedItem"?


.... Or should I wait till forthcoming attractions and just do my UI and data population for now?
Glad i could be of help ;)

Binding to a Element that only exists in your UI is a great practice and also make sense to avoid un-necessary creation of classes, i personally see no problem in doing that. On my upcoming tutorial i'm going to handle some fun stuff on how to extend the EventBehaviour to call events and pass parameters totally independent of the UI and also independent comunications between viewmodels using the ViewModel Manager and EventBehaviour. Comunications between viewmodels using the ViewModelManager is a great thing specially when you have different user controls that have different viewmodels where one control react according to the changes of another control viewmodel.

I personally believe that the best way to improve yourself is by writing your code "YOUR" way and learning by the mistakes you do in the process. As mentioned i myself have learned a lot while writing the tutorials as i spotted issues that i overlooked in the past. One thing that i find funny at the moment is the fact that when i look at popular MVVM implementations out there i see a bunch of bloated stuff with hundreds of un-necessary lines of code and limitations not to mention over complicated at the end... specially behaviour scenarios and multiple command bindings.
 

· Registered
Joined
·
65 Posts
Hi Ruantec... Slowly migrating my code. I'm on a set of radio buttons at the moment and was needing some direction. I've got PermitType with 2 options either CWP or HWP. I'm storing the 3 letters as such in the database. I was thinking of using 2 vars called isTypeCWP and isTypeHWP each within a case statement in VM set to 1 or 0 dependant on db values... but I'm thinking that I'm going to get stuck with this when the user either checks or unchecks it... not sure on how to get to reflect this within the DataGrid and then back into the database as this would reverse the IsChecked into the 3 lettered acronym. Should I be making use of "behaviours" similar to what you used for your navigation in no 4 tutorial to reset these values and would the same principal work for more than 2 radio buttons
 

· Premium Member
Joined
·
19,572 Posts
Discussion Starter · #7 ·
I'm not sure if i understand correctly what you are doing there... are you trying to handle options via radio buttons that are stored in the db according to the one you choose? i will give another read to your post tomorrow... it's probably late and my brain need some rest after coding the whole day :p
 

· Registered
Joined
·
65 Posts
Hi Ruantec...
I decided to move my radio button options to combobox which workd ok but have hit a similar snag regarding checkboxes. I'm now trying to show/hide a tabitem. Options on tabitem visibility are collapse/visible/hidden. I need to collapse the TabItem if checkbox is not checked or make visible when checked. The checkbox is bound to database value of either true/false. How do I go about converting my true/false to visible/collapsed but still maintain its database value of true/false. Hope I'm not over explaining my needs here. What I've tried was to manipulate the datagrid cell value via a boung UI textbox and it works if I edit the value by hand... not sure how to replicate in VM by checkbox true/false. I've been using your behaviours method on the checkbox and remembered that you said you will be updating that method in next tutorial, is that correct?

I've tried googling for a proper way of doing this but nothing of good structure is coming up. Best one is http://stackoverflow.com/questions/11064888/mvvm-binding-to-check-box-if-checkbox-checked but am not sure if this is the way to go.
 

· Premium Member
Joined
·
19,572 Posts
Discussion Starter · #10 · (Edited)
Ups... almost forgot to reply..... you can use an option or checkbox but you will have to update a property for that. Basically you can solve the problem by extending the eventbehaviour class to accept a custom flag property for options and then set the value in your viewmodel. The solution in your link is ok but it's not gonna work always and in my opinion not properly solved(it kinda does the trick tho.). If you're looking to show/hide tab items you can do that differently and using checkboxes could work too. For instance the "isChecked" property of the checkboxes is a boolean so you can bind them in your ViewModel and by altering the collection of your tab source you can decide whatever a tab is visible or not. Remember that each item of your tab source collection represents a tab so each tab viewmodel is a item of your collection which means you can add properties there to show or hide stuff. On every change you can obviously save the changes to your database and when necessary load it back again.

It should be pretty easy to solve tho.
 

· Premium Member
Joined
·
19,572 Posts
Discussion Starter · #11 · (Edited)
So... it seems like you're having a hard time trying to figure out how to deal with options in MVVM right? i was giving you a bit of time to see if you can figure it out by yourself after my last tips. I know it looks kinda difficult to solve but you will wonder how easy the solution is. I'm going to show you a very nice method to handle options in MVVM by extending the already existent eventbehaviour class to do that.

First of all you are going to need a enumeration to represent your options:
Code:
[Flags]
    public enum Options
    {
        None = 1,
        Option1 = 2,
        Option2 = 3,
        Option3 = 4
    }
By specifying this enum as flags i'm providing the XAML with the necessary info to auto-complete when the property is being set in XAML. This enumeration is the base of options available i want in my personal case but feel free to change it as you want. Next we need to implement two dependency properties where one is going to be the property to bind to(you will see later) and the second one is the one we specify for each element:

Code:
        public static readonly DependencyProperty OptionProperty =
          DependencyProperty.RegisterAttached("Option", typeof(Options),
          typeof(EventBehaviour), null);

        public static void SetOption(DependencyObject d, Options value)
        {
            d.SetValue(OptionProperty, value);
        }

        public static readonly DependencyProperty OptionParameterProperty =
          DependencyProperty.RegisterAttached("OptionParameter", typeof(Options),
          typeof(EventBehaviour), new PropertyMetadata(IsOptionChanged));
     
        public static void SetOptionParameter(DependencyObject d, Options value)
        {
            d.SetValue(OptionParameterProperty, value);
        }
     
        public static Options GetOptionParameter(DependencyObject d)
        {
            return (Options)d.GetValue(OptionParameterProperty);
        }
     
        private static void IsOptionChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var option = d as FrameworkElement;
            if (option != null)
            {
                // Create events here
                option.PreviewMouseUp += option_PreviewMouseUp;
            }
        }
     
        static void option_PreviewMouseUp(object sender, System.Windows.Input.MouseButtonEventArgs e)
        {
            var element = sender as DependencyObject;
            if(element != null)
            {
                SetOption(element, GetOptionParameter(element));
            }
        }
As you can see the "Option" property is going to hold the current one while the "OptionParameter" is the one who is going to hold independent values. The parameter option has an event which is going to set the current option each time the user click on it. In this particular case i used the mouse up as a quick example but you can use whatever you want. Next is XAML of course and all you need to add are your radio buttons and use the newly added feature of the EventBehaviour class:
Code:
<StackPanel>
                    <RadioButton Content="Clear"
                                    behaviours:EventBehaviour.Option="{Binding SelectedOption, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
                                    behaviours:EventBehaviour.OptionParameter="None"/>
                    <RadioButton Content="Show Control 1"
                                    behaviours:EventBehaviour.Option="{Binding SelectedOption, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
                                    behaviours:EventBehaviour.OptionParameter="Option1"/>
                    <RadioButton Content="Show Control 2"
                                    behaviours:EventBehaviour.Option="{Binding SelectedOption, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
                                    behaviours:EventBehaviour.OptionParameter="Option2"/>
                    <RadioButton Content="Show Control 3"
                                    behaviours:EventBehaviour.Option="{Binding SelectedOption, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
                                    behaviours:EventBehaviour.OptionParameter="Option3"/>
                </StackPanel>
As you can see i'm now using two properties where the one is always the same and i bind to a property of my viewmodel called "SelectedOptions" which is of type Options. Obviously i use the TwoWay mode and the updatetrigger in order to reflect changes each time that property changes. Additionaly i use the "OptionsParameter" property to provide the desired option for each element.

Voila!!! everything now works and i believe is a good base to do whatever you please with options in MVVM. In case you want to see the code working i've attached a modified version of the part4 sample to this post. Take the sample as a reference for future extensions of the EventBehaviour class. Remember, that class is just a base so it wasn't meant to remain unchanged ;)

Have fun!!!!
Text Font Line Document


If you have any questions feel free to ask as usual.
 

Attachments

· Registered
Joined
·
65 Posts
Hi Ruantec... worked a treat. Regarding checkbox... I've opted to use the "BooleanToVisibilityConverter" to show/hide tabs (shoot me down if you think this is not good option)... One question regarding this... when the tab is hidden/collapsed, are all the controls on that tab still bound and if so how would I negate binding on these controls?

Thanks once again.
 

· Premium Member
Joined
·
19,572 Posts
Discussion Starter · #13 ·
The "BooleanToVisibilityConverter" is a good option but it only works for true/false. In the case of the show/hide tabs scenario is just perfect. When a tab is hidden the controls on the tabs are still bound but as far as i know if collapsed that isn't the case. Another way is by removing the control entirely like i do in the sample above by using a content control and datatemplates but you have to be careful as it could be slow at times depending on the UI complexity. Btw... great to see you're thinking in optimizations ;)
 

· Registered
Joined
·
65 Posts
Thanks for prompt reply... while motorbiking home I was thinking if I needed it to remain bound and remembered that in my old codebehind I had it bound just in case the user changed their mind and they didn't have to re-enter data. So all good to maintain bindings. Still can't figure out how to save my data back to db though... but will get there once I get time to look at it.
Once again thanks... will keep you updated as how I'm going... :cool:
 

· Premium Member
Joined
·
19,572 Posts
Discussion Starter · #15 ·
To save the data back to your database you can simply create a button with a function to store the changes back to the database by commiting changes. Alternatively you have two options:

Option 1:
Call a method when the "SET" of your property changes.

Option 2:
Hook to the "PropertyChanged" event of your viewmodel and write a switch to the listener in order to save your changes.


Changes can only be triggered by actions so you either have a button to save or the two alternatives above. Another alternative is by implementing the IManageable interface in your viewmodel which will automatically add it to the viewmodel manager. Whatever you are in your application you can call the ViewModelManager.SaveSettings() method which will automatically call the "SaveSettings" method on all viewmodels implementing the IManageable interface ;)
 

· Premium Member
Joined
·
19,572 Posts
Discussion Starter · #17 · (Edited)
It doesn't matter where you are at on your code or which viewmodel call it. If you check the interface implementation you will see that is quite basic yet quite helpful. Basically all it does is each time a viewmodel is created(check viewmodel base) it checks if the interface is implemented and add the instance to the viewmodel manager. By doing that methods like "Save" or "Get" are available which allows you to get an instance of any viewmodel that implements that interface anywhere by just using the "ViewModelManager.GetViewModel()" method. The manager not just call the save method on each viewmodel but also with a simple converter implementation it allows you to add viewmodels to any UI everywhere and anytime!. That is actually one of the topics i want to explain in the next tutorial as is quite helpful to display values everywhere regarding where the viewmodel is used.

Keep in mind that the interface must be implemented in main viewmodels only and not in items viewmodels of a element collection for example. If you keep that in mind it should work perfectly. The interface is just a basic example tho. so you can go ahead and extend it as you wish or just implement your own interfaces if you want. The reason why i did that is just to provide a basic idea on how people can provide global implementations etc.
 

· Registered
Joined
·
65 Posts
Hi @Ruantec
Was reluctant to ask as I know you are very busy and don’t really want to be tackling anyone else’s problems, but I’m stuck on three aspects of my code. They are:
  1. Obtaining info/values from parent VM - still struggeling. I've managed to get it to UI but can't seem to use it in VM (you suggested using IManager)
  2. Validation – no updates until all validation rules are met (can execute)
  3. Saving data back to database. I know how to save data but it’s getting all the updated fields on parent and all children VM’s
I know you are coding up your next tutorial regarding IManager, but how much of the above will be covered in next tutorial?
 
1 - 20 of 28 Posts
This is an older thread, you may not receive a response, and could be reviving an old thread. Please consider creating a new thread.
Top