Sitelet https://github.com/unoplatform/uno/pull/24838
Skip to content

feat: Add WinUI-shaped input validation - #24838

Open
Xiaoy312 wants to merge 48 commits into
masterfrom
dev/xygu/20260922/input-validation
Open

Xiaoy312 wants to merge 48 commits into
masterfrom
dev/xygu/20260922/input-validation

Conversation

@Xiaoy312

@Xiaoy312 Xiaoy312 commented Sep 30, 2026 •

Copy link
Copy Markdown
Contributor

GitHub Issue: closes #4839, related: unoplatform/uno-private#2384

PR Type:

✨ Feature

What changed? 🚀

Before: Neither WinUI nor Uno supports input validation. INotifyDataErrorInfo works in the BCL, but nothing carries its errors from a binding source to the bound control. Apps have to build the whole presentation layer themselves.

After: Controls can opt in to WinUI-shaped input validation. Errors from an INotifyDataErrorInfo source reach the bound control's HasValidationErrors / ValidationErrors and drive WinUI's validation visual states and error presenter. Design and rationale are in specs/059-input-validation/spec.md (transport + read model) and specs/060-input-validation-presentation/spec.md (visuals). §10/§10b of 059 record where the implementation departed from the original draft.

Opt-in (off by default)

// Before the first binding is registered, i.e. before the first page's InitializeComponent
Uno.UI.FeatureConfiguration.InputValidation.IsEnabled = true;
// The error presentation can also be packaged into the control styles, but that is not the scope of this PR.
<TextBox x:Name="UserNameBox" InputValidationMode="Auto" Text="{Binding UserName, Mode=TwoWay}" />
<TextBlock Text="{Binding ValidationErrors, ElementName=UserNameBox, Converter=...}"
           Visibility="{Binding HasValidationErrors, ElementName=UserNameBox, Converter=...}" />

Validation is off unless both switches are set. With the global flag off, the binding registration path does no extra work. InputValidationMode defaults to Disabled rather than WinUI's Auto, because here it also gates the INotifyDataErrorInfo subscription.

Transport

  • Registration happens where both SetBindingInternal overloads converge. Resolution runs on BindingExpression.OnValueChanged, which both {Binding} and {x:Bind} go through. The leaf source is read from the compiled binding's update sources as well as the binding path, so the two binding kinds behave the same (avoiding microsoft-ui-xaml#4642).
  • ErrorsChanged is subscribed weakly, so a view model cannot keep a control alive. A raise from another thread is dispatched to the UI thread. Teardown hooks ClearBinding, which covers rebinding and ClearValue.
  • The transport lives on Control (Control.Validation*.cs), where WinUI keeps the equivalent (CControl::IsValidationEnabled / EnsureValidationVisuals).

