Skip to main content

Overview

The UnitConverterViewModel class manages unit conversions for various categories (Currency, Volume, Length, Weight, Temperature, Energy, etc.). It handles real-time currency data, supplementary results, and bidirectional conversion between units. Namespace: CalculatorApp.ViewModel Implements: Windows::UI::Xaml::Data::INotifyPropertyChanged Declared in: UnitConverterViewModel.h:156

Observable Properties

Category and Units

Categories

OBSERVABLE_PROPERTY_R(
    Windows::Foundation::Collections::IObservableVector<Category^>^,
    Categories
);
Collection of available conversion categories (Currency, Volume, Length, Weight, etc.).

CurrentCategory

property Category^ CurrentCategory
{
    Category^ get();
    void set(Category^ value);
}
The currently selected conversion category. When changed, updates the Units collection and resets the converter.

Units

OBSERVABLE_PROPERTY_R(
    Windows::Foundation::Collections::IObservableVector<Unit^>^,
    Units
);
Collection of available units for the current category.

Mode

OBSERVABLE_PROPERTY_RW(CalculatorApp::ViewModel::Common::ViewMode, Mode);
The current ViewMode (Currency, Volume, Length, etc.).

Input/Output Values

Unit1

OBSERVABLE_PROPERTY_RW(Unit^, Unit1);
The first (source) unit for conversion.

Value1

OBSERVABLE_PROPERTY_RW(Platform::String^, Value1);
The value in the first unit (user input).

Value1Active

OBSERVABLE_PROPERTY_RW(bool, Value1Active);
Indicates whether the first value field is currently active for input.

Unit2

OBSERVABLE_PROPERTY_RW(Unit^, Unit2);
The second (target) unit for conversion.

Value2

OBSERVABLE_PROPERTY_RW(Platform::String^, Value2);
The value in the second unit (conversion result or user input when active).

Value2Active

OBSERVABLE_PROPERTY_RW(bool, Value2Active);
Indicates whether the second value field is currently active for input.

Currency-Specific Properties

CurrencySymbol1

OBSERVABLE_PROPERTY_RW(Platform::String^, CurrencySymbol1);
Currency symbol for the first unit (e.g., ”$”, ”€”).

CurrencySymbol2

OBSERVABLE_PROPERTY_RW(Platform::String^, CurrencySymbol2);
Currency symbol for the second unit.

CurrencySymbolVisibility

property Windows::UI::Xaml::Visibility CurrencySymbolVisibility
{
    Windows::UI::Xaml::Visibility get();
}
Controls visibility of currency symbols. Returns Collapsed if either symbol is empty.

IsCurrencyCurrentCategory

OBSERVABLE_NAMED_PROPERTY_R(bool, IsCurrencyCurrentCategory);
Indicates whether the current category is Currency.

IsCurrencyLoadingVisible

OBSERVABLE_NAMED_PROPERTY_RW(bool, IsCurrencyLoadingVisible);
Controls visibility of the currency data loading indicator.

CurrencyDataLoadFailed

OBSERVABLE_NAMED_PROPERTY_RW(bool, CurrencyDataLoadFailed);
Indicates whether currency data failed to load from the network.

CurrencyDataIsWeekOld

OBSERVABLE_NAMED_PROPERTY_RW(bool, CurrencyDataIsWeekOld);
Indicates whether the currency data is more than one week old.

CurrencyRatioEquality

OBSERVABLE_PROPERTY_RW(Platform::String^, CurrencyRatioEquality);
Displays the exchange rate ratio (e.g., “1 USD = 0.85 EUR”).

CurrencyRatioEqualityAutomationName

OBSERVABLE_PROPERTY_RW(Platform::String^, CurrencyRatioEqualityAutomationName);
Accessibility-friendly version of the currency ratio.

CurrencyTimestamp

OBSERVABLE_PROPERTY_RW(Platform::String^, CurrencyTimestamp);
Displays when currency rates were last updated.

NetworkBehavior

OBSERVABLE_NAMED_PROPERTY_RW(
    CalculatorApp::ViewModel::Common::NetworkAccessBehavior,
    NetworkBehavior
);
Controls network access behavior for currency updates:
  • Normal - Fetch from network
  • Offline - Use cached data only
  • OptIn - Ask user permission

Supplementary Results

SupplementaryResults

OBSERVABLE_NAMED_PROPERTY_R(
    Windows::Foundation::Collections::IObservableVector<SupplementaryResult^>^,
    SupplementaryResults
);
Collection of suggested conversions to other related units. Example: When converting meters to feet, might show kilometers, centimeters, inches.

SupplementaryVisibility

property Windows::UI::Xaml::Visibility SupplementaryVisibility
{
    Windows::UI::Xaml::Visibility get();
}
Returns Visible if supplementary results are available, otherwise Collapsed.

UI State Properties

IsDecimalEnabled

OBSERVABLE_PROPERTY_RW(bool, IsDecimalEnabled);
Enables/disables the decimal point button.

IsDropDownOpen

OBSERVABLE_PROPERTY_RW(bool, IsDropDownOpen);
Indicates whether the unit selection dropdown is open.

IsDropDownEnabled

OBSERVABLE_PROPERTY_RW(bool, IsDropDownEnabled);
Enables/disables the unit selection dropdown.

Accessibility

Value1AutomationName

OBSERVABLE_PROPERTY_RW(Platform::String^, Value1AutomationName);
Accessibility text for the first value field.

Value2AutomationName

OBSERVABLE_PROPERTY_RW(Platform::String^, Value2AutomationName);
Accessibility text for the second value field.

Unit1AutomationName

OBSERVABLE_PROPERTY_RW(Platform::String^, Unit1AutomationName);
Accessibility text for the first unit.

Unit2AutomationName

OBSERVABLE_PROPERTY_RW(Platform::String^, Unit2AutomationName);
Accessibility text for the second unit.

Announcement

OBSERVABLE_PROPERTY_RW(
    CalculatorApp::ViewModel::Common::Automation::NarratorAnnouncement^,
    Announcement
);
Narrator announcements for conversion results.

Commands

CategoryChanged

COMMAND_FOR_METHOD(CategoryChanged, UnitConverterViewModel::OnCategoryChanged);
Triggered when the user selects a different conversion category.

UnitChanged

COMMAND_FOR_METHOD(UnitChanged, UnitConverterViewModel::OnUnitChanged);
Triggered when the user selects a different unit.

SwitchActive

COMMAND_FOR_METHOD(SwitchActive, UnitConverterViewModel::OnSwitchActive);
Swaps the input and output fields.

ButtonPressed

COMMAND_FOR_METHOD(ButtonPressed, UnitConverterViewModel::OnButtonPressed);
Handles number pad button presses.

CopyCommand

COMMAND_FOR_METHOD(CopyCommand, UnitConverterViewModel::OnCopyCommand);
Copies the conversion result to the clipboard.

PasteCommand

COMMAND_FOR_METHOD(PasteCommand, UnitConverterViewModel::OnPasteCommand);
Pastes a value from the clipboard.

Key Methods

Constructors

UnitConverterViewModel();
Default constructor.
internal: UnitConverterViewModel(
    const std::shared_ptr<UnitConversionManager::IUnitConverter>& model
);
Constructor with custom conversion model (internal use).

Public Methods

OnPaste

void OnPaste(Platform::String^ stringToPaste);
Processes pasted text as numeric input.

RefreshCurrencyRatios

void RefreshCurrencyRatios();
Manually triggers a refresh of currency exchange rates from the network.

OnValueActivated

void OnValueActivated(IActivatable^ control);
Activates the specified value field for input.

AnnounceConversionResult

void AnnounceConversionResult();
Announces the conversion result to screen readers.

SaveUserPreferences

void SaveUserPreferences();
Saves the current category and selected units to local settings for restoration on next launch.

RestoreUserPreferences

void RestoreUserPreferences();
Restores the previously saved category and units from local settings.

Internal Methods

UpdateDisplay

void UpdateDisplay(const std::wstring& from, const std::wstring& to);
Updates the display with converted values (called by the conversion engine).

UpdateSupplementaryResults

void UpdateSupplementaryResults(
    const std::vector<std::tuple<std::wstring, UnitConversionManager::Unit>>& suggestedValues
);
Updates the supplementary results collection.

OnMaxDigitsReached

void OnMaxDigitsReached();
Handles the maximum digit limit event.

OnCurrencyDataLoadFinished

void OnCurrencyDataLoadFinished(bool didLoad);
Callback when currency data loading completes.

OnCurrencyTimestampUpdated

void OnCurrencyTimestampUpdated(_In_ const std::wstring& timestamp, bool isWeekOld);
Callback when currency timestamp is updated.

Helper Classes

Category

public ref class Category sealed : public Windows::UI::Xaml::Data::INotifyPropertyChanged
{
public:
    property Platform::String^ Name { Platform::String^ get(); }
    property Windows::UI::Xaml::Visibility NegateVisibility {
        Windows::UI::Xaml::Visibility get();
    }
    int GetModelCategoryId();
};
Represents a conversion category.

Unit

