Complete Generic MVVM in Silverlight Part III

Vuyiswamb
Posted by in Silverlight category on for Intermediate level | Points: 250 | Views : 7029 red flag
Rating: 5 out of 5  
 3 vote(s)

This is part 3 of the series in MVVM, in this part of the series; I will look at the ViewModel and provide you will examples that might help you to understand better. The ViewModel is the heart of MVVM. This is where everything is happening.

Introduction 

This is part 3 of the series in MVVM, in this part of the series; I will look at the ViewModel and provide you will examples that might help you to understand better. The ViewModel is the heart of MVVM. This is where everything is happening.

Objective

 The objective of this article of this first part of the series is to give you a brief description of the ViewModel.

Looking back at the Overview of the Pattern? 

As you can see, I am using this diagram in all of the series of these articles. In my previous article i have demonstrated the Model and the things you are expected to have in the model and explained that the Model does not have knowledge of the ViewModel , it exist independently and it promotes better unit testing of the application. Let us look at our Diagram. 

From here we can see that the ViewModel is being accessed by the “View” and that means the “View” cannot access the Model directly, but it needs to go via the “ViewModel”. This thus confirms for us that the “ViewModel” is the middle man between the “View” and the “Model”. 

Why do we need a ViewModel? 

The ViewModel allows us to write a code once and use it in many places without rewriting it. Let me share a scenario with you. I built an N-Tier architecture application with more than 40 Silverlight pages. In that Page I obviously call the service asynchronously. 30 of my Pages I call a WCF service function that returns a list of “Customers” and I bind that list to a Combobox. I have written that asynchronously calls on the pages 30 Times. This means I have redundant code that is screaming at me. “Please write me somewhere and reuse me”.

If I was using MVVM pattern in my Project, I would have written that function once and never write any code to bind my Combobox. I know you might think I am too ambitious, but if you follow the series of this article you will see what magic MVVM brought into my life.  

So the ViewModel facilitate what I just explained, the code that needs to be reused is stored in the ViewModel and it is reused in all the Views in your application. The ViewModel does not call any StoredProcedure, but it is the one that calls the WCF services from the model. The Asynchronous calls are done once and exposed to be used as many times as it can be used.  

The View Model removes a lot of View that normally sites in the “View”. The Click event of the Button is handled in the View via the Commands. The Commands allows you to respond to the events fired on the View. e.g. if someone clicks on the button, If someone selected an item on the dropdown. The Commands are beyond the scope of this article and they can confuse a new person in MVVM. So I decided to exclude them from this part of the series and deal with the in the next part of the Series.  

What do we have inside a ViewModel?

 

From the diagram above, you can see that we only have two things on the ViewModel folder. There is the Command that I explained earlier and the actual ViewModel class that contains everything we need. Please note that you can have as many ViewModel as you can. You can have ViewModel for Customers; you can have ViewModel for Human Resource and so on. You are you not limited to any number of ViewModel. The Command Class will always look the same; I just don’t want to get into to it, because I will dedicate the next part of the article to it. Let us look at the ViewModel.  

This is how your ViewModel will look like 

namespace MVVM.ViewModel

