Click here to Skip to main content
Click here to Skip to main content

Data Binding using Caliburn.Micro for Beginners

, 26 May 2014 CPOL
Rate this:
Please Sign up or sign in to vote.
This article provides information on how to implement Data binding using Caliburn.Micro

Introduction

Data binding is one of the key features in MVVM. It establishes a connection between the application UI and presentation logic. It updates the user interface automatically according to the changes introduced in the business logic. In order to update the property in UI, we need to notify the change from the view model. For that, we need to implement an interface such as INotifyPropertyChanged which notifies the UI about any change in Model. But implementation of interface and linking it with the logic itself is a little cumbersome task.

There is an open source framework designed to implement MVVM (Model-View-ViewModel) pattern called Caliburn.Micro. It basically simplifies the coding by reducing the number of lines of code and the extra effort put on to implement MVVM. It’s one of the benefits is that it simplifies the process of Data binding. Caliburn.Micro works on the principle of convention over configuration. We may not need to explicitly implement INotifyPropertyChanged. We just have to follow some simple conventions and work on our logic.

Background

MVVM facilitates a clear separation of the development of the graphical user interface (either as markup language or GUI code) from the development of the business logic or back end logic known as the model (also known as the data model to distinguish it from the view model). The view model of MVVM is a value converter meaning that the view model is responsible for exposing the data objects from the model in such a way that those objects are easily managed and consumed [6]. And for more details on what is MVVM and how to implement it, please follow the link:

Data Binding

Every time when the data of our business model changes, it needs to be reflected to the user interface and vice versa. For example, if the user edits the value in a TextBox element, the underlying data value is automatically updated to reflect that change.

Databinding can be unidirectional (source -> target or target -> source), or bidirectional (source <-> target). The source of a databinding can be a normal .NET property or a Dependency Property, but the target property of the binding must be a Dependency Property.

Now the question arises that how does the source or target get to know that a change has been made and data update needs to be done. For that, usually we implement INotifyPropertyChanged interface. On Dependency Properties, it is done by the PropertyChanged event of this interface.

Databinding is typically done in XAML by using the {Binding} markup extension. The following example shows a simple binding between the text of a TextBox and a Label that reflects the typed value:

<Window x:Class="MyWPFdataBinding.MainWindow" 
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <Label 
            Content="{Binding ElementName=NameoftextBox, Path=Text}"  
            HorizontalAlignment="Left" 
            Name="label1" />
        <TextBox 
            HorizontalAlignment="Left" 
            Name="textBox1"/>
    </Grid>
</Window> 

And then, we need to implement INotifyPropertyChanged interface in ViewModel to update the data.

The process of Data Binding can be explained in a better way using the following diagram:

Caliburn.Micro

Caliburn.Micro's one very important feature is that it follows a series of conventions, i.e., instead of writing the binding code manually; conventions take care of it on the platform provided by Caliburn.Micro. But similar to the two-faced coin, some people love conventions and some don’t like them. That’s why the Caliburn.Micro has been designed in such a way that its conventions are fully customizable and can even be turned off completely if not desired. But the motivation behind this article is useful only if you are going to use these conventions, and since they are ON by default, it’s good to know what those conventions are and how they work.

The very first convention that one encounters when using Caliburn.Micro is binding between View and ViewModel. The way Caliburn.Micro executes data binding can be explained in the following steps:

Passing the ViewModel

There is a Bootstrapper class in Caliburn.Micro which is the starting point of the application if one chooses to use this framework in application development. Here, the root ViewModel is passed to the ViewModelLocator class in order to determine how your application’s shell should be rendered. The syntax for passing the ViewModel is Bootstrapper<T>, where in place of T, we pass the name of our ViewModel. This step is taken when we follow the ViewModel-First approach. Caliburn.Micro supports both ViewModel-First and View-first approach. We can follow any of them according to our need and use. But we need to understand the difference between the two approaches. We don’t need to change our code much to implement any of these approaches. There is a slight change in the ViewModelLocator class of the Caliburn.Micro.
ViewModel first

