Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Catel - Part 0 of n: Why choose Catel?

0.00/5 (No votes)
3 Nov 2014 3  
This article tries to explain why you should use Catel as the (or one of the) frameworks when developing WPF, Silverlight, and Windows Phone 7 applications.

Article browser

Table of contents

  1. Introduction
  2. Generic functionality
  3. Target framework specific subjects
  4. Writing n-tier applications using MVVM

1. Introduction

Welcome to part 0 of the article series about Catel. Quite a strange part, don’t you think? Well, we hear from a lot of people that the real power of Catel cannot be seen at first sight. When people start using Catel, they become very happy, but why should they choose Catel?

This article tries to explain why you should use Catel as the (or one of the) frameworks when developing WPF, Silverlight, and Windows Phone 7 applications.

This article is separated into several sections, one general one, and the rest is specific for the different frameworks that are targeted by Catel. It’s recommended to read the general part first, and then you can skip to the target framework you are interested in.

This article is not about in-depth explanations of all the functionality. If you are looking for a deep-dive into Catel, it’s recommended to read all the other articles which all focus on a specific part of the framework. This article is more of a summary of what Catel has to offer.

2. Generic functionality

2.1. It’s your choice

We care a lot about the freedom you need as a software developer. Most frameworks require a developer to learn its conventions, or use the whole framework or nothing at all. When we, the developers of Catel, use an external framework, we choose that framework for a specific reason, and don’t want to be bothered by all the other superb stuff it has to offer (maybe later, but not now).

During the development of Catel, we tried to maintain this freedom aspect which is very important to us. Therefore, all functionality is loosely coupled. Sounds great, but everything is called loosely coupled nowadays. Catel contains a lot of different aspects, such as logging, diagnostics, Reflection, MVVM, user controls, windows, etc. All these aspects are complementary to each other, but the great thing about Catel is that you decide whether to use just one, some, or maybe all aspects.

As an example: you have a legacy application and want to use the DataWindow to write simple entry windows, but you are not ready for MVVM yet. No problem, the DataWindow is complementary to MVVM, but does not require it. Therefore, you have all the freedom to use just what you need, whenever you need it.

Most frameworks require a bootstrapper that completely decides how your application structure should look like. For example, your Views must have this name, your controls must have that name. Again, in Catel, we wanted to give you the freedom you would expect from a framework.

The great thing about this freedom is that the different aspects of Catel can be used side-by-side with other frameworks, so you as a developer can use the best framework for every aspect in your application.

2.2. Data handling

The first thing that is important is that lots of developers lose way too much time writing custom serializable objects. Serialization is a field of expertise, and only a handful of developers I know really master the serialization of objects (think of version changes of the assembly, class changes (new or removed properties), etc.). Most developers think they master serialization by creating a BinaryFormatter object like the code belows show:

BinaryFormatter serializer = new BinaryFormatter();
var myObject = (MyObject)serializer.Deserialize(stream);

Most developers don’t know that Binary Serialization breaks when:

  1. You change the version number of your assembly;
  2. You add or remove a property or field;
  3. You add or remove an event.

And even if you know, it takes a lot of knowledge and courage to start beating the beast of burden. Like every developer, I also encountered this and was writing backwards compatibility code until I had enough of it and decided to master the field of serialization. The result is the DataObjectBase class, which can be used as a base class for all data objects that need to be held in memory and maybe serialized to disk (or a stream, or XML, or ...).

2.2.1. DataObjectBase class

Using the class is extremely simple. Just declare a new class that derives from DataObjectBase and you are ready to go:

/// <summary>
/// MyObject Data object class which fully supports serialization,
///          property changed notifications,
/// backwards compatibility and error checking.
/// </summary>
#if !SILVERLIGHT
[Serializable]
#endif
public class MyObject : DataObjectBase<MyObject>
{
    /// <summary>
    /// Initializes a new object from scratch.
    /// </summary>
    public MyObject() { }

#if !SILVERLIGHT 
/// <summary>
/// Initializes a new object based on <see cref="SerializationInfo"/>.
/// </summary>
/// <param name="info"><see cref="SerializationInfo"/>
//     that contains the information.</param>
/// <param name="context"><see cref="StreamingContext"/>.</param>
protected MyObject(SerializationInfo info, StreamingContext context)
    : base(info, context) { }
#endif
}

