Click here to Skip to main content
15,868,016 members
Articles / Desktop Programming / XAML
Article

Introduction to using MVVM with Visual C++ 2012

Rate me:
Please Sign up or sign in to vote.
4.81/5 (17 votes)
23 Mar 2013CPOL5 min read 45.3K   668   23   6
Writing a basic Windows Store Hello World equivalent using VC++ and MVVM

Image 1

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
C++
[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.

C++
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

C++
INotifyPropertyChanged
.

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

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

The OnPropertyChanged is fairly simple.

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

Now, you just need to implement the property bodies, and call

C++
OnPropertyChanged
in the setter methods.

C++
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

C++
BindableBase
.

C++
[Bindable] 
public ref class MainViewModel sealed : BindableBase
{

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

C++
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

C++
IObservableVector<Restaurant^>
. This interfaces notifies listeners when the collection changes and is basically the collection equivalent of
C++
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.

XML
<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.

XML
<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.

XML
<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.

XML
<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.

XML
<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.

XML
<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.

XML
<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.

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

Defining styles is fairly straightforward.

XML
<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.

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

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

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

Converter classes are basically implementations of IValueConverter.

C++
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.

C++
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.

C++
property ICommand^ AddRestaurantCommand;
property ICommand^ DeleteRestaurantCommand;

The commands are initialized in the VM constructor.

C++
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.

C++
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.

XML
<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)


Written By
United States United States
Nish Nishant is a technology enthusiast from Columbus, Ohio. He has over 20 years of software industry experience in various roles including Chief Technology Officer, Senior Solution Architect, Lead Software Architect, Principal Software Engineer, and Engineering/Architecture Team Leader. Nish is a 14-time recipient of the Microsoft Visual C++ MVP Award.

Nish authored C++/CLI in Action for Manning Publications in 2005, and co-authored Extending MFC Applications with the .NET Framework for Addison Wesley in 2003. In addition, he has over 140 published technology articles on CodeProject.com and another 250+ blog articles on his WordPress blog. Nish is experienced in technology leadership, solution architecture, software architecture, cloud development (AWS and Azure), REST services, software engineering best practices, CI/CD, mentoring, and directing all stages of software development.

Nish's Technology Blog : voidnish.wordpress.com

Comments and Discussions

 
BugYour code does not build in windows 8.1 Pin
Tombstarship13-Feb-14 17:59
Tombstarship13-Feb-14 17:59 
GeneralRe: Your code does not build in windows 8.1 Pin
Nish Nishant14-Feb-14 2:03
sitebuilderNish Nishant14-Feb-14 2:03 
GeneralMy vote of 5 Pin
Ștefan-Mihai MOGA12-Apr-13 19:00
professionalȘtefan-Mihai MOGA12-Apr-13 19:00 
GeneralRe: My vote of 5 Pin
Nish Nishant13-Apr-13 7:32
sitebuilderNish Nishant13-Apr-13 7:32 
GeneralMy vote of 5 Pin
Espen Harlinn24-Mar-13 9:22
professionalEspen Harlinn24-Mar-13 9:22 
GeneralRe: My vote of 5 Pin
Nish Nishant25-Mar-13 1:51
sitebuilderNish Nishant25-Mar-13 1:51 

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

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