It simply means that we have an existing ViewModel that we need to render to the screen. In this scenario, the ViewModel is responsible for creating the view and binding itself to the view. Also the binding is more straightforward because you can use a convention-based approach to map the view to the view model. Caliburn.Micro generally prefers this approach. To implement this, we pass the name of our ViewModel in a separate class called AppBootStrapper which we need to add manually. The ViewLocator.LocateForModelType looks like this:

      public static Func<Type, DependencyObject, object, UIElement> LocateForModelType = 
                   (modelType, displayLocation, context) =>
        {
            var viewTypeName = modelType.FullName.Replace("Model", string.Empty);
            if (context != null)
            {
                viewTypeName = viewTypeName.Remove(viewTypeName.Length - 4, 4);
                viewTypeName = viewTypeName + "." + context;
            }
            var viewType = (from assmebly in AssemblySource.Instance
                            from type in assmebly.GetExportedTypes()
                            where type.FullName == viewTypeName
                            select type).FirstOrDefault();
            return viewType == null ? new TextBlock {Text = string.Format("{0} not found.",               viewTypeName) } : GetOrCreateViewType(viewType);
        };  
View first

It is another approach to achieve the MVVM application where View and ViewModels are loosely coupled. Most users operate by moving through screens and most developers comprehend their applications in terms of screens. So, this approach best suits them. In this approach, it is the View which drives the creation or search of the ViewModel. The view typically binds to the view model as a resource, uses a locator pattern, or has the view model injected via MEF, Unity, or some other means. This approach is generally preferred when working with WP7. In this implementation, we need to bind our view model in XAML using command “cal:View.Model” as follows:

          <ContentControl cal:View.Model="{Binding ListingViewModel}" /> 

The code in the ViewModelLocator.LocateForViewType:

      public static Func<Type, object> LocateForViewType = viewType =>
        {
            var typeName = viewType.FullName;
            if (!typeName.EndsWith("View"))
                typeName += "View";
            var viewModelName = typeName.Replace("View", "ViewModel");
            var key = viewModelName.Substring(viewModelName.LastIndexOf(".") + 1);
            return IoC.GetInstance(null, key);
        };    

Naming convention: Here for this article, we are using ViewModel First approach and for implementing this approach, Caliburn.Micro uses a simple naming convention to find a UserControl that it should bind to the ViewModel and display. What we need to do is to append the text “ViewModel” in the name of our ViewModels, and remove the text “Model” from that name when naming the corresponding View. In other words, we can say that UI name should suffix with “View”. So suppose we are developing some application for customer client interface and name our ViewModel as CustomerViewModel, then the name of our View would be CustomerView. Example: Let’s take an example where we are implementing the tic-tac-toe game using Caliburn.Micro. The name of our View is “TTTView” and corresponding ViewModel is “TTTViewModel”. We need to add the “AppBootstrapper” class which is derived from CM’s Bootstrapper class and pass the name of our ViewModel. This class contains the following code:

namespace Caliburn_TTT
{
    class AppBootstrapper : Bootstrapper<TTTViewModel>
    {
    }
} 

How Binding Works

This is the most important step in this whole process. After the binding of View and ViewModel is complete, regardless of whether we used ViewModel-First or a View-First approach, the ViewModelBinder.Bind method is called. This method sets the Action.Target of the View to the ViewModel, i.e., the controls in the View will be updated according to the ViewModel/View based on type of binding selected in XAML file. ViewModelBinder also determines if it needs to create any conventional property bindings or actions. For this, it searches the View for a list of element candidates for bindings/actions and compares them against the properties and methods we defined in our ViewModel. When a match is found, it binds that element to the corresponding property or method.
Search for the match : There is a proper method using which Caliburn.Micro searches for the elements. It searches going up and down the tree until it finds a suitable root node for example, a Window or UserControl or element without a parent. Once it discovers such elements, it proceeds for the second task, i.e., out of these elements, searching the elements which have names. This function then returns the found elements to ViewModelBinder to apply conventions.

<UserControl x:Class="Caliburn_TTT.TTTView"
         xmlns=http://schemas.microsoft.com/winfx/2006/xaml/presentation
         xmlns:x=http://schemas.microsoft.com/winfx/2006/xaml
         xmlns:cal="http://www.caliburnproject.org">
     
     <Grid   x:Name="grid1" Margin="0,0,0,0" Height="473" VerticalAlignment="Top" 
               HorizontalAlignment="Left" Width="450">
        <Grid.RowDefinitions>
            <RowDefinition Height="20*"/>
            <RowDefinition Height="150*"/>
        </Grid.RowDefinitions>
        <Menu Height="21" Grid.ColumnSpan="3" Grid.RowSpan="2"/>
        <Button x:Name="Shape00" Background="White" Grid.Row="1">
             <TextBlock Width="150" Height="150" 
                  cal:Message.Attach="[Event DoubleClick] = [Action DrawShape(0,0)]"/>
        </Button>
        <Button x:Name="Shape01" Background="White" Grid.Row="1" Grid.Column="1">
             <TextBlock Width="150" Height="150" 
                  cal:Message.Attach="[Event DoubleClick] = [Action DrawShape(0,1)]"/>
        </Button>
     </Grid>