As you can see in the code above, the MyObject class derives from DataObjectBase and provides an empty constructor, but also a constructor that is used for binary deserialization. The code above might look complex, but it is created using the dataobject code snippet, and you only have to type the name of the class.

2.2.2. Defining properties

Defining properties for the class is very easy, and works the same like dependency properties. The advantages of this way of defining properties are:

  • Properties defined like this are automatically included during serialization; no need to specify complex data contracts;
  • You can specify a default value for a property which will be used when the class is constructed or the property is not found during deserialization (in case this property is added to an existing class);
  • The PropertyData object can be used to retrieve property values so the compiler checks for errors;
  • You can directly subscribe to change notifications, and all properties automatically support INotifyPropertyChanged out of the box.

Below is the code that defines a new property Name of type string:

/// <summary>
/// Gets or sets the name.
/// </summary>
public string Name
{
    get { return GetValue<string>(NameProperty); }
    set { SetValue(NameProperty, value); }
}

/// <summary>
/// Register the Name property so it is known in the class.
/// </summary>
public static readonly PropertyData NameProperty = 
       RegisterProperty("Name", typeof(string), string.Empty);

A registered property can be excluded from serialization if wanted. When the object is deserialized, the default value will be used for the property in that case.

2.2.3. Serialization

I have mentioned serialization a few times already. Let’s take a look at how easy it is to (de)serialize your objects no matter what assembly version you use. First of all, instead of deriving from DataObjectBase, it is very important to derive from SavableDataObjectBase.

Depending on the target framework, several options are available as serialization modes:

  • Binary;
  • XML;
  • DataContract;
  • JSON.

The code below shows how to save an object (which can, of course, be a complex graph of nested objects):

var myObject = new MyObject();
myObject.Save(@"C:\myobject.dob");

Looks too easy, but this really is the only thing you need to do. You can specify the serialization mode in the several available overloads of the Save method.

Loading is as easy as saving, as you can see in the following code:

var myObject = MyObject.Load(@"C:\myobject.dob");

2.2.4. Functionality provided out of the box

The DataObjectBase provides a lot of functionality out of the box. A few points I want to mention are:

All properties registered using the RegisterProperty method automatically take care of change notifications.

It is very easy to set field and business errors using the SetFieldError and SetBusinessError methods that can be used in the overridable ValidateFields and ValidateBusinessRules methods.

The data object can automatically create an internal backup and restore it, if required, using the IEditableObject interface.

As told many times before, using the SavableDataObjectBase, you can simply save your file to a stream (file on disk, stream in memory, etc.).

  • INotifyPropertyChanged
  • IDataErrorInfo
  • IEditableObject
  • Serialization

Keep in mind that this class is not suitable for database communication, there are much better ways to handle this (ORM mappers such as Entity Framework, NHibernate, LLBLGen Pro, etc.).

This API is located in the Catel.Core assembly which can be used without WPF (thus in ASP.NET, Windows Forms, etc.).

2.3. MVVM basics

The last few years, MVVM has become the number one pattern to write applications using WPF, Silverlight, and Windows Phone 7. The actual pattern is very simple, but there are some flaws and questions lots of MVVM users have, such as:

  • How to show modal dialogs or message boxes inside a View-Model?
  • How to run processes inside a View-Model?
  • How to let the user select a file inside a View-Model?

In my opinion, this is where the good frameworks separate themselves from the bad ones. For example, people actually calling MessageBox.Show inside a View-Model are using the pattern wrong. If you are one of the developers that directly call a MessageBox inside a View-Model, ask yourself this: who is going to click the button during a unit test?

Before we actually started developing Catel, we did lots of investigations to make sure that the MVVM pattern was really useful in Line of Business (LoB) applications and does not miss the finishing touch. Thanks to this investigation and research, we created a solid MVVM framework which solves all the known problems of the MVVM pattern.

2.3.1. ViewModelBase

Like almost every other MVVM framework, the base class for all View-Models is ViewModelBase. This base class is derived from the DataObjectBase class explained earlier in this article, which gives the following advantages:

  • Dependency property a-like property registration;
  • Automatic change notification;
  • Support for field and business errors.

Because the class derives from DataObjectBase, you can simply add field and business errors that are automatically being reflected to the UI. Writing View-Models has never been so easy!

