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

Introduction to using MVVM with Visual C++ 2012

, 23 Mar 2013 CPOL
Rate this:
Please Sign up or sign in to vote.
Writing a basic Windows Store Hello World equivalent using VC++ and MVVM

Introduction

This is an introductory article on using the MVVM pattern with Visual C++ 2012 to write Windows Store applications. The article will cover how to get data binding working with ref classes, hooking up the view models with the XAML views, using commands, setting styles in XAML, creating and using a value converter, and using item templates with a ListBox control. The article assumes that you are familiar with MVVM and C++/CX, and also that you have a fundamental understanding of using XAML views.

Setting up data binding

Getting a class ready for data binding involves two steps:

  1. Adding the [Bindable] attribute on the class
  2. Implementing INotifyPropertyChanged on the class
[Bindable] 
public ref class Restaurant sealed : BindableBase
{
private:
  String^ name;
  String^ city;
  String^ notes;
  int rating;

public:
  property String^ Name 
  { 
    String^ get(); 
    void set(String^ value); 
  }

  property String^ City 
  { 
    String^ get(); 
    void set(String^ value); 
  }

  property String^ Notes 
  { 
    String^ get(); 
    void set(String^ value); 
  }

  property int Rating
  { 
    int get(); 
    void set(int value); 
  }
};

Adding the Bindable attribute involves one additional step to get the code to compile. This is due to a quirk in the XAML compiler, and I've blogged about it here:

You need to add an include to the header file in any of your xxx.xaml.h files and the code will compile fine. The compiler will auto-generate plumbing code required for data binding in XamlTypeInfo.g.cpp once it sees the Bindable attribute on a class. Here's a small snippet showing the sort of code it generates for us.

if (typeName == L"MvvmHelloWorld.ViewModels.Restaurant")
{
    ::XamlTypeInfo::InfoProvider::XamlUserType^ userType = ref new 
      ::XamlTypeInfo::InfoProvider::XamlUserType(
        this, 
        typeName, 
        GetXamlTypeByName(L"MvvmHelloWorld.DataBinding.BindableBase"));
    userType->KindOfType = ::Windows::UI::Xaml::Interop::TypeKind::Custom;
    userType->Activator =
        []() -> Platform::Object^ 
        {
            return ref new ::MvvmHelloWorld::ViewModels::Restaurant(); 
        };
    userType->AddMemberName(L"Rating");
    userType->AddMemberName(L"Notes");
    userType->AddMemberName(L"City");
    userType->AddMemberName(L"Name");
    userType->SetIsBindable();
    return userType;
}

Instead of implementing INotifyPropertyChanged for each data-bindable class, the common practice is to have a base class that all view models would implement. I've used BindableBase which implements INotifyPropertyChanged.

public ref class BindableBase : DependencyObject, INotifyPropertyChanged 
{
public:
  virtual event PropertyChangedEventHandler^ PropertyChanged;

protected:
  virtual void OnPropertyChanged(String^ propertyName);
};

The OnPropertyChanged is fairly simple.

void BindableBase::OnPropertyChanged(String^ propertyName)
{
  PropertyChanged(this, ref new PropertyChangedEventArgs(propertyName));
}

Now, you just need to implement the property bodies, and call OnPropertyChanged in the setter methods.

String^ Restaurant::Name::get()
{
  return name;
}

void Restaurant::Name::set(String^ value)
{
  if(name != value)
  {
    name = value;
    OnPropertyChanged("Name");
  }
}

int Restaurant::Rating::get()
{
  return rating;
}

void Restaurant::Rating::set(int value)
{
  if(rating != value)
  {
    if(value < 1) 
    {
      value = 1;
    }
    else if(value > 5)
    {
      value = 5;
    }

    rating = value;
    OnPropertyChanged("Rating");
  }
}

Bind to the VM data in XAML

For the main view, it's quite common to have a main view model class. And this is also marked as [Bindable] and derives from BindableBase.

