Next Generation Emulation banner

Part 5 - New WPF/MVVM Tutorial(C#)

7441 Views 41 Replies 2 Participants Last post by  @ruantec
After quite a long time i've finally found some time to continue writing the tutorial serie. This time i'm going to keep updating this thread in the next few days in order to complete the part #5 so keep checking it if you're interested.

This time i'm going to show several topics that will help you to share information between views and viewmodels and those are:

- ViewModel Manager
- Converters
- Binding behaviour
- PropertyChanged listener
- Global exception handling(bonus :p)

The ViewModel Manager:
Since i started to write this tutorials you have probably come across the ViewModelManager class and the IManagable interface. As of now both of them are included in the projects provided by me but never used. First of all the ViewModelManager class is a basic implementation to share information between your viewmodel classes. A well written application usually doesn't necessary need to share information between viewmodels if the proper logic is used. However by experience i know that isn't the case in a hell bunch of complex applications as there is a time when you just need to share information between viewmodels.

How is that done? quite simple. If you check the ViewModel base class you will quickly spot this code in the constructor:

Code:
var parseable = this as IManageable;
if(parseable != null)
{
      parseable.InstanceID = Guid.NewGuid();
      ViewModelManager.AddViewModel(this);
}
What we do here is cast the current viewmodel as IManageable. If the current viewmodel implements the IManageable interface it's going to automatically be added to the ViewModelManager list. The interface itself is very basic as well since it should serve as a template for your further implementations. Now if you check the ViewModelManager class you will find out that as mentioned is also a basic implementation of what a manager should be. The class is very straightforward and as you can see it implements a list of viewmodelbase items and offer few methods to get or add viemodels. Remember, the base of each viewmodel is the viewmodelbase ;)

The ViewModelManager class as you can notice is static and i did that on porpuse. Being static means you can access it anytime as long as the namespace is included on your viewmodel. Now that we know what the porpuse of the ViewModelManager class is let's now explain how it works. The manager in order to work properly needs to be implemented correctly and for that you will have to keep this in mind:

Text Line Font Design Parallel


In the scenario above we have an application that has several viewmodels. Each viewmodel is unique and represent the main logic of each element(main, item list control, child control, child control of child). Elements provided by controls such as items are obviously viewmodels too but those should not implement the IManageable interface but only the main ones. If you check the ViewModelManager implementation you can see that each element added to the manager must be unique and not of the same type(at least in my basic implementation).

Now let's make the following scenario... the Child control of child viewmodel is called "ChildOfChildViewModel" and the Child control viewmodel is called "ChildViewModel". Let's say i'm currently in the ChildOfChildViewModel class and want to access a property of the ChildViewModel class. There are different ways of doing that but the simple one is done as follows:

Code:
var childVM = ViewModelManager.GetViewModel(typeof(ChildViewModel)) as ChildViewModel;
if(childVM != null)
{
    // Voila! we have a reference to the parent viewmodel and can now access it's property.
}
By adding the code above you get access to the ChildViewModel if it implements the IManageable interface. The same can be done from any item viewmodel as well. There are also other ways to accomplish that but this is the simpliest one. I will cover other ways later in this tutorial when i talk about Binding behaviour and PropertyChanged listener.

Converters:
Logic between your code and UI. Basically what they do is provide a pre-determined information at binding time. How do you create one? pretty simple. Start by creating a class that implements the IValueConverter interface like this:

Code:
public class ManagerVMConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            Type type = Type.GetType(string.Format("MVVMTutorial.ViewModels.{0}", parameter.ToString()));
            return ViewModelManager.GetViewModel(type);
        }

        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            return null;
        }
    }
As you can see the interface contains two methods which are "Convert" and "ConvertBack". This time we are going to focus in the "Convert" method only. The method itself is very straightforward and provide you all the information coming from the UI object that implements it. How do we implement the ManagerVMConverter class in your UI? again, pretty simple:

Start by adding the namespace where the converter is located to your XAML code like for example:
Code:
xmlns:converters="clr-namespace:MVVMTutorial.Converters"
Now add the following to your Window or Control XAML resource like for example:
Code:
<Window.Resources>
        <converters:ManagerVMConverter x:Key="ManagerConverter"/>
