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:
- Adding the
[Bindable]
attribute on the class - 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