[Bindable] 
public ref class MainViewModel sealed : BindableBase
{

For the example project, I've also added these properties.

property String^ Title
{
  String^ get()
  {
    return "MVVM Hello World with Visual C++";
  }
}

property IObservableVector<Restaurant^>^ Restaurants
{
  IObservableVector<Restaurant^>^ get();
}

property Restaurant^ SelectedRestaurant
{
  Restaurant^ get();
  void set(Restaurant^ value);
}

Notice how the Restaurants property is of type IObservableVector<Restaurant^>. This interfaces notifies listeners when the collection changes and is basically the collection equivalent of INotifyPropertyChanged. The backing storage variable in the example project is of type Vector<Restaurant^> which is a library collection type that implements IObservableVector<T>. I don't want to clutter up the article with full XAML listings, so I'll just show some of the relevant snippets here.

<TextBlock Style="{StaticResource HeaderTextStyle}" 
           Margin="10,10,0,10" Text="{Binding Title}" />

Notice the binding there, which sets the text of that control to the value of the Title property. If you've never used XAML before, then this might look a little strange initially. The reason it's able to find the Title property is because I've hooked up the data context for the Page to an instance of the MainViewModel class.

<Page
    x:Class="MvvmHelloWorld.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:MvvmHelloWorld"
    xmlns:localVM="using:MvvmHelloWorld.ViewModels"
    xmlns:localConv="using:MvvmHelloWorld.Converters"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    DataContext="{Binding Source={StaticResource MainViewModel}}">

And MainViewModel itself is defined as a static resource in App.xaml.

<Application.Resources>
    <ResourceDictionary>
        <localVM:MainViewModel x:Key="MainViewModel" />
    </ResourceDictionary>
</Application.Resources>

The Restaurants property is data bound to a ListBox that uses a custom item template.

<ListBox ItemsSource="{Binding Restaurants}" Width="500" Margin="10" Height="500"
     HorizontalAlignment="Left"
     SelectedItem="{Binding SelectedRestaurant, Mode=TwoWay}">
    <ListBox.ItemTemplate>
        <DataTemplate>
            <StackPanel Orientation="Vertical">
                <TextBlock Text="{Binding Name}" 
                    Style="{StaticResource RestaurantTitleTextStyle}" />
                <TextBlock Text="{Binding City}" 
                    Style="{StaticResource RestaurantSubTitleTextStyle}" />
                <TextBlock Text="{Binding Rating, Converter={StaticResource RatingConverter}}" 
                    Style="{StaticResource RestaurantSubTitleTextStyle}" />
            </StackPanel>
        </DataTemplate>
    </ListBox.ItemTemplate>
</ListBox>

Notice how the SelectedItem property on the ListBox is two-way bound to the SelectedRestaurant property on the view model. This way the VM can track the selected restaurant, and a possible details view can data bind to that property (and we do that in the example). Notice the converter used for rating, I talk about it a little later in this article.

<TextBlock Text="{Binding SelectedRestaurant.Name}" 
    Style="{StaticResource RestaurantTitleTextStyle}" />
<TextBox Text="{Binding SelectedRestaurant.City, Mode=TwoWay}" 
    Style="{StaticResource RestaurantSubTitleTextBoxStyle}" />
<TextBox Text="{Binding SelectedRestaurant.Notes, Mode=TwoWay}" 
    Style="{StaticResource RestaurantSubTitleTextBoxStyle}" />
<TextBox Text="{Binding SelectedRestaurant.Rating, Mode=TwoWay}" 
    Style="{StaticResource RestaurantSubTitleTextBoxStyle}" />

This serves as a sort of details view, and is bound to the SelectedItem property. So when the user selects a restaurant in the ListBox, this view is automatically updated. And when the details view is updated by the user, the item in the ListBox is automatically updated (because of the TwoWay bindings). The TextBox values bind only when focus leaves the control, so I've added a dummy button that the user can click for binding to fire.

<Button Content="Update" Style="{StaticResource MediumButtonStyle}" 
      Width="120" Margin="5" />

Note that you can write a custom behavior to have binding fire when the text changes, but this is not standard behavior at the moment.

Using styles

I've applied styles on the controls wherever possible.

<TextBlock Text="{Binding Name}" 
    Style="{StaticResource RestaurantTitleTextStyle}" />
<TextBlock Text="{Binding City}" 
    Style="{StaticResource RestaurantSubTitleTextStyle}" />

<Button Content="Update" Style="{StaticResource MediumButtonStyle}" 
    Width="120" Margin="5" />

These styles come off a style dictionary (defined in XAML). The dictionary is referenced in App.xaml.

<Application.Resources>
    <ResourceDictionary>
        <ResourceDictionary.MergedDictionaries>
            <ResourceDictionary Source="Common/StandardStyles.xaml"/>
        </ResourceDictionary.MergedDictionaries>

Defining styles is fairly straightforward.

<Style x:Key="RestaurantTitleTextStyle" TargetType="TextBlock">
    <Setter Property="FontSize" Value="24"/>
    <Setter Property="FontWeight" Value="Bold"/>
</Style>

<Style x:Key="RestaurantTitleGreenTextStyle" TargetType="TextBlock" 
            BasedOn="{StaticResource RestaurantTitleTextStyle}">
    <Setter Property="Foreground" Value="GreenYellow"/>
</Style>

<Style x:Key="RestaurantSubTitleTextStyle" TargetType="TextBlock">
    <Setter Property="FontSize" Value="18"/>
</Style>

<Style x:Key="RestaurantSubTitleTextBoxStyle" TargetType="TextBox">
    <Setter Property="FontSize" Value="18"/>
    <Setter Property="Margin" Value="5"/>
</Style>

<Style x:Key="MediumButtonStyle" TargetType="Button">
    <Setter Property="FontSize" Value="22"/>
</Style>

It's analogous to using CSS to define styles in HTML. Style editing is not very smooth with VS 2012 and the IDE does not offer much help when you edit the style dictionary. Blend may be a little more functional there but may involve a bit of  learning curve and getting used to. One neat thing is how you can have the same project open at the same time in both VS 2012 and Blend.

Using a value converter

If you remember, the rating UI in the ListBox item template uses a value converter.

<TextBlock Text="{Binding Rating, Converter={StaticResource RatingConverter}}" 
    Style="{StaticResource RestaurantSubTitleTextStyle}" />

The converter instance is defined as a static resource within the page.

<Page.Resources>
    <localConv:RatingConverter x:Key="RatingConverter" />
</Page.Resources>

Converter classes are basically implementations of IValueConverter.

public ref class RatingConverter sealed : IValueConverter
{
public:

  virtual Object^ Convert(Object^ value, 
      TypeName targetType, Object^ parameter, String^ language)
  {
    auto boxedInt = dynamic_cast<Box<int>^>(value);
    auto intValue = boxedInt != nullptr ? boxedInt->Value : 1;

    return "Rating : " + ref new String(std::wstring(intValue, '*').c_str());
  }

  virtual Object^ ConvertBack(Object^ value, 
      TypeName targetType, Object^ parameter, String^ language)
  {
    return value;
  } 
};

My implementation only handles one way conversion and converts the integer into a string of asterisks. This is a fairly simple implementation but converters can be quite powerful and any serious project would quite likely see you creating a fair number of them.

Setting up commands

The last thing I wanted to cover in this article is the use of commands. The ICommand interface is what command objects implement to support command binding. I use a very simple implementation that will be familiar to anyone who's used any basic MVVM library.

public delegate void ExecuteDelegate(Object^ parameter);
public delegate bool CanExecuteDelegate(Object^ parameter);

[WebHostHidden]
public ref class DelegateCommand sealed : public ICommand
{
private:
  ExecuteDelegate^ executeDelegate;
  CanExecuteDelegate^ canExecuteDelegate;
  bool lastCanExecute;    

public:
  DelegateCommand(ExecuteDelegate^ execute, CanExecuteDelegate^ canExecute);

  virtual event EventHandler<Object^>^ CanExecuteChanged;
  virtual void Execute(Object^ parameter);
  virtual bool CanExecute(Object^ parameter);
};

The main VM class defines a couple of command properties.

property ICommand^ AddRestaurantCommand;
property ICommand^ DeleteRestaurantCommand;

The commands are initialized in the VM constructor.

MainViewModel::MainViewModel()
{
  AddRestaurantCommand = ref new DelegateCommand(
    ref new ExecuteDelegate(this, &MainViewModel::AddRestaurant),
    nullptr);

  DeleteRestaurantCommand = ref new DelegateCommand(
    ref new ExecuteDelegate(this, &MainViewModel::DeleteRestaurant),
    nullptr);

AddRestaurant and DeleteRestaurant are private class methods.

void MainViewModel::AddRestaurant(Object^ parameter)
{
  auto restaurant = ref new Restaurant();
  restaurant->Name = NewName;
  restaurant->City = "unassigned";
  restaurant->Notes = "unassigned";
  restaurant->Rating = 1;
  restaurants->Append(restaurant);

  SelectedRestaurant = restaurant;
}

void MainViewModel::DeleteRestaurant(Object^ parameter)
{
  if(SelectedRestaurant != nullptr)
  {
    unsigned int index;
    if(restaurants->IndexOf(SelectedRestaurant, &index))
    {
      restaurants->RemoveAt(index);
      SelectedRestaurant = nullptr;
    }
  }
}

And then the commands are hooked up to buttons in the XAML.

<Button Content="Delete" Command="{Binding DeleteRestaurantCommand}" 
    Width="120" Margin="5" Style="{StaticResource MediumButtonStyle}" />

<Button Content="Add Restaurant" Command="{Binding AddRestaurantCommand}" 
    Style="{StaticResource MediumButtonStyle}" />

Conclusion

Alright, that's all. I do intend to write a series of articles that will cover more topics on writing Windows Store apps using Visual C++, although I am not sure if I'd do them in any particular order. As always, do send in your feedback and criticism via the forum at the bottom of this article. Thank you.

History

  • March 23rd, 2013 - Article published

License

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

Share

About the Author

Nish Sivakumar

United States United States
Nish is a real nice guy who has been writing code since 1990 when he first got his hands on an 8088 with 640 KB RAM. Originally from sunny Trivandrum in India, he has been living in various places over the past few years and often thinks it’s time he settled down somewhere.
 
Nish has been a Microsoft Visual C++ MVP since October, 2002 - awfully nice of Microsoft, he thinks. He maintains an MVP tips and tricks web site - www.voidnish.com where you can find a consolidated list of his articles, writings and ideas on VC++, MFC, .NET and C++/CLI. Oh, and you might want to check out his blog on C++/CLI, MFC, .NET and a lot of other stuff - blog.voidnish.com.
 
Nish loves reading Science Fiction, P G Wodehouse and Agatha Christie, and also fancies himself to be a decent writer of sorts. He has authored a romantic comedy Summer Love and Some more Cricket as well as a programming book – Extending MFC applications with the .NET Framework.
 
Nish's latest book C++/CLI in Action published by Manning Publications is now available for purchase. You can read more about the book on his blog.
 
Despite his wife's attempts to get him into cooking, his best effort so far has been a badly done omelette. Some day, he hopes to be a good cook, and to cook a tasty dinner for his wife.

Comments and Discussions

 
BugYour code does not build in windows 8.1 [modified] PinmemberTombstarship13-Feb-14 18:59 
GeneralRe: Your code does not build in windows 8.1 PinmvpNish Sivakumar14-Feb-14 3:03 
GeneralMy vote of 5 PinmemberMihai MOGA12-Apr-13 20:00 
GeneralRe: My vote of 5 PinsitebuilderNish Sivakumar13-Apr-13 8:32 
GeneralMy vote of 5 PinmvpEspen Harlinn24-Mar-13 10:22 
Really nice Nish, a good introduction Smile | :)
GeneralRe: My vote of 5 PinsitebuilderNish Sivakumar25-Mar-13 2:51 

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
Web04 | 2.8.141216.1 | Last Updated 23 Mar 2013
Article Copyright 2013 by Nish Sivakumar
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid