The ViewModel layer is contained in the CalcViewModel project and serves as the intermediary between the View (XAML) and Model (CalcManager). ViewModels expose data for UI binding and handle UI-specific logic without coupling to specific UI controls.
Project Structure
src/CalcViewModel/
├── ApplicationViewModel.h
├── StandardCalculatorViewModel.h
├── DateCalculatorViewModel.h
├── UnitConverterViewModel.h
├── HistoryViewModel.h
├── MemoryItemViewModel.h
└── Common/
├── Utils.h # Observable property macros
├── DelegateCommand.h
└── ...
ViewModel Hierarchy
The ViewModel structure mirrors the View hierarchy:
ApplicationViewModel Root ViewModel for MainPage.xaml Manages mode switching and coordinates child ViewModels
StandardCalculatorViewModel ViewModel for Calculator.xaml Handles Standard, Scientific, and Programmer modes
DateCalculatorViewModel ViewModel for DateCalculator.xaml Date calculation logic
UnitConverterViewModel ViewModel for UnitConverter.xaml All converter modes including currency
Core Responsibilities
ViewModels in Calculator:
Expose Data for Binding
Provide properties that XAML elements can bind to
Implement INotifyPropertyChanged
Notify the UI when data changes
Transform Model Data
Convert Model data into display-friendly formats
Handle Commands
Process user actions from the View
Manage UI State
Track mode, visibility, enabled state, etc.
PropertyChanged Events
The INotifyPropertyChanged interface is the foundation of data binding in MVVM. It allows ViewModels to notify the UI when properties change.
INotifyPropertyChanged Interface
ViewModels implement this interface to support data binding:
StandardCalculatorViewModel.h Declaration
[ Windows :: UI :: Xaml :: Data :: Bindable ]
public ref class StandardCalculatorViewModel sealed
: public Windows::UI::Xaml::Data::INotifyPropertyChanged
{
public:
StandardCalculatorViewModel ();
OBSERVABLE_OBJECT_CALLBACK (OnPropertyChanged);
// ... properties and methods
};
OBSERVABLE_OBJECT Macro
The OBSERVABLE_OBJECT() macro (defined in Utils.h) implements the required PropertyChanged event:
Utils.h - OBSERVABLE_OBJECT Macro (src/CalcViewModel/Common/Utils.h:123-131)
#define OBSERVABLE_OBJECT () \
virtual event Windows :: UI :: Xaml :: Data ::PropertyChangedEventHandler ^ PropertyChanged; \
internal: \
void RaisePropertyChanged ( Platform :: String ^ p ) \
{ \
PropertyChanged ( this , ref new Windows :: UI :: Xaml :: Data :: PropertyChangedEventArgs (p)); \
} \
\
public:
This macro:
Declares the PropertyChanged event
Provides a RaisePropertyChanged helper function
The RaisePropertyChanged function is called whenever a property value changes, triggering UI updates.
Observable Properties
Calculator uses macros to simplify property declarations that automatically trigger PropertyChanged events.
OBSERVABLE_PROPERTY_RW
Defines a Read/Write property with automatic change notification:
Property Declaration
Macro Expansion (src/CalcViewModel/Common/Utils.h:77-97)
OBSERVABLE_PROPERTY_RW ( Platform ::String ^ , CategoryName);
The setter checks if the new value differs from the current value before raising PropertyChanged, avoiding unnecessary events.
OBSERVABLE_PROPERTY_R
Defines a Read-Only property (setter is private):
OBSERVABLE_PROPERTY_R (HistoryViewModel ^ , HistoryVM);
Use this for properties that should only be modified internally by the ViewModel.
Property Examples from StandardCalculatorViewModel
StandardCalculatorViewModel.h Properties (src/CalcViewModel/StandardCalculatorViewModel.h:40-86)
OBSERVABLE_OBJECT_CALLBACK (OnPropertyChanged);
OBSERVABLE_PROPERTY_RW ( Platform ::String ^ , DisplayValue);
OBSERVABLE_PROPERTY_R (HistoryViewModel ^ , HistoryVM);
OBSERVABLE_PROPERTY_RW ( bool , IsAlwaysOnTop);
OBSERVABLE_PROPERTY_R ( bool , IsBinaryBitFlippingEnabled);
OBSERVABLE_NAMED_PROPERTY_R ( bool , IsInError);
OBSERVABLE_PROPERTY_R ( bool , IsOperatorCommand);
OBSERVABLE_PROPERTY_R (
Windows :: Foundation :: Collections ::IObservableVector < Common ::DisplayExpressionToken ^> ^ ,
ExpressionTokens
);
OBSERVABLE_PROPERTY_R ( Platform ::String ^ , DecimalDisplayValue);
OBSERVABLE_PROPERTY_R ( Platform ::String ^ , HexDisplayValue);
OBSERVABLE_PROPERTY_R ( Platform ::String ^ , OctalDisplayValue);
OBSERVABLE_NAMED_PROPERTY_R ( Platform ::String ^ , BinaryDisplayValue);
OBSERVABLE_PROPERTY_R ( bool , IsBinaryOperatorEnabled);
OBSERVABLE_PROPERTY_R ( bool , IsMemoryEmpty);
Data Binding Flow
Here’s how property changes flow to the UI:
Example: Display Value Update
ViewModel Property
XAML Binding
Update Flow
StandardCalculatorViewModel.h
OBSERVABLE_PROPERTY_RW ( Platform ::String ^ , DisplayValue);
This expands to a property with automatic change notification. < TextBlock Text = "{x:Bind Model.DisplayValue, Mode=OneWay}" />
The UI binds to the DisplayValue property.
Model performs calculation
ViewModel receives result
ViewModel sets DisplayValue = "42"
Property setter compares values
If different, updates m_DisplayValue
Calls RaisePropertyChanged(L"DisplayValue")
UI receives PropertyChanged event
Binding re-evaluates and updates TextBlock
Commands
ViewModels expose commands for user actions using the COMMAND_FOR_METHOD macro.
Command Declaration
StandardCalculatorViewModel.h Commands (src/CalcViewModel/StandardCalculatorViewModel.h:88-94)
COMMAND_FOR_METHOD (CopyCommand, StandardCalculatorViewModel ::OnCopyCommand);
COMMAND_FOR_METHOD (PasteCommand, StandardCalculatorViewModel ::OnPasteCommand);
COMMAND_FOR_METHOD (ButtonPressed, StandardCalculatorViewModel ::OnButtonPressed);
COMMAND_FOR_METHOD (ClearMemoryCommand, StandardCalculatorViewModel ::OnClearMemoryCommand);
COMMAND_FOR_METHOD (MemoryItemPressed, StandardCalculatorViewModel ::OnMemoryItemPressed);
COMMAND_FOR_METHOD (MemoryAdd, StandardCalculatorViewModel ::OnMemoryAdd);
COMMAND_FOR_METHOD (MemorySubtract, StandardCalculatorViewModel ::OnMemorySubtract);
COMMAND_FOR_METHOD Macro
Utils.h - Command Macro (src/CalcViewModel/Common/Utils.h:170-188)
#define COMMAND_FOR_METHOD ( p , m ) \
property Windows :: UI :: Xaml :: Input ::ICommand ^ p \
{ \
Windows :: UI :: Xaml :: Input ::ICommand ^ get () \
{ \
if ( ! donotuse_ ##p ) \
{ \
donotuse_ ##p = ref new CalculatorApp :: ViewModel :: Common :: DelegateCommand ( \
CalculatorApp :: ViewModel :: Common :: MakeDelegateCommandHandler ( this , & m) \
); \
} \
return donotuse_ ##p ; \
} \
} \
\
private: \
Windows :: UI :: Xaml :: Input ::ICommand ^ donotuse_ ##p ; \
\
public:
Command Binding in XAML
MainPage.xaml Command Binding
< Button x:Name = "CopyButton"
Command = "{x:Bind Model.CopyCommand}" />
When the button is clicked, the command executes OnCopyCommand in the ViewModel.
ViewModel-Model Interaction
ViewModels call into the Model layer through CalculatorManager:
// ViewModel holds a reference to CalculatorManager
private:
CalculationManager ::CalculatorManager * m_calculatorManager;
// ViewModel calls Model for calculations
void StandardCalculatorViewModel :: OnButtonPressed ( Object ^ parameter )
{
// Process button press
auto command = GetCommandFromParameter (parameter);
// Delegate to Model
m_calculatorManager -> SendCommand (command);
// Model will call back via ICalcDisplay interface
// which updates ViewModel properties
}
The Model communicates back to the ViewModel through the ICalcDisplay interface, which the ViewModel implements.
Complex Property Example
Some properties have custom logic beyond simple get/set:
StandardCalculatorViewModel.h Custom Property (src/CalcViewModel/StandardCalculatorViewModel.h:98-114)
property bool IsBitFlipChecked
{
bool get ()
{
return m_isBitFlipChecked;
}
void set ( bool value)
{
if (m_isBitFlipChecked != value)
{
m_isBitFlipChecked = value;
// Update related properties
IsBinaryBitFlippingEnabled = IsProgrammer && m_isBitFlipChecked;
AreProgrammerRadixOperatorsVisible = IsProgrammer && ! m_isBitFlipChecked;
RaisePropertyChanged ( L"IsBitFlipChecked" );
}
}
}
This property updates multiple dependent properties when changed.
Best Practices
Use OBSERVABLE_PROPERTY_RW or OBSERVABLE_PROPERTY_R for properties that trigger UI updates. This ensures consistent behavior and reduces boilerplate.
Always check if the new value differs before raising PropertyChanged to avoid unnecessary UI updates. The macros handle this automatically.
ViewModels should never reference XAML controls or UI-specific types. Use data binding and commands instead.
Convert Model data into display-friendly formats in the ViewModel. For example, format numbers with appropriate precision.
Prefer OBSERVABLE_PROPERTY_R for properties that shouldn’t be modified by the View.
Property Change Callbacks
Use OBSERVABLE_OBJECT_CALLBACK to react to any property change:
OBSERVABLE_OBJECT_CALLBACK (OnPropertyChanged);
private:
void OnPropertyChanged ( Platform :: String ^ propertyName )
{
// React to specific property changes
if (propertyName == L"DisplayValue" )
{
// Update accessibility announcements, etc.
}
}
Testing ViewModels
ViewModels are highly testable because they don’t depend on UI: // Create ViewModel
auto vm = ref new StandardCalculatorViewModel ();
// Set property
vm -> DisplayValue = "123" ;
// Verify
Assert :: AreEqual ( "123" , vm -> DisplayValue );
Next Steps
Model Layer Understand how ViewModels interact with CalcManager
View Layer See how XAML binds to ViewModel properties
MVVM Pattern Review the overall architecture pattern