Public surface

  • Microsoft.UI.Xaml.Controls: IInputValidationControl (WinUI's full interface except ValidationContext), InputValidationMode, InputValidationKind, InputValidationError, InputValidationErrorEventArgs, InputValidationErrorEventAction, InputValidationContext. HasValidationErrorsChangedEventArgs replaces its [Uno.NotImplemented] generated stub with a real implementation.
  • Uno.UI.Xaml.Controls: InputValidationPropertyAttribute and InputValidationPropertyMap, which declare which property of a control carries its input.
  • FeatureConfiguration.InputValidation (IsEnabled, ValidationProperties).
  • Control gains protected helpers (UpdateValidationStates, the changed callbacks, event add/remove helpers, GetOrCreateValidationErrors) so that a control outside Uno.UI can take part. Uno.UI.Tests.ViewLibrary (not covered by InternalsVisibleTo) contains such a control, so the build fails if that contract regresses.
  • ⚠️ Parity risk: these type names follow WinUI's PrivateApiContract / Feature_InputValidation IDL, which has never shipped publicly. If microsoft-ui-xaml#179 ships with a different shape, the names will collide. Details in spec 059 §10b.

Participants

The participants are WinUI's four: TextBox→Text, PasswordBox→Password, AutoSuggestBox→Text, ComboBox→SelectedItem. ComboBox departs from WinUI, which validates Text: a non-editable ComboBox is bound on SelectedItem, so validating Text would leave the common case with no errors. Each registers its own InputValidationMode, InputValidationKind, HasValidationErrors, ValidationErrors and ErrorTemplate dependency properties, so a Style Setter can target them and a Binding can drive them.

Presentation (spec 060, partial)

  • WinUI's InputValidationEnabledStates / InputValidationErrorStates are driven from each participant's visual-state method.
  • Port of CControl::EnsureErrors / DeferErrors: the first error realizes the template's ErrorPresenter, which shows ErrorTemplate inline, or in the new DefaultCompactErrorIconTemplate tooltip for compact mode.
  • Not in this PR: the per-control default template changes (060 §4) and CommonStates contention (060 §3 / Q10). The shipped Fluent templates don't have validation parts yet, so nothing appears on screen unless the app's template or markup renders it.

Related fix

fix: Resolve attached properties of uninitialized owners (3241cea): DependencyPropertyDescriptor.Parse redirected a parenthesized path to the owner type without running that type's static constructor. The lookup missed and the miss was cached, so {Binding (Owner.Property)} read the initial value once and never updated. This affected Canvas.Left too.

Tests & sample

  • Unit tests (Uno.UI.UnitTests/InputValidation/): transport, per-control properties, public surface guard, a third-party control, and attached-path resolution.
  • Runtime tests (Uno_UI_Xaml_Controls/InputValidation/): validation visual states and the error presenter.
  • Sample: Uno/UI/Xaml/Controls/Validation/InputValidation. The SamplesApp now turns the global flag on.

PR Checklist ✅

  • 🧪 Added Runtime tests, UI tests, or a manual test sample (for bug fixes / features, if applicable)
  • 📚 Docs have been added/updated following the documentation template (for bug fixes / features) — specs only so far; a doc/articles page will follow once the templates (060 §4) land
  • 🖼️ Validated PR Screenshots Compare Test Run results.
  • ❗ Contains NO breaking changes — the feat! commits only break API added earlier in this branch, which has never been released. Compared with master, the changes are additive and off by default.
  • 👀 Reviewed 2 other open pull requests (optional but appreciated!)

🤖 Generated with Claude Code

@github-actions github-actions Bot added area/automation Categorizes an issue or PR as relevant to project automation kind/documentation labels Sep 30, 2026
@github-actions

Copy link
Copy Markdown
Contributor

WinAppSDK sync generator drift detected on 92c127f

The Uno.WinAppSDKSyncGenerator produced changes that are not committed on this PR.

To apply automatically (PRs from branches in this repo only): comment /apply-sync-gen on this PR and a bot will push chore: Sync generator run to the head branch.

To apply manually (required for PRs from forks):

  1. Download the sync-generator-patch artifact from this run.
  2. In your local checkout of the PR branch:
    git apply sync-generator.patch
    git add -A
    git commit -m "chore: Sync generator run"
    git push
    

Or re-run the generator locally on Windows via build\run-api-sync-tool.cmd.

@unodevops

Copy link
Copy Markdown
Contributor

🤖 Your Docs stage site is ready! Visit it here: https://unodocsprstaging.z13.web.core.windows.net/pr-24838/docs/index.html

@unodevops

Copy link
Copy Markdown
Contributor

🤖 Your WebAssembly Skia Sample App stage site is ready! Visit it here: https://unowasmprstaging.z20.web.core.windows.net/pr-24838/wasm-skia-net9/index.html

Comment thread src/Uno.UI.UnitTests/InputValidation/Given_Validation_Transport.cs
Comment thread src/Uno.UI.UnitTests/InputValidation/Given_Validation_Transport.cs
Comment thread src/SamplesApp/SamplesApp.Shared/App.xaml.cs Outdated
Comment thread src/Uno.UI/UI/Xaml/Controls/Control/Control.Validation.cs

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

make sure DefaultCompactErrorIconTemplate is documented, not just in specs

Comment thread src/Uno.UI/UI/Xaml/Controls/Control/Control.Validation.Transport.cs Outdated
@nventive-devops

Copy link
Copy Markdown
Contributor

The build 236518 found UI Test snapshots differences: skia-linux-screenshots: 57, skia-windows-screenshots: 2304, wasm: 11

Details
  • skia-linux-screenshots: 57 changed over 2348

    🚨🚨 Comparison Details (first 20) 🚨🚨
    • ButtonClippingTestsControl.png-dark
    • ButtonClippingTestsControl.png
    • Buttons.png-dark
    • Buttons.png
    • CalendarView_Theming.png-dark
    • CalendarView_Theming.png
    • ClipboardTests.png-dark
    • ContextRequested.png-dark
    • ContextRequested.png
    • Gamepad_CurrentReading.png-dark
    • Gamepad_CurrentReading.png
    • Gamepad_Enumeration.png-dark
    • Gamepad_Enumeration.png
    • DisplayInformation.png-dark
    • DropDownButtonPage.png-dark
    • DropDownButtonPage.png
    • ImageIconPage.png-dark
    • ElementLevelTheme.png-dark
    • ElementLevelTheme.png
    • Image_AnimatedWebP.png-dark
  • skia-windows-screenshots: 2304 changed over 2504

    🚨🚨 Comparison Details (first 20) 🚨🚨
    • AnimatedIconPage.png
    • AndroidWindowInsets.png-dark
    • AppBar_KeyBoard.png-dark
    • AppBarToggleButtonTest.png-dark
    • AppWindowClosing.png-dark
    • ApplicationViewSizing.png
    • AppWindowPresenters.png-dark
    • AppWindowPositionAndSize.png
    • Arrange_Performance01.png-dark
    • ArcSegment.png-dark
    • Attributed_text_Supserscript.png
    • Attributed_text_Simple.png
    • AutoBorderStretchwithrightmargin.png
    • AutoBorderStretchwithleftmargin.png-dark
    • AutomationProperties_Name.png
    • AutomationPropertiesExtensions_Role.png-dark
    • AutoSuggestBox_Icons.png
    • AutoSizedTopLeft.png
    • AutoSuggestBox_Keyboard.png-dark
    • AutoSuggestBoxChosenSuggestion.png-dark
  • wasm: 11 changed over 1076

    🚨🚨 Comparison Details (first 20) 🚨🚨
    • SamplesApp.Windows_UI_Xaml_Controls.ListView.ListViewSelectedItems
    • UITests.Microsoft_UI_Xaml_Controls.WebView2Tests.WebView2_NavigationProperties
    • SamplesApp.Wasm.Windows_UI_Xaml_Controls.ListView.ListView_IsSelected
    • UITests.Shared.Microsoft_UI_Xaml_Controls.ExpanderTests.WinUIExpanderPage
    • SamplesApp.Microsoft_UI_Xaml_Controls.WebView2Tests.WebView2_EnableDevTools
    • UITests.Shared.Windows_UI_Xaml_Controls.MediaPlayerElement.MediaPlayerElement_Minimal
    • UITests.Shared.Windows_UI_Xaml_Controls.MediaPlayerElement.MediaPlayerElement_Ogg_Extension
    • UITests.Uno_Web.Http.CookieManagerTests
    • UITests.Windows_UI_Xaml_Controls.CalendarView.CalendarView_Theming
    • UITests.Windows_UI_Xaml_Media_Animation.ColorAnimation_Background
    • Uno.UI.Samples.Content.UITests.WebView.WebView_AnchorNavigation

@Xiaoy312
Xiaoy312 force-pushed the dev/xygu/20260922/input-validation branch from 92c127f to 80a2860 Compare October 1, 2026 14:41

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

move back properties into attached-DPs (housed under uno.extras, with winappsdk impl being inert)

@unodevops

Copy link
Copy Markdown
Contributor

🤖 Your WebAssembly Skia Sample App stage site is ready! Visit it here: https://unowasmprstaging.z20.web.core.windows.net/pr-24838/wasm-skia-net9/index.html

@unodevops

Copy link
Copy Markdown
Contributor

🤖 Your Docs stage site is ready! Visit it here: https://unodocsprstaging.z13.web.core.windows.net/pr-24838/docs/index.html

@nventive-devops

Copy link
Copy Markdown
Contributor

The build 236733 found UI Test snapshots differences: skia-linux-screenshots: 116, skia-windows-screenshots: 148

Details
  • skia-linux-screenshots: 116 changed over 2454

    🚨🚨 Comparison Details (first 20) 🚨🚨
    • CalendarView_Theming.png-dark
    • ClipboardTests.png-dark
    • CompositionEffectBrush.png-dark
    • CompositionEffectBrush.png
    • ButtonClippingTestsControl.png-dark
    • ButtonClippingTestsControl.png
    • Buttons.png-dark
    • Buttons.png
    • DoubleImageBrushInList.png-dark
    • DoubleImageBrushInList.png
    • Focus_FocusVisual_Properties.png-dark
    • Focus_FocusVisual_Properties.png
    • Examples.png
    • Gamepad_CurrentReading.png-dark
    • Gamepad_Enumeration.png-dark
    • DisplayInformation.png-dark
    • ImageIconPage.png-dark
    • ImageIconPage.png
    • ImageBrushInList.png-dark
    • ImageBrushInList.png
  • skia-windows-screenshots: 148 changed over 2456

    🚨🚨 Comparison Details (first 20) 🚨🚨
    • ButtonClippingTestsControl.png-dark
    • ButtonClippingTestsControl.png
    • ClipboardTests.png-dark
    • ClipboardTests.png
    • CalendarView_Theming.png-dark
    • Buttons.png-dark
    • Buttons.png
    • DoubleImageBrushInList.png-dark
    • DoubleImageBrushInList.png
    • Examples.png
    • DisplayInformation.png-dark
    • ImageSourceUrlMsAppDataScheme.png-dark
    • ImageSourceUrlMsAppDataScheme.png
    • ElementLevelTheme.png-dark
    • Gamepad_CurrentReading.png-dark
    • Gamepad_Enumeration.png-dark
    • Gamepad_Enumeration.png
    • ImageBrush_Stretch.png-dark
    • ImageBrush_Stretch.png
    • ImageIconPage.png-dark

@unodevops

Copy link
Copy Markdown
Contributor

🤖 Your Docs stage site is ready! Visit it here: https://unodocsprstaging.z13.web.core.windows.net/pr-24838/docs/index.html

@unodevops

Copy link
Copy Markdown
Contributor

🤖 Your WebAssembly Skia Sample App stage site is ready! Visit it here: https://unowasmprstaging.z20.web.core.windows.net/pr-24838/wasm-skia-net9/index.html

@nventive-devops

Copy link
Copy Markdown
Contributor

The build 236770 found UI Test snapshots differences: skia-linux-screenshots: 116, skia-windows-screenshots: 149

Details
  • skia-linux-screenshots: 116 changed over 2454

    🚨🚨 Comparison Details (first 20) 🚨🚨
    • ButtonClippingTestsControl.png-dark
    • ButtonClippingTestsControl.png
    • Buttons.png-dark
    • Buttons.png
    • ClipboardTests.png-dark
    • CalendarView_Theming.png-dark
    • CompositionEffectBrush.png-dark
    • CompositionEffectBrush.png
    • DisplayInformation.png-dark
    • Examples.png
    • Focus_FocusVisual_Properties.png-dark
    • Focus_FocusVisual_Properties.png
    • ImageBrushInList.png-dark
    • ImageBrushInList.png
    • DoubleImageBrushInList.png-dark
    • DoubleImageBrushInList.png
    • ImageIconPage.png-dark
    • ImageIconPage.png
    • Gamepad_CurrentReading.png-dark
    • Gamepad_Enumeration.png-dark
  • skia-windows-screenshots: 149 changed over 2456

    🚨🚨 Comparison Details (first 20) 🚨🚨
    • ButtonClippingTestsControl.png-dark
    • ButtonClippingTestsControl.png
    • ClipboardTests.png-dark
    • ClipboardTests.png
    • Buttons.png-dark
    • Buttons.png
    • Focus_FocusVisual_Properties.png-dark
    • Grid_left_column_Auto.png-dark
    • Grid_left_column_Auto.png
    • Grid_Margin_And_Padding.png-dark
    • Grid_Margin_And_Padding.png
    • Grid_middle_col_auto__bottom_row_auto.png-dark
    • Grid_middle_col_auto__bottom_row_auto.png
    • Grid_MinWidth_MaxWidth.png-dark
    • Grid_MinWidth_MaxWidth.png
    • Examples.png
    • ImageBrushInList.png-dark
    • ImageBrushInList.png
    • ImageSourceUrlMsAppDataScheme.png-dark
    • ImageSourceUrlMsAppDataScheme.png

@unodevops

Copy link
Copy Markdown
Contributor

🤖 Your WebAssembly Skia Sample App stage site is ready! Visit it here: https://unowasmprstaging.z20.web.core.windows.net/pr-24838/wasm-skia-net9/index.html

@nventive-devops

Copy link
Copy Markdown
Contributor

The build 237007 found UI Test snapshots differences: skia-linux-screenshots: 107, skia-windows-screenshots: 163

Details
  • skia-linux-screenshots: 107 changed over 2454

    🚨🚨 Comparison Details (first 20) 🚨🚨
    • CalendarView_Theming.png-dark
    • CalendarView_Theming.png
    • ColorPickerSample.png-dark
    • ColorPickerSample.png
    • CompositionEffectBrush.png-dark
    • CompositionEffectBrush.png
    • ClipboardTests.png-dark
    • ClipboardTests.png
    • ButtonClippingTestsControl.png-dark
    • ButtonClippingTestsControl.png
    • Buttons.png-dark
    • Buttons.png
    • DisplayInformation.png-dark
    • ElementLevelTheme.png-dark
    • ElementLevelTheme.png
    • ExpanderColorValidationPage.png-dark
    • ExpanderColorValidationPage.png
    • Focus_FocusVisual_Properties.png-dark
    • Focus_FocusVisual_Properties.png
    • Examples.png
  • skia-windows-screenshots: 163 changed over 2456

    🚨🚨 Comparison Details (first 20) 🚨🚨
    • ClipboardTests.png-dark
    • ClipboardTests.png
    • CalendarView_Theming.png-dark
    • ButtonClippingTestsControl.png-dark
    • ButtonClippingTestsControl.png
    • Buttons.png-dark
    • DisplayInformation.png-dark
    • Focus_FocusVisual_Properties.png-dark
    • Grid_Multi_Column_Span.png-dark
    • Grid_Multi_Column_Span.png
    • Grid_RowSpan_Auto_WithText.png-dark
    • Grid_RowSpan_Auto_WithText.png
    • Grid_RowSpan_Right.png-dark
    • Grid_RowSpan_Right.png
    • ElementLevelTheme.png-dark
    • ElementLevelTheme.png
    • ImageBrush_Stretch.png-dark
    • ImageBrush_Stretch.png
    • Grid_in_GridClipping.png-dark
    • Grid_in_GridClipping.png

@unodevops

Copy link
Copy Markdown
Contributor

🤖 Your Docs stage site is ready! Visit it here: https://unodocsprstaging.z13.web.core.windows.net/pr-24838/docs/index.html

@unodevops

Copy link
Copy Markdown
Contributor

⚠️⚠️ The build 237007 has failed on Uno.UI - CI.

Xiaoy312 and others added 8 commits October 5, 2026 15:04
Records the input-validation design as two staged specs. Docs only, no
production code changes.

Neither WinUI nor Uno has an input-validation story (microsoft-ui-xaml#179
open since 2019, its spec PR unmerged, #4839 closed as
blocked/missing-api). INotifyDataErrorInfo already works on Uno, the binding
engine already exposes a binding's leaf source, and {Binding} and {x:Bind}
already converge on one funnel -- what is missing is the wiring between them
plus a bindable read model.

specs/059-input-validation (Proposal) covers the transport layer and the read
model: participation decided at DependencyPropertyDetailsCollection.SetBinding
where both SetBindingInternal overloads converge (and where ResourceBinding is
structurally excluded), resolution through BindingExpression.OnValueChanged
against the binding's leaf, the validation property declared by a new Uno
InputValidationPropertyAttribute on the control type and cached under a new
FeatureConfiguration.InputValidation, and the
Validation.IsEnabled/HasErrors/Errors attached properties plus a public
IInputValidationControl.

WinUI's InputPropertyAttribute is deliberately not reused and not honoured as
a fallback: it means XAML child-element processing, not "the property the user
types into", and ComboBox already carries it for that meaning. The new
attribute is AllowMultiple = false for now -- an editable ComboBox validating
both Text and SelectedItem is the case that would trigger a revisit, and
widening it later stays source- and binary-compatible, provided resolution
stops relying on GetCustomAttribute (which throws AmbiguousMatchException once
multiple are allowed). Inherited = true, so a third-party MyTextBox : TextBox
participates unchanged, under the convention that the attribute never sits on
a shared base with non-validating subclasses.

A FrameworkPropertyMetadataOptions flag was considered and rejected: a
DependencyProperty is registered once per inheritance branch and Uno has no
OverrideMetadata, so a per-DP flag cannot separate Slider from ProgressBar nor
ComboBox from FlipView. A type-level attribute resolves per type, and works
because InternalGetProperty walks the base-type chain.

specs/060-input-validation-presentation (Deferred) covers the built-in visuals,
and records two blocking findings rather than leaving them to be rediscovered:
Control.ChangeVisualState is private protected and 19 of its 31 overrides skip
the base call (including every control that would participate), and
ValidationStates cannot be given priority over CommonStates because both write
through a single animated-value slot that records no writer.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
DependencyPropertyDescriptor.Parse redirects a parenthesized binding path to the
attached property's owner type, but only the originally queried type's static
constructor had been forced. When the owner had not been initialized yet its
property was not registered, the lookup missed, and the null was negatively
cached -- so a binding to an attached path read the initial value once and never
subscribed to changes. Affects any `{Binding (Owner.Property)}`, Canvas.Left
included.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Implements steps 1-3 of specs/059-input-validation:

- ValidationPropertyAttribute declares which dependency property of a control
  type is its input, resolved through the existing DependencyProperty.GetProperty
  and cached per type (negative answers included) in a ConditionalWeakTable, so
  the cache does not pin collectible AssemblyLoadContexts.
- FeatureConfiguration.Validation hosts the app-wide switch and that lookup.
- Validation exposes IsEnabled / HasErrors / Errors as attached properties, the
  read model an application binds its own error markup to.

Inert until the transport layer populates them.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Implements step 4 of specs/059-input-validation.

Participation is decided where both SetBindingInternal overloads converge, which
is also the only place the owner and the fresh BindingExpression are both in
hand; a ResourceBinding never reaches it. Resolution is separate and runs on
BindingExpression.OnValueChanged, the funnel every path re-resolution passes
through -- including the initial one, which is what survives a source whose
value already equals the target property default.

The subscription is keyed on the control rather than on the expression, since a
rebound property replaces its expression with no notification. ErrorsChanged is
subscribed weakly so a view model cannot root the control, and an off-thread
raise hops to the dispatcher.

Two hazards the spec did not cover are handled here: the attached property is
usually set after the binding in generated XAML, so registration cannot be gated
on it and the changed callback pulls the current expression instead; and binding
removal is silent, so teardown hooks ClearBinding, which covers both rebinding
and ClearValue.

Leaf resolution reads the update sources of a compiled binding, not just the
binding path -- the latter stays empty for x:Bind, which would otherwise have
reproduced microsoft-ui-xaml#4642 in reverse.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Implements step 5 of specs/059-input-validation. TextBox, PasswordBox, NumberBox,
AutoSuggestBox, ToggleSwitch, ToggleButton (and so CheckBox and RadioButton),
Slider and ComboBox declare their input and re-broadcast the errors of their
source. RichEditBox is left out: its content is an ITextDocument with no
dependency property to bind.

The attribute sits on the leaf type, never on RangeBase or Selector, which is
what keeps ProgressBar and FlipView out -- the case a per-dependency-property
flag could not have expressed.

ErrorChanged is backed by one shared attached property rather than per-control
storage, kept apart from the subscription state so handlers survive a rebind.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Shows the whole opt-in: a view model implementing INotifyDataErrorInfo, one
attached property on the control, and the error text rendered by the markup of
the application through its own converters -- this slice ships none.

Enables the feature in SamplesApp startup, which is also where a real
application would set it, and covers the compiled-binding path with a unit test.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The spec renames the FeatureConfiguration class and the attribute to carry an
InputValidation prefix, so that they no longer read as one type in two places
next to the Validation attached-property owner.

Also records in the spec what implementation corrected about it: the three
lifecycle hazards that do not hold, the compiled-binding leaf, the attached
property lookup bug that had to be fixed first, and the resolutions of Q1, Q3,
Q5, Q8, Q9 and Q11.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The weakly subscribed ErrorsChanged handler is the part of the design most
likely to regress silently, and a strong handler would let one long-lived view
model root every control bound to it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Xiaoy312 and others added 22 commits October 5, 2026 15:04
Uno.UI.Tests.ViewLibrary is deliberately absent from Uno.UI's
InternalsVisibleTo list and is already project-referenced by the unit
tests, so a participating control there makes the third-party contract a
compile-time one: if any member of the participation surface goes back to
internal, the build stops. That is a stronger and far cheaper signal than
the throwaway canary head this repo has tried before, which rotted as
soon as nobody ran it.

MyValidatingControl covers the attribute route; MyMappedValidatingControl
covers the escape hatch for a control that cannot carry one, registered
through FeatureConfiguration.InputValidation.ValidationProperties. Writing
it surfaced a sharp edge worth knowing: FrameworkPropertyMetadata's
(value, callback) overload is internal, so such a control registers with
PropertyMetadata instead.

Given_Validation_ThirdParty then covers what was silently broken before
the members became protected -- ValidationError per error added and
removed, ErrorChanged carrying the source's args, HasValidationErrors-
Changed once per transition rather than per synchronization, and mode
changes re-syncing a source that already had errors.

Kept the in-assembly doubles rather than moving them out, so the 36
transport tests are untouched; their comment claiming they stand in for a
third-party control was the false part, and now points here instead.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Asserts the ten members are protected rather than internal, so an
accidental narrowing fails with a named member instead of a compile error
from another project.

It proves the modifier, not the sufficiency of the surface: a member left
internal elsewhere would keep this green while a third-party control
still could not be written. The proof of sufficiency is that
Uno.UI.Tests.ViewLibrary compiles at all, so do not delete the control
there believing this covers it. The names are spelled out rather than
taken from nameof, since a protected member is not accessible to a type
that does not derive from Control -- which is the point of the guard.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
059 kept describing a Validation class that no longer exists. Section 4.1
gets a superseded banner rather than a rewrite -- 10b's decisions are
written against it, so deleting it would strand them -- and 10b gains D3
for the fold onto Control, including the third-party defect the old shape
hid and the internal FrameworkPropertyMetadata overload found while
proving it fixed. The owner-type, ordering and read-only notes named
members that were renamed or removed.

060 claimed three framework triggers where two of them were the same
callback, and still placed the shared statics on Validation and
ErrorTemplate on an attached property.

The historical sections of both specs keep their original wording: 10b is
the mechanism that supersedes them, as it already was.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
UpdateValidationStatesInternal now applies or leaves the validation
state groups, so OnValidationModeChanged no longer branches on
participation only for UpdateValidationStates to re-test it.

The template-application hook in FrameworkElement therefore leaves the
groups too, closing the mirror of the timing gap it was added for: a
control whose mode went to Disabled before its template was realized
had its GoToState return false with nothing left to re-apply it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The ObservableValidator of the MVVM Toolkit reports ValidationResult
rather than strings, and only its ToString reaches InputValidationError.
Realize the template's ErrorPresenter on the first error and present the
ErrorTemplate in it (inline, or in the DefaultCompactErrorIconTemplate
tooltip). Wired to HasValidationErrors, InputValidationMode and a new
protected OnErrorTemplateChanged, plus template application (Uno-only).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Template realization applies the disabled branch of
EnsureValidationVisuals, so a non-participating control lands in
ValidationDisabled while its error group stays unset.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Only TextBox, PasswordBox, AutoSuggestBox and ComboBox take part in
input validation, as in WinUI's CControl type switches. NumberBox,
Slider, ToggleSwitch and ToggleButton no longer implement
IInputValidationControl, and ComboBox validates Text rather than
SelectedItem, matching its WinUI [InputProperty].

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
A non-editable ComboBox is bound on SelectedItem, so validating Text
left the common setup without errors. ComboBox validates SelectedItem
again; the app-wide map and a shadowing subclass remain the routes to
validate SelectedIndex or another property instead.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The boxing analyzer (UnoInternal0002) that landed on master flags the
default(bool) metadata of the four per-control HasValidationErrors
registrations. Use BoolBoxes.False, as the rest of Uno.UI does.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The WinAppSDK sync generator keeps a placeholder for a hand-implemented
type, with its members skipped, and fails the check when it is missing.
This is the file it regenerates.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
WinAppSDK exposes no public input validation surface: neither
InputValidationError nor TextBox.InputValidationMode exist there, so
the sample cannot compile against it.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The handlers and the subscription lived in attached dependency properties,
a leftover from when a separate Validation class owned them. A single
lazily-created state object on Control now holds them.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The validation surface no longer lives on the WinUI types. The Mode, Kind,
ErrorTemplate, HasErrors and Errors properties are now attached properties
on Uno.Extras.Input.Validation, in Uno.UI.Extras. On WinAppSDK their
handlers are compiled out.

- IInputValidationControl is deleted, along with the per-control DPs and
  the HasValidationErrorsChanged, ValidationError and ErrorChanged events.
  The HasValidationErrorsChangedEventArgs stub is restored.
- InputValidationMode, InputValidationKind and InputValidationError move to
  Uno.Extras.Input and are compile-linked into Uno.UI.Extras.Windows.
- The engine stays on Control. It reads the attached DPs through an
  internal slot that Validation's static constructor fills, and a control
  participates through its validation property alone.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@Xiaoy312
Xiaoy312 force-pushed the dev/xygu/20260922/input-validation branch from 1630679 to 8b8ebbd Compare October 5, 2026 19:04
@unodevops

Copy link
Copy Markdown
Contributor

🤖 Your WebAssembly Skia Sample App stage site is ready! Visit it here: https://unowasmprstaging.z20.web.core.windows.net/pr-24838/wasm-skia-net9/index.html

@unodevops

Copy link
Copy Markdown
Contributor

🤖 Your Docs stage site is ready! Visit it here: https://unodocsprstaging.z13.web.core.windows.net/pr-24838/docs/index.html

@nventive-devops

Copy link
Copy Markdown
Contributor

The build 237533 found UI Test snapshots differences: skia-linux-screenshots: 120, skia-windows-screenshots: 163

Details
  • skia-linux-screenshots: 120 changed over 2458

    🚨🚨 Comparison Details (first 20) 🚨🚨
    • ClipboardTests.png-dark
    • AutomationProperties_AutomationId.png-dark
    • AutomationProperties_AutomationId.png
    • ButtonClippingTestsControl.png-dark
    • ButtonClippingTestsControl.png
    • CalendarView_Theming.png-dark
    • CompositionEffectBrush.png-dark
    • CompositionEffectBrush.png
    • CompositionNineGridBrush_Source_Changes.png-dark
    • CompositionNineGridBrush_Source_Changes.png
    • DisplayInformation.png-dark
    • Buttons.png-dark
    • DropDownButtonPage.png-dark
    • Examples.png
    • ImageBrush_Stretch.png-dark
    • ImageBrush_Stretch.png
    • DoubleImageBrushInList.png-dark
    • DoubleImageBrushInList.png
    • Gamepad_CurrentReading.png-dark
    • Gamepad_Enumeration.png-dark
  • skia-windows-screenshots: 163 changed over 2460

    🚨🚨 Comparison Details (first 20) 🚨🚨
    • CalendarView_Theming.png-dark
    • ClipboardTests.png-dark
    • ClipboardTests.png
    • ColorPickerSample.png-dark
    • ColorPickerSample.png
    • ButtonClippingTestsControl.png-dark
    • ButtonClippingTestsControl.png
    • Buttons.png-dark
    • Buttons.png
    • DoubleImageBrushInList.png-dark
    • DoubleImageBrushInList.png
    • ImageBrushInList.png-dark
    • ImageBrushInList.png
    • Examples.png
    • ImageBrush_Formats.png-dark
    • ImageBrush_Formats.png
    • ImageIconPage.png-dark
    • Focus_FocusVisual_Properties.png-dark
    • Focus_FocusVisual_Properties.png
    • Gamepad_CurrentReading.png-dark

@Xiaoy312
Xiaoy312 marked this pull request as ready for review October 5, 2026 22:10
@unodevops

Copy link
Copy Markdown
Contributor

🤖 Your Docs stage site is ready! Visit it here: https://unodocsprstaging.z13.web.core.windows.net/pr-24838/docs/index.html

@unodevops

Copy link
Copy Markdown
Contributor

🤖 Your WebAssembly Skia Sample App stage site is ready! Visit it here: https://unowasmprstaging.z20.web.core.windows.net/pr-24838/wasm-skia-net9/index.html

@nventive-devops

Copy link
Copy Markdown
Contributor

The build 237558 found UI Test snapshots differences: skia-linux-screenshots: 130, skia-windows-screenshots: 151

Details
  • skia-linux-screenshots: 130 changed over 2458

    🚨🚨 Comparison Details (first 20) 🚨🚨
    • ButtonClippingTestsControl.png-dark
    • ButtonClippingTestsControl.png
    • Buttons.png-dark
    • Buttons.png
    • CalendarView_Theming.png-dark
    • CompositionEffectBrush.png-dark
    • CompositionEffectBrush.png
    • CompositionNineGridBrush_Source_Changes.png-dark
    • CompositionNineGridBrush_Source_Changes.png
    • AutomationProperties_AutomationId.png-dark
    • AutomationProperties_AutomationId.png
    • ColorPickerSample.png-dark
    • ColorPickerSample.png
    • CompositionPath.png-dark
    • CompositionPath.png
    • ClipboardTests.png-dark
    • DisplayInformation.png-dark
    • ExpanderColorValidationPage.png-dark
    • ExpanderColorValidationPage.png
    • Focus_FocusVisual_Properties.png-dark
  • skia-windows-screenshots: 151 changed over 2460

    🚨🚨 Comparison Details (first 20) 🚨🚨
    • ButtonClippingTestsControl.png-dark
    • ButtonClippingTestsControl.png
    • Buttons.png-dark
    • Buttons.png
    • DisplayInformation.png-dark
    • CalendarView_Theming.png-dark
    • ClipboardTests.png-dark
    • ClipboardTests.png
    • DoubleImageBrushInList.png-dark
    • DoubleImageBrushInList.png
    • ElementLevelTheme.png-dark
    • ElementLevelTheme.png
    • Examples.png
    • Gamepad_CurrentReading.png-dark
    • Grid_ColSpan_Bottom.png-dark
    • Grid_ColSpan_Bottom.png
    • Gamepad_Enumeration.png-dark
    • Grid_DataBound_ColumnRow_Definitions.png-dark
    • Grid_DataBound_ColumnRow_Definitions.png
    • Grid_def_MinMaxValues.png-dark

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/automation Categorizes an issue or PR as relevant to project automation kind/documentation

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add support for WinUI 3 Input Validation

3 participants