Skip to content

Progress

Progress derives from native System.Windows.Controls.ProgressBar (NaviusProgress), reusing its PART_Track/PART_Indicator template parts and built-in IRangeValueProvider automation. It is a non-interactive, read-only display: an out-of-range Value flips the bar to indeterminate rather than clamping, and NaviusProgressValue/NaviusProgressLabel are companion text parts wired by reference rather than nested content.

xmlns:navius="clr-namespace:Navius.Wpf.Primitives.Controls;assembly=Navius.Wpf.Primitives"
<StackPanel>
<navius:NaviusProgressLabel x:Name="UploadLabel" Text="Upload progress" />
<navius:NaviusProgress x:Name="Upload"
Value="42"
Maximum="100"
AutomationProperties.LabeledBy="{Binding ElementName=UploadLabel}" />
<navius:NaviusProgressValue Source="{Binding ElementName=Upload}" />
</StackPanel>
Property Type Default From Description
Value double 0.0 NaviusProgress (overrides RangeBase) Current value. Negative or > Maximum flips IsIndeterminate to true via a CoerceValueCallback override instead of clamping into range (“validate, don’t clamp”). NaN/Infinity are rejected outright by RangeBase’s own inherited validation before this coercion runs.
Maximum double 100.0 NaviusProgress (overrides RangeBase) A value <= 0 falls back to 100.
IsIndeterminate bool false ProgressBar Native property; set directly or coerced to true by an out-of-range Value.
GetValueLabel Func<double, double, string?>? null NaviusProgress Builds the value text (value, maximum) for a determinate value. Defaults to the rounded percentage. Never invoked while indeterminate.
IsComplete bool false NaviusProgress Read-only. True when determinate and Value >= Maximum.
IsProgressing bool computed NaviusProgress Read-only, not a DP. True when determinate and Value < Maximum (!IsIndeterminate && !IsComplete).
Source NaviusProgress? null NaviusProgressValue The progress instance whose formatted value text this element renders.
TextOverride string? null NaviusProgressValue When set, overrides the default rounded-percentage text.
Method Description
FormatValueText() Returns the formatted value text: null while indeterminate, otherwise GetValueLabel or the default rounded percentage.
Event Signature Fires when
StateChanged RoutedEventHandler Value, Maximum, or IsIndeterminate changes; consumed by NaviusProgressValue to refresh its text.

NaviusProgressAutomationPeer (derives ProgressBarAutomationPeer) overrides GetItemStatusCore() to return FormatValueText(), since the base peer has no first-class value-text slot. AutomationControlType stays the inherited ProgressBar value, and the native IRangeValueProvider (min/max/current) continues to work unmodified. The unit suite covers the coercion rule (Value_AboveMaximum_BecomesIndeterminateWithoutClamping, Value_Negative_BecomesIndeterminate, Value_ReenteringValidRange_ClearsIndeterminate), the NaN/Infinity rejection (Value_NaN_IsRejectedByNativeValidation), the Maximum <= 0 fallback, and the automation peer’s ItemStatus text.

  • “Validate, don’t clamp” is preserved via a CoerceValueCallback override on Value rather than a clamp, deliberately replacing RangeBase’s default clamp-to-range coercion.
  • NaN/Infinity are not reachable at all: RangeBase.ValueProperty’s own inherited ValidateValueCallback rejects them with an ArgumentException before NaviusProgress’s coercion ever runs; only negative/> Maximum values are portable to the “becomes indeterminate” path.
  • GetValueLabel is Func<double, double, string?>? (a non-nullable value parameter) rather than the web contract’s Func<double?, double, string?>?, since there is no nullable-value state to pass through at this layer.
  • aria-labelledby/HasLabel push-based registration is replaced by the native WPF idiom: a consumer sets AutomationProperties.LabeledBy on the NaviusProgress, pointing at a NaviusProgressLabel, rather than a label registering itself with a cascaded context.
  • NaviusProgressValue is a companion element wired via an explicit Source property (not nested content or a visual-tree ancestor lookup), since ProgressBar has no content model to nest a value part inside.
  • Known residual (M6 audit): while IsIndeterminate is true, the inherited ProgressBarAutomationPeer still surfaces a RangeValue.Value to UIA, whereas the web contract omits aria-valuenow entirely in the indeterminate state. This is not claimed as fixed anywhere in the implementation notes, so it is a genuine unfixed parity gap in the UIA surface, not a false claim.

The Progress Gallery page rendered at the pinned commit, in each theme.

Progress Gallery page in the light theme

Progress Gallery page in the dark theme

Progress Gallery page in the high contrast theme