public ref class Unit sealed : public Windows::UI::Xaml::Data::INotifyPropertyChanged
{
public:
    property Platform::String^ Name { Platform::String^ get(); }
    property Platform::String^ AccessibleName { Platform::String^ get(); }
    property Platform::String^ Abbreviation { Platform::String^ get(); }
    bool IsModelUnitWhimsical();
    int ModelUnitID();
};
Represents a conversion unit.

SupplementaryResult

public ref class SupplementaryResult sealed : public Windows::UI::Xaml::Data::INotifyPropertyChanged
{
public:
    OBSERVABLE_PROPERTY_R(Platform::String^, Value);
    OBSERVABLE_PROPERTY_R(CalculatorApp::ViewModel::Unit^, Unit);
    bool IsWhimsical();
    Platform::String^ GetLocalizedAutomationName();
};
Represents a supplementary conversion result.

INotifyPropertyChanged Implementation

The ViewModel uses the OBSERVABLE_OBJECT_CALLBACK macro:
OBSERVABLE_OBJECT_CALLBACK(OnPropertyChanged);
This generates automatic property change notifications and calls OnPropertyChanged for custom handling.

Usage Examples

Basic Unit Conversion

// Create ViewModel
auto converter = ref new UnitConverterViewModel();

// Set category to Length
converter->CurrentCategory = lengthCategory;

// Set units
converter->Unit1 = metersUnit;
converter->Unit2 = feetUnit;

// Set value to convert
converter->Value1 = "10";

// Get converted value
Platform::String^ result = converter->Value2;
// e.g., "32.8084"

// Get supplementary results
auto supplementary = converter->SupplementaryResults;
// e.g., [{"10000", "Millimeters"}, {"0.01", "Kilometers"}]

Currency Conversion

// Create ViewModel
auto converter = ref new UnitConverterViewModel();

// Set to Currency mode
converter->Mode = ViewMode::Currency;
converter->CurrentCategory = currencyCategory;

// Select currencies
converter->Unit1 = usdUnit;
converter->Unit2 = eurUnit;

// Convert 100 USD
converter->Value1 = "100";

// Get result
Platform::String^ euros = converter->Value2;
Platform::String^ symbols = converter->CurrencySymbol1 + " to " + converter->CurrencySymbol2;
Platform::String^ ratio = converter->CurrencyRatioEquality;
// e.g., "1 USD = 0.85 EUR"

// Refresh currency rates
converter->RefreshCurrencyRatios();

// Check if data is old
if (converter->CurrencyDataIsWeekOld)
{
    // Show warning
}

Switching Active Fields

// User types in Value1 by default
converter->Value1Active = true;
converter->Value1 = "50";

// Switch to allow input in Value2
converter->SwitchActive->Execute(nullptr);

// Now Value2 is active, Value1 updates automatically
converter->Value2 = "100";
Platform::String^ reversedValue = converter->Value1;

XAML Binding Example

<ComboBox ItemsSource="{x:Bind ViewModel.Categories}"
          SelectedItem="{x:Bind ViewModel.CurrentCategory, Mode=TwoWay}" />

<ComboBox ItemsSource="{x:Bind ViewModel.Units}"
          SelectedItem="{x:Bind ViewModel.Unit1, Mode=TwoWay}" />

<TextBox Text="{x:Bind ViewModel.Value1, Mode=TwoWay}"
         AutomationProperties.Name="{x:Bind ViewModel.Value1AutomationName}" />

<TextBlock Text="{x:Bind ViewModel.CurrencySymbol1}" />

<ListView ItemsSource="{x:Bind ViewModel.SupplementaryResults}"
          Visibility="{x:Bind ViewModel.SupplementaryVisibility}" />

<Button Command="{x:Bind ViewModel.SwitchActive}" Content="⇄" />
<Button Command="{x:Bind ViewModel.CopyCommand}" Content="Copy" />

Persistence

The ViewModel automatically saves and restores user preferences:
// Save on category/unit change
converter->SaveUserPreferences();

// Restore on app launch
converter->RestoreUserPreferences();
Stored preferences include:
  • Last selected category
  • Last selected units for each category

Currency Data Management

Currency rates are fetched from the network and cached:
  1. Initial load shows loading indicator (IsCurrencyLoadingVisible)
  2. Data is cached locally with timestamp
  3. If data is over 1 week old, CurrencyDataIsWeekOld is set
  4. If network fails, CurrencyDataLoadFailed is set
  5. User can manually refresh with RefreshCurrencyRatios()

Accessibility Features

  • Separate automation names for all values and units
  • Live announcements for conversion results
  • Support for whimsical units (special units with unique formatting)
  • Localized number formatting respects regional settings

See Also

Build docs developers (and LLMs) love