</Window.Resources>
What we do here is declare a key to use the converter anytime. For example... if you want to add a certain viewmodel that was already registered in the ViewModelManager class you can do it directly on your XAML now without the need of code behind. Let's say you have a simple button and you want to use the "ChildViewModel" as DataContext. Normally you will just go to codebehind and do it but let's do it directly in XAML now:

Code:
<Button DataContext="{Binding Converter={StaticResource ManagerConverter}, ConverterParameter=ChildViewModel}"/>
As you can see here i'm using a button and directly binding the DataContext property to the Converter which use the previously created resource key. By adding the ConverterParameter of "ChildViewModel" i'm providing the type name of the viewmodel registered to use. After that the button is going to use the "ChildViewModel" class automatically. The same can be done on every single element and you can use the same viewmodel on all UI elements of your need without to modify the default viewmodel given to the main control. This step is very important when you want to display information that is stored in a viewmodel other than the one you are currently using on your current view. Since your button DataContext binding has changed you can now bind other properties of the control as usual using the given viewmodel property.

Isn't that cool??? this method saves you from having to get instances of foreign classes or even worst... having to write huge loops and methods just to access a single property. If used properly this method could bring a huge speed up by avoiding unnecessary code. At the end of this tutorial i will provide a project to show what i just explained. For the time being try to figure out yourself using my explanation.


To be continued in the next few days......... enjoy!
See less See more
1 - 20 of 42 Posts
There are few reasons for that so make sure to check it out:

1. ViewModel doesn't implement the IManageable interface.
2. ViewModel instance is created after the UI binding.
3. If you use the converter check the convert method and check the namespace string if correct for your needs.
To prevent such scenarios from happening you can instanciate required Viewmodels in your main application entry(where you instanciate your main viewmodel for example). Alternatively new instances can be created in the main viewmodel itself by just writing this:

new MyChildViewModel();

Usually you just have to check your project structure to avoid using stuff you haven't created yet. In case you run into such a situation just create a new instance as i said above or another way could be by implementing that line in your viewmodel manager.

When your binding use the converter to get the viewmodel it check if is already registered after the instance creation. If that isn't the case you can create the instance yourself by using the Activator to create a instance of the Child viewmodel. Remember that you already have the type name so it shouldn't be hard to implement in either the converter or viewmodel manager. In case you are having some hard time doing so i can show you later today how to do that ;)
See less See more
After checking it myself i think i will implement that in the GetViewModel method of the ViewModel Manager to prevent such situations from happening. At the same time i will make the need of "DataContext = new MyViewModel()" in codebehind useless as viewmodels can be always defined in XAML and that would be cool. See? even i learn in the process :D. I will toy around later and will upload a project to show that with the updated ViewModelManager class.
Each control can have separate datacontexts in fact every single UI control can have a different datacontext and it doesn't matter where is located. However when you have a combobox control for example you bind the datasource to a list which contains items. Each item on that list is the datacontext of each combobox item. If you want controls inside those items to use a different datacontext you have to define those in your item template by using my example of binding datacontext with the converter in XAML. The reason why you may get some issues at the beginning is because each item automatically gets the item as datacontext so you have to override that by defining a new datacontext on child elements. I will try to find time today or tomorrow to write a small sample application to show you that.

I don't know if that helps you but each component on your UI is a independent element that can have own logic behind a viewmodel. By default each UI element gets the parent datacontext if no other is defined.
See less See more
After re-reading your post i can't figure out why your content control is bound to a datasource... it shouldn't be that way as a content control serves only as a presenter. With that in mind you either have a defined class in your viewmodel to represent what is going to be displayed through templates or you have different classes that represents different templates to be shown but never a datasource. Somehow i don't quite get what you want to do here.
I've been terribly busy lately setting up my hybrid OSX/Windows/Linux enviroment for iOS/OSX/Android/Windows development. I will try to take some free time and complete the tutorial and include validation. The good news is that i may bring my MVVM concept to mobile as well so that everybody can use my concept and develope universal apps that runs the same code across all plattforms.... this is going to be fun :)
Fantastic to hear about iOS/OSX/Android/Win dev. Funny though, I was going to ask you about developing in MVVM across those platforms. I played with a plugin to vs 2010 C# android dev but found it clunky. Would this still use WPF and C#?
Keep us posted as to this project.
I use Xamarin studio for mobile development. In the past it was not that good but they added XAML and MVVM support a while ago and it's been a bless so far(porting my concept over). Of course is not as advanced as WPF but is looking great :) i posted this picture here about a week ago:

See less See more
Ok, let's go with the first two points since i worked all day and my brain is a bit tired today :p

1. You can actually add all the events you want in the Behaviour class... just look for this part:

Code:
// Create events here
element.PreviewMouseUp += element_PreviewMouseUp;
Create your events there. In the event itself don't forget to add this line just as in the mouse up one:

Code:
InvokeViewModelMethod(sender, AruanTuber_DarkMoon.ViewModels.ViewModelBase.EvenTypes.MouseLeftButtonUp);
All i do here is pass the message to the viewmodel hooked to the UI. Add the required entries to the EvenTypes enum to expand it and replace "MouseLeftButtonUp" with your new one. That's all required. Basically you just have all the required events in one place.

2. No, you can expand the Behaviour class as it serves as a base only so is there to get expanded as much as you want as i described on point #1.

As i can see you have improved.... i like that :)
See less See more
Of course is firing both and that's good. That's the reason why i told you in my previous post to expand the EvenTypes enum and add a MouseLeave item. In the viewmodel you have access to that property and you can check which event has been fired. I made that to show how things work and what could happen if you register several events on your behaviour. As i said this is just a basic implementation and as such is great to see the problems you have to deal with. Alternatively you can add a second parameter to the behaviour to determine which event should be generated instead. However is not that necessary i believe but i can show you how to do that if needed.
Ok, that's indeed cluncky.... what i meant was to extend the enum of the viewmodel base and add a new event entry. The idea is to fire the invoke method and provide the new enum item. The downside of this method is that the method is gonna get fired several times depending on the events triggered. However you can check from the viewmodel which one is firing using the enum. There is another method using a secondary parameter flag in your behaviour to determine which event is gonna get fired. This is done by using the enum as a property flag. Will see if i can get some time today to show you how to do that.

ps... When you get a chanse can you advise on question 3 and 4 prev post?)
Yeah, but first i want you to understand the first two :p
So.... it seems like you are having some hard times trying to figure out how to deal with it so let me give you a hand. First of all as i said on my previous post you should focus in the EvenTypes enum and expand it(i just realized that i have a typo lol! it should be called "EventTypes"). The enum is located in the ViewModelBase class:

Code:
/// <summary>
        /// Handle the different types of events
        /// </summary>
        public enum EvenTypes
        {
            None = 0,
            MouseLeftButtonUp = 1,
            MouseLeftButtonDown = 2,
            MouseDoubleClick = 3,
            Enter = 4,
            Drop = 5,
            Delete = 6,
            MouseRightButtonUp = 7,
            MouseRightButtonDown = 8,
            MouseLeave = 9,
            AltLeftUp = 10
        }
As you can see i added the "MouseLeave = 9," entry so i'm ready to handle that in my behaviour now.

Code:
private static void IsMethodChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
     var element = d as FrameworkElement;
     if (element != null)
     {
         // Create events here
         element.PreviewMouseUp += element_PreviewMouseUp;
         element.MouseLeave += element_MouseLeave;
     }
}
Code:
static void element_MouseLeave(object sender, System.Windows.Input.MouseEventArgs e)
{
      InvokeViewModelMethod(sender, AruanTuber_DarkMoon.ViewModels.ViewModelBase.EvenTypes.MouseLeave);
}
Now we have all setup but this method have a downside still and that's the fact that our method is going to be called on all events registered. This is fine as the basic idea is to work like the commands on windows where commands go through the same place...now is time to determine what we need. In this case i'm using the "Test" method i added to the tutorial project. If you check the Control3ViewModel.cs you will notice that i added the following:

Code:
class Control3ViewModel : ViewModelBase
    {
        void Test()
        {
            switch(this.EventType)
            {
                case EvenTypes.MouseLeave:
                    break;
                case EvenTypes.MouseLeftButtonUp:
                    break;
            }
        }
    }
As you an see each viewmodel gets the events from the behaviour but each time the EventType property of the baseclass has the current event that called the method. In this way you can determine what todo next.

The other method to register the necessary event only is done by using a secondary parameter so i will show you that in the next few days when i get some time. In the mean time try to understand a little more what is happening here so that you can get a clear picture of it and find your own ways. This method is not the best but also not bad and at certain scenarios probably the only way so is important to comprehend what's going on in order to deal with future problems you may get. The alternative method with the extra parameter may help to register one event only but is going to limit you to that event as well so you will quickly realize the pros and cons.

Attachments

See less See more
There's no MouseDoubleClick but just as you found out it's done by getting the "ClickCount". I know it sounds weird but it works just as good by checking the click count of 2. As weird as it may sound is already a default practice when doing MVVM as you basically work with base components where in this case is the FrameworkElement which is the base of all components. The reason why is important to know how things work is because it's very important when dealing with MVVM as you may face scenarios that require you to build your own solution at times.
Just a quick one....what's the difference in using behavior as opposed to commands?
There are many differences between them but the biggest one is the fact that you will end up having to define your commands on your viewmodel over and over just to handle a single click not to mention handling from different controls. In case you need more features you will end up having some ugly code that do the job or having to add a third party dll that you probably don't need at least 90% of it. Using the behaviour you have a relatively clean way to handle events and all them are re-usable meaning that you just declare the event once in your entire project not to mention the behaviour is also re-usable with any other future project you may have.

I will recommend you to check commands if you wish but after a while you will see how much your code is gonna grow and how bloated is gonna get ;). Of course my solution isn't perfect either but after many years trying to figure out the best way to handle such scenarious i've learn that behaviours are probably the best way so i stay away from commands as much as i can this days. Of course there are people out there that may not agree with my view of things :p
See less See more
Hi Ruantec... I'm trying not to bug you too often... (but from all my postings, I don't seem to be succeeding in that :D )
You don't bug me at all my friend. In fact... i'm really liking your determination and i'm willed to help as much as i can. I wish i had more time this days to spend in the tutorials but sadly have found myself busy with other stuff.

Just a word on the English language... to use the term "and my control is binded" is not perfect English, but it seems to be used all over the net. I wish people would use "Is bound" or "I've bound it to..."... (as you are trying to learn English, do you find this a bit strange or have you ever questioned it?)... but anyway that's just my personal view...
Thanks! will keep that in mind ;)

Help... again. I've a problem with a datagrid which seems strange. I've buttons to move selected item up or down. Moving up works like a charm... moving down I get "ArgumentOutOfRangeException" (Specified argument was out of the range of valid values.\r\nParameter name: index). This error is not consistent... It could be on the 3rd click of down btn or 5th... pretty random. I think what's happening is that on the remove of item from current position my indexing goes out of whack and when on the insert of item into new row it fails, but I can't seem to fathom why its giving error and why it's random... any ideas?
Looking at your code i can't really say what's wrong there except for the way you are getting the index. Usually controls offer the SelectedIndex property for bindings so you can use that instead on your viewmodel. Other than that have you ever thought on swapping rather than remove and insert? it has a small overhead by creating a copy but is way faster at the end as the list doesn't get re-organized and no items are inserted or removed. Basically this:

var myItem = JobStepItems[currentIndex]; // Copy
// Increase your index
++currentIndex;
// Swap items
JobStepItems[currentIndex -1] = JobStepItems[currentIndex];
JobStepItems[currentIndex] = myItem;

I haven't tested myself since is quite late here but if i'm not mistaken the only thing required would be to update using an invokepropertychanged on your list in case is not done automatically.
See less See more
I checked the problem to see what's exactly the reason for you getting the exception. After writing a small example myself the problem was clear. Each time you change items on your list the whole list gets updated and because of that the index resets and obviously your property too. At the moment you want to insert the new item your index has the value -1 causing the argument out of range exception. To solve that problem you have to keep a copy of both the item and the current index before swapping items. I made a small project for you to show you how to solve your problem easily.