2.3.2. Model to View-Model mappings

During of the use of the MVVM pattern, we noticed that lots and lots of developers have a Model, and map the values of the Model to all properties of the View-Model. When the UI closes, the developers map all the properties back to the Model. All this redundant code is not necessary when using the View-Models of Catel.

In Catel, we have created attributes that allow you to define a property as a Model. A Model is a property that a part of the View-Model represents to the user. A View-Model might have multiple Models if it is a combination of several Models.

Defining a Model is very simple, you only have to decorate your property with the Model attribute:

/// <summary>
/// Gets or sets the shop.
/// </summary>
[Model]
public IShop Shop
{
    get { return GetValue<IShop>(ShopProperty); }
    private set { SetValue(ShopProperty, value); }
}

/// <summary>
/// Register the Shop property so it is known in the class.
/// </summary>
public static readonly PropertyData ShopProperty = 
              RegisterProperty("Shop", typeof(IShop));

Using the Model attribute is very powerful. Basically, this is the extended functionality in the View-Model. If the model supports IEditableObject, BeginEdit is automatically called in the Initialize of the View-Model. When the View-Model is canceled, the CancelEdit is called so the changes are undone.

When a Model is defined, it is possible to use the ModelToViewModel attribute, as you can see in the code below:

/// <summary>
/// Gets or sets the name of the shop.
/// </summary>
[ViewModelToModel("Shop")]
public string Name
{
    get { return GetValue<string>(NameProperty); }
    set { SetValue(NameProperty, value); }
}

/// <summary>
/// Register the Name property so it is known in the class.
/// </summary>
public static readonly PropertyData NameProperty = RegisterProperty("Name", typeof(string));

The ViewModelToModel attribute in the code example above automatically maps the View-Model Name property to the Shop.Name property. This way, you don’t have to manually map the values from and to the Model. Another nice effect is that the View-Model automatically validates all objects defined using the Model attribute, and all field and business errors mapped are automatically mapped to the View-Model.

Summarized, the Model and ViewModelToModel attributes make sure no duplicate validation and no manual mappings are required.

2.3.3. Commands

Commanding is perfectly supported by Catel. Catel supports Command classes, which is also known as RelayCommand or DelegateCommand in other frameworks. Defining a command on a View-Model is very easy, as you can see in the code below:

// TODO: Move code below to constructor
Edit = new Command<object, object>(OnEditExecute, OnEditCanExecute);
// TODO: Move code above to constructor

/// <summary>
/// Gets the Edit command.
/// </summary>
public Command<object, object> Edit { get; private set; }

/// <summary>
/// Method to check whether the Edit command can be executed.
/// </summary>
/// <param name="parameter">The parameter of the command.</param>
private bool OnEditCanExecute(object parameter)
{
    return true;
}

/// <summary>
/// Method to invoke when the Edit command is executed.
/// </summary>
/// <param name="parameter">The parameter of the command.</param>
private void OnEditExecute(object parameter)
{
    // TODO: Handle command logic here
}

There are some people who don’t like the ICommand implementations. For example, Caliburn (Micro) uses convention and does not require the creation of a command. There are a few downsides for that:

  • It requires you to make sure the name of the control is the same as the method;
  • It is not clear that it is actually a command if you are not fully familiar with the conventions;
  • The methods need to be public (otherwise, how are you going to invoke the commands during unit tests?), which make them freely available (and that’s not something we like);
  • You will always have to invoke CanExecute yourself again in Execute, because you have no guarantee that the source of Execute is actually the convention mapping;
  • There is no way to manually refresh the CanExecute state on the bound controls.

2.3.4. Services

Services are the way Catel solves the issue where you need to show messages to the user, let the user run processes, etc. The ViewModelBase has a GetService method to retrieve the actual implementation of an interface via an Inversion of Control (IoC) container. For WPF and Silverlight, Catel uses Unity. For Windows Phone 7, a custom implementation of IoC is used.

The great thing about services in Catel is that, by default, the real implementations are registered with the Unity container. Via the IoC container, you can register test implementations (which, of course, also ship with Catel) so you can decide the result of, for example, a message box click. This way, you can simulate a user clicking OK, or if you want, you can also simulate Cancel. This way, you can unit test all the available possibilities a user can execute on the View-Model.

Using the services is very easy. As told earlier, the GetService method retrieves the actual implementation of the service. For example, to show a message to the user, you can use the following code:

var messageService = GetService<IMessageService>();
messageService.ShowError("An error occurred");

All the services can be used this easily. Catel supports several services. Below is a table with all the services available per target framework.

Service name and description WPF Silverlight WP7

ILogger - makes it possible to write messages to the log

Description: C:\Users\Geert\Desktop\yes.png

Description: C:\Users\Geert\Desktop\yes.png

Description: C:\Users\Geert\Desktop\yes.png

ILocationService - retrieve geographical locations

Description: C:\Users\Geert\Desktop\no.png

Description: C:\Users\Geert\Desktop\no.png

Description: C:\Users\Geert\Desktop\yes.png

IMessageService - show message boxes

Description: C:\Users\Geert\Desktop\yes.png

Description: C:\Users\Geert\Desktop\yes.png

Description: C:\Users\Geert\Desktop\yes.png

INavigationService - navigation service

Description: C:\Users\Geert\Desktop\no.png

Description: C:\Users\Geert\Desktop\no.png

Description: C:\Users\Geert\Desktop\yes.png

IOpenFileService - lets the user select a file to open

Description: C:\Users\Geert\Desktop\yes.png

Description: C:\Users\Geert\Desktop\no.png

Description: C:\Users\Geert\Desktop\no.png

IPleaseWaitService - runs a delegate on the UI thread and shows a busy indicator

Description: C:\Users\Geert\Desktop\yes.png

Description: C:\Users\Geert\Desktop\yes.png

Description: C:\Users\Geert\Desktop\no.png

IProcessService - run processes

Description: C:\Users\Geert\Desktop\yes.png

Description: C:\Users\Geert\Desktop\no.png

Description: C:\Users\Geert\Desktop\no.png

ISaveFileService - lets the user select a file to save

Description: C:\Users\Geert\Desktop\yes.png

Description: C:\Users\Geert\Desktop\no.png

Description: C:\Users\Geert\Desktop\no.png

IUIVisualizerService - show (model) windows

Description: C:\Users\Geert\Desktop\yes.png

Description: C:\Users\Geert\Desktop\yes.png

Description: C:\Users\Geert\Desktop\no.png

Of course, you can also write and register your own services.

2.3.5. Communication with other View-Models

Most frameworks require you to set up complex message systems (messengers) or other techniques to communicate with other View-Models. The downside of this approach is that once a View-Model is written in module X, and you are interested in the View-Model, the developer of module X must take care of notifying other View-Models. We think this is not the responsibility of the originating View-Model.

If a View-Model is interested in the changes of another View-Model, it’s the responsibility of the View-Model that is interested to watch the View-Model, not the other way around. To be notified of changes on other View-Models, the only thing you have to do is to decorate a View-Model with the InterestedIn attribute, like shown in the code below:

[InterestedIn(typeof(FamilyViewModel))]
public class PersonViewModel : ViewModelBase

Then, inside the PersonViewModel (which is interested in the changes of FamilyViewModel), you only have to override the OnViewModelPropertyChanged method:

/// <summary>
/// Called when a property has changed for a view model type
/// that the current view model is interested in. This can
/// be accomplished by decorating the view model with the <see cref="InterestedInAttribute"/>.
/// </summary>
/// <param name="viewModel">The view model.</param>
/// <param name="propertyName">Name of the property.</param>
protected override void OnViewModelPropertyChanged(IViewModel viewModel, string propertyName)
{
    // You can now do something with the changed property
}

It is possible to be interested in multiple View-Models. Since the View-Model is passed to the OnViewModelPropertyChanged method, it is very easy to check the type of the View-Model.

2.3.6. Nested user controls problem

In MVVM, there is a complex architectural problem that we call the “nested user controls problem”. Until now, we have not found any other framework that is capable of solving this issue in a clean manner. The problem is that nested user controls should normally get their own View-Model, but how do you manage such a thing? We see several (bad) solutions, like:

  • Defining nested View-Models on another View-Model;
  • Defining the properties of nested user controls on the top View-Model and assume properties inside the nested user control.

Below is a graphical representation of the problem:

Regular MVVM Frameworks

Catel MVVM Framework

As the images above show, the method that Catel uses to solve the problem is much more professional. Below are a few reasons:

  • Separation of concerns (each control has a View-Model only containing the information for itself, not for children);
  • User controls are built so they can be re-used. Without the user controls to be able to have their own View-Model, how should one actually use user controls with MVVM?

UserControl<TViewModel> is able to construct a View-Model based on the actual datacontext of the user control. For example, when you define a nested user control, the only thing you’ll have to do is to make sure that the datacontext of the user control has an object that can be injected in the View-Model that belongs to the user control.

As an example, say we have a Person control. This Person control can only be constructed with a valid IPerson instance (the only constructor of the View-Model available). Then this is the way to define it in XAML:

<Controls:PersonControl DataContext=&rdquo;{Binding Person}&rdquo; />

The user control notices that the datacontext has changed, and tries to construct the PersonViewModel with the real datacontext, in our case, an instance of the IPerson interface. Then, it auto magically transforms the IPerson object into a PersonViewModel and the control has its own View-Model.

2.4. User Interface

As we have probably told you many times before, Catel is more than just another MVVM framework. It also provides lots of UI elements out of the box. The controls below are the most popular ones.

2.4.1. InfoBarMessageControl

InfoBarMessageControl is able to show warnings and errors to the user using the IDataWarningInfo and IDataErrorInfo interfaces. The control is very useful to show the current state of a window or control to the user in a consistent way.

2.4.2. DataWindow

When developing software in WPF, I always need the following three types of windows:

  • OK / Cancel buttons for data windows;
  • OK / Cancel / Apply buttons for application settings / options;
  • Close button on windows for action windows.

Creating these windows is just boring and the steps are always the same:

  1. Create a WrapPanel at the bottom of the window
  2. Add the buttons with the same RoutedUICommand objects over and over again

The DataWindow class makes it much easier to create these basic windows, simply by specifying the mode of the window. By using this window, you can concentrate on the actual implementation, and you don’t have to worry about the implementation of the buttons itself, which saves you time! In the example below, the only thing that is coded manually in XAML are the actual input controls (textboxes).

2.4.3. PleaseWaitWindow

PleaseWaitWindow is a great window to show during long operations. There is also a PleaseWaitHelper class to make it even easier to use the PleaseWaitWindow.

2.5. IO

What a lot of people don’t know is that Catel offers an extended API for IO handling. Now you probably ask yourself why. The following additions are made with the IO framework:

  • Support for files and directories longer than 255 characters;
  • Combine multiple paths or URLs together, like Path.Combine(“C:”, “Windows”, “Temp”);.

The API is designed to match the System.IO style, so the usage is very intuitive.

This API is located in the Catel.Core assembly which can be used without WPF (thus in ASP.NET, Windows Forms, etc.).

2.6. Reflection

Internally, Catel uses lots of Reflection to make sure all of the functionality promised actually gets done. This chapter explains a few advantages of these Reflection implementations, which are not meant to fully replace the .NET Reflection classes. The Reflection classes of Catel are more an addition to the default classes.

The assembly extensions are mostly created to create an equal behavior for all the different target frameworks of Catel. For example, in WPF, we can simply get the loaded assemblies by using the current AppDomain. However, Silverlight requires us to query the current Deployment object. For Windows Phone 7, it’s even harder to get the loaded assemblies. By implementing the differences inside a separate class, all the shared classes between the target frameworks can be shared.

This API is located in the Catel.Core assembly which can be used without WPF (thus in ASP.NET, Windows Forms, etc.).

2.7. Diagnostics & logging

Catel uses log4net as the base for all the logging activities. It does provide additional functionality and extension methods to make it even easier to log exceptions and custom messages. Because Silverlight and Windows Phone 7 don’t have logging capabilities (client side), the ILog implementation is just a dummy one.

This API is located in the Catel.Core assembly which can be used without WPF (thus in ASP.NET, Windows Forms, etc.).

2.8. Quality

Every framework claims to provide perfect quality. We wanted to go further than that. Therefore, more than 450 unit tests are written to prove that Catel provides quality and consistency. The unit tests are shared between WPF and Silverlight to make sure that the expected behavior is the same for both frameworks. This way, we tried to make sure that there is no loss in functionality for Silverlight, where we succeeded on most points (some things are just not possible in Silverlight).

2.9. Examples & documentations

Examples and documentation are very important to us. Therefore, each framework implementation has one or more example applications to show how to use the several aspects per target framework.

Catel is also very well documented. Besides lots of articles and blog posts, it also includes reference documentation with all the class definitions and their meanings.

Besides examples and documentation, Catel also offers project and item templates per target framework. This way, you don’t have to do a lot of the plumbing and let the tools (Visual Studio) do it for you. Besides the templates, Catel also ships code snippets which are very important if you want to be able to rapidly write software using Catel.

Last but not least, it is very important to us that you like Catel. Therefore we provide very good support on the discussion boards on the CodePlex project site. You can also e-mail one of the project members personally if you have a question with sensitive information.

3. Target framework specific subjects

As you probably know by now, Catel supports Silverlight implementations. Silverlight is a very popular platform for software development, but it lacks a lot of features that are already available in WPF. Therefore, with the development of Catel, we tried to even the functionality of all available classes and controls available in Catel.

We tried to implement the Windows Phone 7 implementation the same way, but there are a lot more things to keep in mind when developing a framework for Windows Phone 7, such as performance and a totally different approach of UI and state management.

3.1. Automatic refresh of commands in Silverlight

In Silverlight, commands are not re-evaluated automatically because there is no CommandManager that calls the CanExecute on every routed event. If the user solves an error in the window by, for example, adding a value in an empty field, the CanExecute state of the OK or Save command is not updated (and still is disabled). Other frameworks require the developer to manually refresh the commands.

Catel offers a clean way of providing the same behavior of WPF. Thanks to the ViewModelBase property InvalidateCommandsOnPropertyChanged (which is enabled by default for Silverlight and Windows Phone 7), all the ICommand implementations on the View-Model are automatically re-evaluated for you. This way, the user will immediately see the OK or Save button become enabled after setting the value.

3.2. Serialize data to isolated storage in Silverlight and Windows Phone 7

When developing software for Silverlight or Windows Phone 7, you have access to isolated storage. SavableDataObjectBase supports saving and loading of complex graph objects to isolated storage out of the box. To save an object (including all the child objects), you can use the code below:

using (var isolatedStorageFile = IsolatedStorageFile.GetUserStoreForApplication())
{
    using (var isolatedStorageFileStream = 
           isolatedStorageFile.OpenFile("UserData.dob", 
           FileMode.Create, FileAccess.ReadWrite))
    {
        myObject.Save(isolatedStorageFileStream);
    }
}

To load the data again, you can use the following code:

using (var isolatedStorageFile = IsolatedStorageFile.GetUserStoreForApplication())
{
    if (isolatedStorageFile.FileExists("UserData.dob"))
    {
        using (var isolatedStorageFileStream = 
               isolatedStorageFile.OpenFile("UserData.dob", 
               FileMode.Open, FileAccess.Read))
        {
            return MyObject.Load(isolatedStorageFileStream);
        }
    }
}

3.3. Navigation on Windows Phone 7

Navigation on Windows Phone 7 is very important. The navigation works the same like the web, thus with request variables. In Catel, it is very easy to navigate to another View (Model) using INavigationService.

To navigate to another View directly, simply use this code inside your View-Model:

var navigationService = GetService<INavigationService>();
navigationService.Navigate("/UI/Pages/MyPage.xaml");

Of course, you can also pass parameters without a hassle:

var parameters = new Dictionary<string, object>();
parameters.Add("MyObjectID", 1);

var navigationService = GetService<INavigationService>();
navigationService.Navigate("/UI/Pages/MyPage.xaml", parameters);

The navigation service automatically converts this into the following URL:

/UI/Pages/MyPage.xaml?MyObjectID=1

3.4. Retrieving and simulating geographical locations on Windows Phone 7

The Windows Phone 7 device has a great feature: it can retrieve the current geographical location via GPS. However, how can we implement this correctly in MVVM? In Catel, a separate service is available to get the geographical location. The usage is very simple:

var locationService = GetService<ILocationService>();
locationService.LocationChanged += OnCurrentLocationChanged;
locationService.Start();

It is very important to stop the service when you no longer need it (think about the battery of the user):

var locationService = GetService<ILocationService>();
locationService.LocationChanged -= OnCurrentLocationChanged;
locationService.Stop();