</UserControl>  

There are two techniques which Caliburn uses to bind a UserControl to its corresponding method:

  1. If the UserControl and the method have the same name. As in our example, the Button can be bound to a method having the same name as that of button (here “Shape00” and “Shape01”). But there are some limitations when we are using this binding technique as we can’t pass parameters and the method is bound to the default events of the controls as in case of button, the “Click” event, i.e., the method will be called when user clicks the button.
  2. Second technique is to use the command “cal:Message.Attach”. This technique overcomes the limitations of the previous one. And also, in this technique it’s not necessary that the name of the method should be the same as that the name of the control. We can give any name to the method. As in the above example, we are using the name “DrawShape” and (0,0) is being passed as parameters. "[Event DoubleClick]” represents when the method will be called. In this case, doubleclick on the TextBlock will call the method DrawShape. Similarly, we can give any event on occurrence of which we want our method to get called like click, keypress, etc.

Search of Methods

After the elements get located, ViewModelBinder searches for the corresponding methods in the ViewModel for convention bindings. It first searches for the public methods in the ViewModel. Then it goes for case sensitive string comparison of the element name and the method name. After the match is found, it binds the element functionality to that method. The methods in the following code “Shape00” and “Shape01” are bound to the TextBlock defined in the part of code above. And the method “DrawShape” is bound to the click of the button. Row and Column number has been passed as the parameter.

public string Shape00
        { _______;
          _______;            }
public string Shape01
        {_________;
         _________;
        }
public void DrawShape(int row, int col)
        {____________;
         ____________;
        }  

Binding Multiple Views to a Single ViewModel

This is another one of the various capabilities of Caliburn.Micro. It also supports multiple Views of same ViewModel, i.e., one can present different view to the user on the basis of some event occurrence bound to the same ViewModel. Generally, beginners face issue with using the Caliburn.Micro’s convention property in this case. However, we don’t need this for our example but it is being explained for general issue.
Example: A ViewModel "MainViewModel" is bound to "MainView" through convention. But after event occurs, we want the application to be displaying "MainView2" instead of "MainView". This can be solved as follows: We need to use “cal:View.Context” attached property and then name our view like OurNamespace.Something.ContextView (removing "ViewModel" from our view model name, add a dot, and the value of Context property). Using this, we can even bind several views to one view model.

<ContentControl x:Name="Toolbar" cal:View.Model="{Binding ActiveItem}" cal:View.Context="Toolbar" /> 

According to the above code, name of your ViewModel should be Something.Toolbar.

Conclusion

It becomes less cumbersome when we have to bind View with ViewModel using Caliburn.Micro. This helps in enforcing MVVM. Most of the times, we can get rid of xaml.cs file whose presence always runs the risk of view contamination.
From the above, we can easily judge that using Caliburn.Micro makes life easier in case of Data Binding. But its uses are not limited to Binding only. Binding is just one of the many features which gets simplified by Caliburn.Micro.

References

  1. http://en.wikipedia.org/wiki/Data_binding
  2. http://msdn.microsoft.com/en-US/
  3. http://www.codeproject.com/script/Articles/Submit.aspx
  4. http://www.mindscapehq.com/blog/index.php/2012/01/16/caliburn-micro-part-2-data-binding-and-events/
  5. http://caliburnmicro.codeplex.com/documentation
  6. http://en.wikipedia.org/wiki/Model_View_ViewModel

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Amar Sneh
Software Developer Harman International
India India
I love to work on WPF, HTML5 and cross platform development using QT framework.

Comments and Discussions

 
QuestionGood article for beginners! PinmemberMember 1048766627-May-14 20:57 
GeneralMy vote of 5 PinmemberMember 1084754327-May-14 8:37 
QuestionGreat!!! PinprofessionalPraveen Raghuvanshi26-May-14 21:01 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web03 | 2.8.141223.1 | Last Updated 27 May 2014
Article Copyright 2014 by Amar Sneh
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid