Skip to main content
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.xamlManages mode switching and coordinates child ViewModels

StandardCalculatorViewModel

ViewModel for Calculator.xamlHandles Standard, Scientific, and Programmer modes

DateCalculatorViewModel

ViewModel for DateCalculator.xamlDate calculation logic

UnitConverterViewModel

ViewModel for UnitConverter.xamlAll converter modes including currency

Core Responsibilities

ViewModels in Calculator:
1

Expose Data for Binding

Provide properties that XAML elements can bind to
2

Implement INotifyPropertyChanged

Notify the UI when data changes
3

Transform Model Data

Convert Model data into display-friendly formats
4

Handle Commands

Process user actions from the View
5

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:
#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:
  1. Declares the PropertyChanged event
  2. 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:
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):
Read-Only Property
OBSERVABLE_PROPERTY_R(HistoryViewModel^, HistoryVM);
Use this for properties that should only be modified internally by the ViewModel.

Property Examples from StandardCalculatorViewModel

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

StandardCalculatorViewModel.h
OBSERVABLE_PROPERTY_RW(Platform::String ^, DisplayValue);
This expands to a property with automatic change notification.

Commands

ViewModels expose commands for user actions using the COMMAND_FOR_METHOD macro.

Command Declaration

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

#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 → Model Flow
// 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:
Callback Example
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:
Example Unit Test
// 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

Build docs developers (and LLMs) love