In the OnCurrentLocationChanged event, you can simply query the location from the EventArgs:

private void OnCurrentLocationChanged(object sender, LocationChangedEventArgs e)
{
    if (e.Location != null)
    {
        MapCenter = new GeoCoordinate(e.Location.Latitude, 
                        e.Location.Longitude, e.Location.Altitude);
    }
}

4. Writing n-tier applications using MVVM

We get a lot of questions from users about how to implement an n-tier application using MVVM. Most people think that MVVM replaces the business layer, but that is not the case. This chapter explains a few possibilities of how to write line of business (LoB) applications using MVVM, which you can make as complex or as easy as you want to.

Depending on the complexity and the requirements of the end-user, you might decide to use a multi-tier architecture for your software. We don’t believe in a single standard, but in a pragmatic approach where the number of tiers is decided again for every project to make sure no overhead is created, but no layers are forgotten as well.

Each n-tier description in this chapter shows, in a graphical way, how MVVM should be used inside the architecture. A long time ago, the n-tier model was developed to create a separation of concerns. In these days, a data access layer (DAL) was a complex thing on its own. Nowadays, there are tons of ORM mappers which generate the DAL for you. A few of these ORM mappers, such as LLBLGen Pro, allow you to add custom validation on entities and combine the DAL with some (or in simple applications, all) business functionality.

4.1. 2-tier applications

Writing a 2-tier application should only be used in very, very easy applications that do not require any storage or business rules. Below is a graphical representation of the 2-tier application architecture using MVVM:

When writing a 2-tier application, you only have a business layer (BL) or data access layer (DAL). This architecture is an option if you are writing a very simple application with almost no business rules. In that case, you can simply remove the business layer and handle the business rules in either the Models (DAL) or View-Models.

4.2. 3-tier applications

When applications become more and more complex, users start to demand more functionality and probably business rules. When writing larger applications, it is recommended to write a 3-tier application to separate the business rules from the DAL. Below is a graphical representation on how MVVM fits into the 3-tier architecture:

As you can see, the DAL does not participate in the MVVM at all using a 3-tier application. This is due to the fact that the business layer provides DTO objects that are used as Models, and transforms these DTO objects into entities that the DAL can handle. We see a lot of users referencing the DAL from the UI-layer, but this is wrong. Below is a graphical representation of a good and bad situation of the 3-tier architecture:

An arrow in the graphic above means “uses”.

Good situation

In the good situation, you see that the UI layer uses the BL, and the BL uses the DAL. A top-layer can always use a lower “down-the-hill”. A lower layer may never use a layer above (which is situated in the “very wrong” situation).

Because the DAL is not referenced at all, it is required to create data transfer objects (DTOs) to represent data from the DAL in the BL. The DTO objects are defined inside the BL so the BL is responsible for translating entities from the DAL into DTO objects which are accessible from the UI layer.

Wrong situation

The wrong situation shows a “solution” we see a lot. Developers that use this approach are not aware of the n-tier layers and directly reference the DAL in the UI layer. This way, they don’t have to create custom DTO objects.

If you don’t want to write custom DTO objects, the only option you have is to write a cross-cutting concerns library which can be used by all layers. In the cross-cutting concerns, you can write interfaces for all the entities of the DAL and use these in the BL and UI layer.

Nowadays, most people use ORM mappers. ORM mappers can be used to generate source code for the DAL so you don’t have to spend writing this layer yourself. A tip is to customize the templates for the ORM mapper of your choice (if it is supported by the mapper) so it also generates the DTO objects for you.

Very wrong situation

The very wrong situation shows the three layers of the 3-tier architecture where a user references higher-level layers. This is very bad and destroys the whole separation of concerns (SoC) idea behind the n-tier application architecture. If you think this is right, answer the following question: your BL references the UI in a WPF app, how are you going to use the same BL for a Windows Phone 7 or ASP.NET application?

4.3. MVVM in combination with Silverlight and RIA services

Lots of developers find it hard to find a good way to use MVVM in combination with Silverlight and RIA services. From an architectural point of view, a RIA service does nothing more than provide data in the form of data transfer objects (DTOs). Therefore, a Silverlight application with RIA services must be used in the same manner as the 3-tier architecture described above by using a DTO as the Model.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here