C# – WPF MVVM DataBinding 2014

It’s already 6 years since my first post about MVVM and data bindings in WPF MVVM – ListView SelectedItem and DataBinding. The MVVM pattern is now quite popular and comes into use in many WPF applications. However a lot of things have changed since 2008. We have now many new platforms where we can also apply MVVM pattern such as Windows 8 app, Windows phone app… When you use Xamarin (http://xamarin.com/) to develop cross platform applications with C#, maybe you want to use MVVM pattern too. In my last post 6 years ago, I introduced to you the concept of MVVM in ‘raw’ format which means no use of any external frameworks. Today I would like to make a supplementary post by introducing 2 MVVM frameworks for easing the use of MVVM pattern. The first one is Caliburn.Micro https://github.com/Caliburn-Micro/Caliburn.Micro and the second one is Mvvm Cross https://github.com/MvvmCross/MvvmCross . They’re both open source products and can be used in any commercial applications.

1. Caliburn.Micro

Caliburn.Micro is a small, yet powerful framework, designed for building applications across all XAML platforms. Its strong support for MV* patterns will enable you to build your solution quickly, without the need to sacrifice code quality or testability.

I’ve used this framework for years in some projects at work. The nice feature of this framework is naming convention saving us from losing time for writing declaration code, keeping code structure clean with high quality and be testable. In some advanced case, this naming convention makes me confused because of making View and ViewModel talking to each other is pretty tricky. However in this blog we will only make a very simple app with Caliburn.Micro, it’s just a step by step tutorial for beginners with Caliburn.Micro.

1. In Visual Studio, create a WPF Application

WPF Application

2. Add reference to Caliburn.Micro Nuget package

Caliburn.Micro Nuget Package

3. Add new interface IShell. We’ll use this interface to tell which View should be loaded on start up. This IShell will define an entry point. Caliburn.Micro or other MVVM framworks will always hook our application to enhance the data bindings. For them, there are only Views and ViewModels, no start up windows, main form or something like that. We have to define an entry point so that they know which ViewModel/View they should load on starting up. MvvmCross will need also this entry point which we’ll learn in next section.

4. Add new class AppBootstrapper with following code

internal class AppBootstrapper : BootstrapperBase
{
	private CompositionContainer container;

	public AppBootstrapper()
	{
		Initialize();
	}

	protected override void BuildUp(object instance)
	{
		container.SatisfyImportsOnce(instance);
	}

	protected override void Configure()
	{
		AggregateCatalog catalog = new AggregateCatalog(AssemblySource.Instance.Select(x => new AssemblyCatalog(x)).OfType<ComposablePartCatalog>());
		container = new CompositionContainer(catalog);

		var batch = new CompositionBatch();
		batch.AddExportedValue<IWindowManager>(new WindowManager());
		batch.AddExportedValue<IEventAggregator>(new EventAggregator());

		batch.AddExportedValue(container);

		container.Compose(batch);
	}

	protected override IEnumerable<object> GetAllInstances(Type serviceType)
	{
		return container.GetExportedValues<object>(AttributedModelServices.GetContractName(serviceType));
	}

	protected override object GetInstance(Type serviceType, string key)
	{
		string contract = string.IsNullOrEmpty(key) ? AttributedModelServices.GetContractName(serviceType) : key;
		var exports = container.GetExportedValues<object>(contract);

		if (exports.Any())
			return exports.First();

		throw new Exception(string.Format("Could not locate any instances of contract {0}.", contract));
	}

	protected override void OnStartup(object sender, StartupEventArgs e)
	{
		DisplayRootViewFor<IShell>();
	}

	protected override IEnumerable<Assembly> SelectAssemblies()
	{
		List<Assembly> assemblies = new List<Assembly>();
		assemblies.Add(Assembly.GetExecutingAssembly());
		//assemblies.Add(System.Reflection.Assembly.GetAssembly(typeof(RepPixViewerProjectDAO)));
		//assemblies.Add(System.Reflection.Assembly.GetAssembly(typeof(DefaultLocalizer)));

		return assemblies;
	}
}

As I mentioned above, the data bindings will be enhanced by MVVM framework for MVVM pattern. So Caliburn.Micro needs a bootstrapper where we tell him where he can find ViewModel/View and which ViewModel/View is going to be the main window of our apps. In the bootstrapper, we override the OnStartup() and SelectAssemblies() functions to set the start up ViewModel/View and where Caliburn.Micro can find ViewModel/View. I’ll use MEF as our dependency injection container so in the Configure function, I declare a AggregateCatalog to aggregate the dependencies from SelectAssemblies() and from our manual defined ones (IWindowManager and IEventAggregator). Caliburn.Micro also supports many other dependency injection frameworks but I prefer a standard one MEF from Microsoft.

5. Edit App.xaml to load our boot strapper at start up

<Application x:Class="Mvvm_DataBindings_Caliburn.Micro.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:local="clr-namespace:Mvvm_DataBindings_Caliburn.Micro">
    <Application.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary>
                    <local:AppBootstrapper x:Key="bootstrapper" />
                </ResourceDictionary>
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Application.Resources>
</Application>

5. Create Views and ViewModels folder in project.

6. Create a MainViewModel and a MainView (type of Window (WPF)) in appropriated folder. Caliburn.Micro uses naming convention for locating ViewModel for View (or vice versa) therefore we have to following rules of *ViewModel and *View so that they can find and talk to each other.

7. In our demo application, I will bind a ListView to an ObservableCollection() like image below

ListView Binding

8. In MainView.xaml I define ListView as following

<Window x:Class="Mvvm_DataBindings_Caliburn.Micro.Views.MainView"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainView" Height="300" Width="500" WindowStartupLocation="CenterScreen">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="*"></RowDefinition>
            <RowDefinition Height="35"></RowDefinition>
        </Grid.RowDefinitions>
        <ListView Name="Items">
            <ListView.View>
                <GridView>
                    <GridViewColumn Width="200" Header="First name" DisplayMemberBinding="{Binding FirstName}"></GridViewColumn>
                    <GridViewColumn Width="200" Header="Last name" DisplayMemberBinding="{Binding LastName}"></GridViewColumn>
                </GridView>
            </ListView.View>
        </ListView>
        <StackPanel Grid.Row="1" Orientation="Horizontal">
            <TextBlock VerticalAlignment="Center" Text="Selected item:"></TextBlock>
            <TextBlock Margin="5,0,0,0" VerticalAlignment="Center" Text="{Binding SelectedItemDisplayText}" ></TextBlock>
        </StackPanel>
    </Grid>
</Window>

9. In .xaml code above, I haven’t write any code for binding DataSource as well as SelectedItem for ListView excepts giving a name for the ListView. That’s not normal,isn’t it? However it’ll work anyway thanks to naming convention supporting by Caliburn.Micro. To make this naming convention work, in MainViewModel we have to define 2 properties : Items(for DataSource) and SelectedItem (for SelectedItem) as following

using Caliburn.Micro;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel.Composition;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Mvvm_DataBindings_Caliburn.Micro.ViewModels
{
    [Export(typeof(IShell))]
    internal class MainViewModel : Screen, IShell
    {
        private ObservableCollection<Contact> items;
        private Contact selectedItem;

        public MainViewModel()
        {
            DisplayName = "DataBindings with Caliburn.Micro";
        }

        public ObservableCollection<Contact> Items
        {
            get { return items; }
            set
            {
                if (value != items)
                {
                    items = value;
                    NotifyOfPropertyChange(() => Items);
                }
            }
        }

        public Contact SelectedItem
        {
            get { return selectedItem; }
            set
            {
                if (value != selectedItem)
                {
                    selectedItem = value;
                    NotifyOfPropertyChange(() => SelectedItem);
                    NotifyOfPropertyChange(() => SelectedItemDisplayText);
                }
            }
        }

        public string SelectedItemDisplayText { get { return SelectedItem != null ? SelectedItem.DisplayText : null; } }

        protected override void OnViewAttached(object view, object context)
        {
            Items = new ObservableCollection<Contact>(new List<Contact>()
            {
                new Contact("Noah","Smith"),
                new Contact("Sophia","Johnson"),
                new Contact("Liam","Williams"),
                new Contact("Emma","Jones"),
                new Contact("Jacob","Brown"),
                new Contact("Olivia","Davis"),
                new Contact("Mason","Miller"),
                new Contact("Isabella","Wilson"),
                new Contact("William","Moore"),
                new Contact("Ava","Taylor")
            });
        }
    }
}
public class Contact
{
	public Contact(string firstName, string lastName)
	{
		this.FirstName = firstName;
		this.LastName = lastName;
	}

	public string DisplayText { get { return string.Format("{0},{1}", LastName, FirstName); } }

	public string FirstName { get; set; }

	public string LastName { get; set; }
}

Code structure of MainViewModel is clear and clean. We don’t have to write too much declaration code like other MVVM framework to make data bindings work. What important are the names of properties must be correct otherwise the Caliburn.Micro can’t load data correctly. Don’t forget to inject MainViewModel as a implementation of IShell so that Caliburn.Micro knows which ViewModel/View he should load for starting up.

10. Caliburn.Micro is a lightweight framework for enhancing MVVM pattern in WPF, Silverlight, Windows Phone, Windows 8 including universal apps. Thanks to naming convention, we can save a lot of time for writing binding code. If you always stay in Microsoft platform, I think Caliburn.Micro is a good candidate for applying MVVM pattern.

2. MvvmCross

The rapid growth of smartphones and tablets with different operating system such as Android, IOS… presents us new (or old) challenge with writing code once and running it on many platforms as possible. If you know what Xamarin is, you know what I’m meaning here. Xamarin provides us a platform so that we can develop our app with C# and deploy to many other platforms. What Xamrain is doing now, is very promising especially with an announced cooperation between them and Microsoft, I think they will make a really big thing for C# developer in one day.

And when we have Xamarin platform to develop universal apps, we’ll need an MVVM framework which runs on it so that we can apply MVVM pattern into that universal app too. And that’s what MvvmCross (https://github.com/MvvmCross/MvvmCross) is built for.

MvvmCross: The Mvvm Platform for Xamarin.iOS, Xamarin.Android, WindowsPhone, WindowsStore, WPF and Mac. Includes databinding support for Android XML, for iOS XIBs and for MonoTouch.Dialog.

We will create a demo app as same as demo above with MvvmCross.

1. Create a new WPF application

2. Be sure that you are setting .Net Framework 4.5 as target framework

.Net Framework 4.5

3. Add reference to Nuget Package MvvmCross

MvvmCross Nuget Package

4. Create a Views and ViewModels folders like in Caliburn.Micro

5. We also need a MainView and MainViewModel. But the code is now slightly different

5.1 MainView

public partial class MainView : MvxWpfView
{
	public MainView()
	{
		InitializeComponent();
	}

	public new MainViewModel ViewModel
	{
		get { return (MainViewModel)base.ViewModel; }
		set { base.ViewModel = value; }
	}
}

Now we have to explicitly define which ViewModel we want to use now for MainView (no naming convention anymore). The ViewModel property comes from base class MvxWpfView and has to be redefined to deliver the correct ViewModel.

<views:MvxWpfView
    xmlns:views="clr-namespace:Cirrious.MvvmCross.Wpf.Views;assembly=Cirrious.MvvmCross.Wpf"
    x:Class="Mvvm_DataBindings_MvvmCross.Views.MainView"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="*"></RowDefinition>
            <RowDefinition Height="35"></RowDefinition>
        </Grid.RowDefinitions>
        <ListView ItemsSource="{Binding Items}" SelectedItem="{Binding SelectedItem}">
            <ListView.View>
                <GridView>
                    <GridViewColumn Width="200" Header="First name" DisplayMemberBinding="{Binding FirstName}"></GridViewColumn>
                    <GridViewColumn Width="200" Header="Last name" DisplayMemberBinding="{Binding LastName}"></GridViewColumn>
                </GridView>
            </ListView.View>
        </ListView>
        <StackPanel Grid.Row="1" Orientation="Horizontal">
            <TextBlock VerticalAlignment="Center" Text="Selected item:"></TextBlock>
            <TextBlock Margin="5,0,0,0" VerticalAlignment="Center" Text="{Binding SelectedItemDisplayText}" ></TextBlock>
        </StackPanel>
    </Grid>
</views:MvxWpfView>

The .xml code is almost same except that our View has to be now a MvxWpfView, not a normal UserControl anymore.

5.2. MainViewModel

public class MainViewModel : MvxViewModel
{
	private ObservableCollection<Contact> items;
	private Contact selectedItem;

	public MainViewModel()
	{
		//DisplayName = "DataBindings with Caliburn.Micro";
	}

	public ObservableCollection<Contact> Items
	{
		get { return items; }
		set
		{
			if (value != items)
			{
				items = value;
				RaisePropertyChanged(() => Items);
			}
		}
	}

	public Contact SelectedItem
	{
		get { return selectedItem; }
		set
		{
			if (value != selectedItem)
			{
				selectedItem = value;
				RaisePropertyChanged(() => SelectedItem);
				RaisePropertyChanged(() => SelectedItemDisplayText);
			}
		}
	}

	public string SelectedItemDisplayText { get { return SelectedItem != null ? SelectedItem.DisplayText : null; } }

	public override void Start()
	{
		Items = new ObservableCollection<Contact>(new List<Contact>()
		{
			new Contact("Noah","Smith"),
			new Contact("Sophia","Johnson"),
			new Contact("Liam","Williams"),
			new Contact("Emma","Jones"),
			new Contact("Jacob","Brown"),
			new Contact("Olivia","Davis"),
			new Contact("Mason","Miller"),
			new Contact("Isabella","Wilson"),
			new Contact("William","Moore"),
			new Contact("Ava","Taylor")
		});
		base.Start();
	}
}

There are only small differences between MvvmCross ViewModel and Caliburn.Micro ViewModel. They are using different name for the delegates of notifying property changes and start up function but the concept and structure are equivalent. For MvvmCross we also need to define our own ‘bootstrapper’ to declare start up ViewModel/View.

6. Create a App.Xaml.Mvx.cs class and add following code

public partial class App : Application
{
	private bool _setupComplete;

	private void DoSetup()
	{
		LoadMvxAssemblyResources();
		
		var presenter = new MvxSimpleWpfViewPresenter(MainWindow);

		var setup = new Setup(Dispatcher, presenter);
		setup.Initialize();

		var start = Mvx.Resolve<IMvxAppStart>();
		start.Start();

		_setupComplete = true;
	}

	protected override void OnActivated(EventArgs e)
	{
		if (!_setupComplete)
			DoSetup();

		base.OnActivated(e);
	}
	
	private void LoadMvxAssemblyResources()
	{
		for (var i = 0;; i++)
		{
			string key = "MvxAssemblyImport" + i;
			var data = TryFindResource(key);
			if (data == null)
				return;
		}
	}
}

7. A Setup class is also required

public class Setup
	: MvxWpfSetup
{
	public Setup(Dispatcher dispatcher, IMvxWpfViewPresenter presenter)
		: base(dispatcher, presenter)
	{
	}

	protected override IMvxApplication CreateApp()
	{
		return new CoreApp();
	}

	protected override IMvxTrace CreateDebugTrace()
	{
		return new DebugTrace();
	}
}

8. CoreApp is where we define our start up View

internal class CoreApp : MvxApplication
{
	public CoreApp()
	{
		Mvx.RegisterSingleton<IMvxAppStart>(new MvxAppStart<MainViewModel>());
	}
}

Setup and CoreApp works together for building up a ‘bootstrapper’ like in Caliburn.Micro. There is still one point that I’m not sure if MvvmCross supports MEF and other DI frameworks. I’ll check it later.

3. Conclusion

MVVM pattern is quite popular today. If you make your apps only for Microsoft platform, I warmly recommend Caliburn.Micro. It’s lightweight. At the beginning you may need to spend more time for understanding how it works but later when you know the basics it’ll get more easier. The MvvmCross is new, I don’t have any experience with it but I see it’s very promising. In combination with Xamarin I think we can save a lot of work for developing universal apps. However concept is only theory, reality will punch our faces with tough issues but just try it. It’ll be fun.

Source code: https://bitbucket.org/hintdesk/dotnet-wpf-mvvm-databinding-2014

Leave a Reply

Your email address will not be published. Required fields are marked *