I wrote this method in the attached sample to handle both ways:
Code:
private void MoveUp()
        {
            if (SelectedItemIndex == 0 || SelectedItem == null)
                return;
            SwapItems(SelectedItemIndex -1, false);
        }

        private void MoveDown()
        {
            if (SelectedItemIndex >= items.Count -1 || SelectedItem == null)
                return;
            SwapItems(SelectedItemIndex +1, true);
        }

        private void SwapItems(int indexToUse, bool increase = false)
        {
            // Make a copy of the selected item
            var copy = SelectedItem;
            // Make a copy of the selected index
            var indexcopy = SelectedItemIndex;
            // Swap items
            Items[indexcopy] = Items[indexToUse];
            // At this point in time your SelectedItemIndex is -1 because the list was rearranged
            Items[indexToUse] = copy;
            // Set the new index
            SelectedItemIndex = increase ? ++indexcopy : --indexcopy;
        }
Voila!!!! swap works for both ways ;)

A question on the side... when referencing "selectedJobStepIndex" should it be the "selectedJobStepIndex" or "SelectedJobStepIndex"?
If you are refering to items bound to a property then it should always be "SelectedJobStepIndex". In fact... you should never use the selectedJobStepIndex variable if not necessary. The only case when it makes sense is when you want to modify the content without updating the information on your UI(which is kinda weird but it could make sense in some scenarios).

Attachments

See less See more
There is a small bug in the ObservableList which i found recently. "InvokePropertyChanged("JobStepItems");" won't work because you are altering a property of an item but not the item itself. Because of that it thinks that nothing has changed and obviously no update is going to be triggered. There is a dirty way to solve that problem until i find a solution for that and that is by setting the list again so basically doing this:

Code:
JobStepItems = new YourCollectionType(JobStepItems);
As i said it's a dirty way of doing it but there is no other way around it right now(i'm looking for a solution). That's the reason why my code didn't work with the ObservableList.

Now one thing that confuse me is why do you need the StepNumber(which seems to be a property of your class) and what is the use of it. Are you using it as a kind of index to display or something? if there is another reason you can also swap indexes the same way you swap content in the same function without going through the whole list again.
See less See more
Lol... looks like we need a totally dedicated site to help me and all my posts... Will try and do this myself but I'm stuck with how to structure the collections for a listbox within a datagrid row. Table structure is one -> many... so each row in dg will have multiple items in listbox. I need to have the dg item editable (already doing this) and along with each dg row I need to edit/add/delete any listbox items...

I managed to do this in code behind pre MVVM version and that took some time and searching... I don't stand a chance finding anything in MVVM... any pointers in how to structure my vm with above scenario ... guess its a nested observable collection or something?
Well, you have to start thinking on how your datagrid is built. Each row represents an element which is a viewmodel obviously. On that viewmodel you have a observableList/collection/collections etc. that holds the elements to be displayed on your listbox within the datagrid. The itemsource obviously points to your viewmodel list which is where the data is coming from. Now the tricky part is to use an item template and place the listbox on your grid. Obviously the listbox is going to be invisible and with the use of triggers you can check the datagrid edit states and make your combobox visible when necessary. There are other alternatives as well but it could be tricky.

PS. Guess you are pretty busy with your Mobile MVVM stuff? How is it going?
I'm actually not busy with the mobile version of MVVM but more with my real life job ;) atm i'm writing a project using C++, MVVM and C#. I've also faced a new issue and found myself a new way to speed up heavy UI updates simulating something like the UI lock in windows forms but using the manager and invoke property mechanism of MVVM.
See less See more
I'm not quite sure what you are trying to do but as far as my sleepy brain can understand you have lists nested to each JobStepList right? then are you trying to display the content of the nested collection in your row or something???. In the "Add To List" are you trying to add the selected item to the selected item nested collection? and what is the null exception you mentioned? i can't see anything on your picture.

I just need more imput to completely understand what you are doing and provide you a solution. However if the scenario i described is what you are trying to do then i guess the selected item is your JobStepList class so you can use it to fill it with the selected item on your "Add To List" list.

Btw i used to work on a Safety project as well many years ago. Your pictures and code reminds me a lot to what i had to deal with :p

getting frustrated with how little "well coded" samples are out there... my little rant!!!!
Samples on the net are usually messy and in my opinion nothing you should make a big use of. However they are quite useful sometimes as they can give you some ideas on how you can solve your problems.
See less See more
I wrote a PM with my contact information so feel free to mail me anytime. I may no longer answer here anymore so please use the mail instead whatever you have a question.

Cheers!
1 - 20 of 42 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