{

public class ViewModelCustomers : INotifyPropertyChanged

{

bool isReset = false;

public ViewModelCustomers()

{

InitiateViewModel();

_insertCommand = new Commands(Add) { IsEnabled = true };

_updateCommand = new Commands(Update) { IsEnabled = true };

_deleteCommand = new Commands(Delete) { IsEnabled = true };

}

private void InitiateViewModel()

{

GetCustomers();

}

private void GetCustomers()

{

CustomerService.CustomersClient client = new CustomerService.CustomersClient();

client.GetCustomersAsync();

client.GetCustomersCompleted += new EventHandler(client_GetCustomersCompleted);

}

void client_GetCustomersCompleted(object sender, CustomerService.GetCustomersCompletedEventArgs e)

{

CustomersList = e.Result;

}

private ObservableCollection _customerlist;

public ObservableCollection CustomersList

{

get

{

return _customerlist;

}

set

{

if (_customerlist != value)

{

_customerlist = value;

OnNotifyPropertyChanged("CustomersList");

}

}

}

#region Commands

private void ClearControls()

{

isReset = true; //This Tells our Property that we are clearing the textboxes in the View

//Instead of trying to access the Controls form the View directly

//its better to crear the Controls from the Properties

CustomerName = "Enter Customer";

customeraddrees = "Enter Address";

CustomerSurname = "Enter Surname";

CustomerTelephone = "Enter Telephine";

}

private void Add()

{

CustomerService.CustomersClient customer = new CustomersClient();

if (!string.IsNullOrEmpty(CustomerName) & !string.IsNullOrEmpty(customeraddrees) & !string.IsNullOrEmpty(CustomerSurname))

{

if (isReset)

{

CustomerService.CustomersModel model = new CustomersModel()

{

CustomerName = CustomerName,

customeraddrees = customeraddrees,

CustomerSurname = CustomerSurname,

CustomerTelephone = CustomerTelephone

};

customer.AddCustomersCompleted += new EventHandler(customer_AddCustomersCompleted);

customer.AddCustomersAsync(model);

}

}

else

{

MessageBox.Show("Please make sure all Fields are Filled");

}

}

void customer_AddCustomersCompleted(object sender, AddCustomersCompletedEventArgs e)

{

if (e.Error == null)

{

InitiateViewModel();

ClearControls();

}

}

//Update

 

private void Update()

{

if (!string.IsNullOrEmpty(CustomerName) & !string.IsNullOrEmpty(customeraddrees) & !string.IsNullOrEmpty(CustomerSurname))

{

CustomerService.CustomersClient client = new CustomerService.CustomersClient();

CustomerService.CustomersModel model = new CustomersModel()

{

CustomerID = Convert.ToInt32(SessionManagement.GenericMethods.GenericMethods.GetCookie("CustomerID")),

CustomerName = CustomerName,

customeraddrees = customeraddrees,

CustomerSurname = CustomerSurname,

CustomerTelephone = CustomerTelephone

};

client.UpdateCustomersAsync(model);

client.UpdateCustomersCompleted += new EventHandler(client_UpdateCustomersCompleted);

}

else

{

MessageBox.Show("Please make sure all Fields are Filled");

}

}

void client_UpdateCustomersCompleted(object sender, UpdateCustomersCompletedEventArgs e)

{

if (e.Error == null)

{

InitiateViewModel();

}

}

private void Delete()

{

CustomerID = Convert.ToInt32(SessionManagement.GenericMethods.GenericMethods.GetCookie("CustomerID"));

if (CustomerID != null & CustomerID != 0)

{

CustomerService.CustomersClient client = new CustomerService.CustomersClient();

CustomerService.CustomersModel model = new CustomersModel()

{

CustomerID = CustomerID

};

client.DeleteCustomersAsync(model);

client.DeleteCustomersCompleted += new EventHandler(client_DeleteCustomersCompleted);

}

else

{

MessageBox.Show("Please make sure that you select a record to delete");

}

}

void client_DeleteCustomersCompleted(object sender, DeleteCustomersCompletedEventArgs e)

{

if (e.Error == null)

{

InitiateViewModel();

SessionManagement.GenericMethods.GenericMethods.DeleteCookie("CustomerID");

}

}

private readonly ICommand _deleteCommand;

public ICommand DeleteCommand

{

get

{

return _deleteCommand;

}

}

private readonly ICommand _updateCommand;

public ICommand UpdateCommand

{

get

{

return _updateCommand;

}

}

private readonly ICommand _insertCommand;

public ICommand InsertCommand

{

get

{

return _insertCommand;

}

}

#endregion Commands

#region Properties

///

/// Occurs when a property value changes.

///

public event PropertyChangedEventHandler PropertyChanged;

///

/// Called when [notify property changed].

///

/// Name of the property.

protected void OnNotifyPropertyChanged(string propertyName)

{

if (PropertyChanged != null)

{

PropertyChanged(this, new PropertyChangedEventArgs(propertyName));

}

}

private int _CustomerID;

private string _CustomerName;

private string _CustomerSurname;

private string _CustomerTelephone;

private string _customeraddrees;

public int CustomerID

{

get

{

return _CustomerID;

}

set

{

_CustomerID = value;

OnNotifyPropertyChanged("CustomerID");

}

}

public string CustomerName

{

get

{

return _CustomerName;

}

set

{

if (!string.IsNullOrEmpty(value))

{

_CustomerName = value;

OnNotifyPropertyChanged("CustomerName");

}

else

{

throw new Exception("Invalid Customer name");

}

}

}

public string CustomerSurname

{

get

{

return _CustomerSurname;

}

set

{

if (!string.IsNullOrEmpty(value))

{

_CustomerSurname = value;

OnNotifyPropertyChanged("CustomerSurname");

}

else

{

throw new Exception("Invalid Surname");

}

}

}

public string CustomerTelephone

{

get

{

return _CustomerTelephone;

}

set

{

if (!string.IsNullOrEmpty(value))

{

_CustomerTelephone = value;

OnNotifyPropertyChanged("CustomerTelephone");

}

else

{

throw new Exception("Invalid Telephone");

}

}

}

public string customeraddrees

{

get

{

return _customeraddrees;

}

set

{

if (!string.IsNullOrEmpty(value))

{

_customeraddrees = value;

OnNotifyPropertyChanged("customeraddrees");

}

else

{

throw new Exception("Invalid Address");

}

}

}

#endregion

}

} 

Do not be offended by this code, I will break it into bit and peace and explain it to you, like a father or mother teaching a child to walk. 

The Constructor in a ViewModel? 

Every View model must have a Constructor. The Constructor exposes what will be available to the View. The Constructor of the ViewModel is an important part of this pattern. Without the Constructor the ViewModel cannot expose its objects. No Add, Update, Delete if the object is not initialised in the contractor of the ViewModel

The above diagram shows the Constructor of the ViewModel and there is a method that is used to call the functions or methods that the View will need when it make contact with the ViewModel.
<!--[if !supportLineBreakNewLine]-->
<!--[endif]-->
 

Getters and Setters and Events 

In our Viewmodel you have noticed that we have getters and setters that match the definition of our Model. If you have noticed closely, you will see that our setters have something extra on them

 

This is a Property tracker. The First line we declare a Propertychanged event handler that will get fired when a Property value changes. The second part is a method that will handle the event, so when a property changes, we need to make sure that it has surely been fired, So with the “if” statement there we are checking if the event is not null, if it is null, it means the event has not been triggered, else we are going all allow some operation on the “setter”. Now as you can see the “OnNotifyPropertyChanged” method is fired after the value has been set. I have seen a lot of examples on the net, where it is done before the value is set, which is not correct. Make sure it is after the value has been assigned because you want to assign the value and inspect it and throw an Exception if you have to, as depicted below.

 As the Diagram demonstrate, Here we do our business Rules, This is where we Check if the Rules are not being broken and if they are, we throw an exception and this exception is handled gracefully by the View, which I will demonstrate in our last part of the series.  

The Last part that you have seen in our ViewModel, are the Commands. I will not try to explain them because they will be a topic in our next part of the series.  

Conclusion 

The Next part of the Series is still Under the ViewModel. I will explain in more details about the Commands. 

Thank you again for reading this series, I hope you have learned a lot of things, see you next time. 

Page copy protected against web site content infringement by Copyscape

About the Author

Vuyiswamb
Full Name: Vuyiswa Maseko
Member Level: NotApplicable
Member Status: Member,MVP,Administrator
Member Since: 7/6/2008 11:50:44 PM
Country: South Africa
Thank you for posting at Dotnetfunda [Administrator]
http://www.Dotnetfunda.com
Vuyiswa Junius Maseko is a Founder of Vimalsoft (Pty) Ltd (http://www.vimalsoft.com/) and a forum moderator at www.DotnetFunda. Vuyiswa has been developing for 16 years now. his major strength are C# 1.1,2.0,3.0,3.5,4.0,4.5 and vb.net and sql and his interest were in asp.net, c#, Silverlight,wpf,wcf, wwf and now his interests are in Kinect for Windows,Unity 3D. He has been using .net since the beta version of it. Vuyiswa believes that Kinect and Hololen is the next generation of computing.Thanks to people like Chris Maunder (codeproject), Colin Angus Mackay (codeproject), Dave Kreskowiak (Codeproject), Sheo Narayan (.Netfunda),Rajesh Kumar(Microsoft) They have made vuyiswa what he is today.

Login to vote for this post.

Comments or Responses

Posted by: Ogipansrk on: 12/4/2011 | Points: 25
This is the best article I have read about MVVM.
I'm follwowing this article from part-I onwards. Great job.
Hey , please release the fourth article soon and try to cover the commands indepth.
It will be very very very helpful.

When we all expect next article?

Posted by: Vuyiswamb on: 12/4/2011 | Points: 25
Good Day Ogipansrk

Thank you for reading the series of this article. I kept the Commands separately because i know they can be complicated. SO i tend to explain them in detail. So all of my article i like to explain things clearly. i have started writing the article and it is half way, you can expect it on Wednesday or Thursday.


Login to post response

Comment using Facebook(Author doesn't get notification)