Every strong type carries its converter as a [TypeConverter] attribute on the type itself, so any framework that resolves converters through TypeDescriptor binds two-way with no package and no registration call. This issue originally tracked extending that to more UI frameworks; it is now refocused (see the discussion with @drventure below) on proving that WPF and WinForms actually hold up in a production scenario — a real form with a submit action, where a successful submit commits strong-typed values and invalid input shows a visible error to the user.
What's already proven
Binding mechanics, on isolated TextBoxes:
| Framework |
Suite |
Covered |
| WPF |
StrongTypes.Wpf.Tests |
Two-way binding; invalid input leaves the source untouched and raises Validation.GetHasError; recovery on the next valid input; culture; nullable properties |
| WinForms |
StrongTypes.WinForms.Tests (#127) |
Two-way binding; invalid input leaves the source untouched and fires BindingComplete with BindingCompleteState.Exception; culture (FormatInfo ?? CurrentCulture — the opposite default from WPF); nullable properties |
What's not proven is the part the user actually experiences: the tests assert the programmatic error signal, not that the error is presented on screen, and no test exercises a submit flow. Only the positive path has been checked by hand.
Remaining work
For each framework, build a small but production-shaped form — several strong-typed fields (e.g. NonEmptyString name, Email, Positive<int> age) plus a submit action — and test both paths end-to-end:
Success path
- Fill all fields with valid input and submit.
- Assert every value reached the view model as a strong type and the submit action ran with those values.
Error path
- Enter invalid input (whitespace name, malformed email,
0 age, non-numeric text) and assert:
- the source property keeps its previous value;
- the error is visibly shown the way production apps show it:
- WPF — the default
ErrorTemplate adorner is applied to the offending control, and Validation.GetErrors(...) carries the message the user sees; pin down what message the TypeConverter exception actually produces on screen;
- WinForms — an
ErrorProvider shows the error icon and text on the offending control, wired the way a production form would wire it (BindingSource / BindingComplete);
- submitting while an error is pending does not commit the invalid value — define and assert exactly what happens (submit blocked vs. stale value submitted);
- correcting the input clears the visible error and a subsequent submit succeeds.
Document the answers
- The converter throws on invalid input, so the per-field error message is not configurable. Confirm what text the user actually sees in each framework and document it in the readme's WPF/WinForms sections, including the escape hatch for custom messages: bind to a
string property and map to the strong type yourself.
Related, outside binding
DataAnnotations validation attributes compose with strong-typed properties ([Required], [MaxLength]/[MinLength]/[Length], [RegularExpression], and since #126 plain [Range]); details in the comments below.
Deferred — other frameworks (implement on request)
- MAUI — two-way binding cannot work via
[TypeConverter] at all: the target → source path converts with Convert.ChangeType only and never consults converters (verified empirically against headless Microsoft.Maui.Controls 10.0.80 and cross-checked in the MAUI source; display works for every strong type via the implicit-to-string operators / ToString). Support would mean a StrongTypeValueConverter (IValueConverter, string ↔ strong type via Parse / ToString) in a potential Kalicz.StrongTypes.Maui package, plus a headless test suite (the plain net10.0 TFM restores without the MAUI workload; the rig needs a small IDispatcherProvider stub because bindings look up a dispatcher on source property changes).
- WinUI 3 / WinAppSDK — not investigated; a XAML markup compiler plus a WinRT type system, needs its own validation pass.
- Avalonia — not investigated.
Every strong type carries its converter as a
[TypeConverter]attribute on the type itself, so any framework that resolves converters throughTypeDescriptorbinds two-way with no package and no registration call. This issue originally tracked extending that to more UI frameworks; it is now refocused (see the discussion with @drventure below) on proving that WPF and WinForms actually hold up in a production scenario — a real form with a submit action, where a successful submit commits strong-typed values and invalid input shows a visible error to the user.What's already proven
Binding mechanics, on isolated
TextBoxes:StrongTypes.Wpf.TestsValidation.GetHasError; recovery on the next valid input; culture; nullable propertiesStrongTypes.WinForms.Tests(#127)BindingCompletewithBindingCompleteState.Exception; culture (FormatInfo ?? CurrentCulture— the opposite default from WPF); nullable propertiesWhat's not proven is the part the user actually experiences: the tests assert the programmatic error signal, not that the error is presented on screen, and no test exercises a submit flow. Only the positive path has been checked by hand.
Remaining work
For each framework, build a small but production-shaped form — several strong-typed fields (e.g.
NonEmptyStringname,Email,Positive<int>age) plus a submit action — and test both paths end-to-end:Success path
Error path
0age, non-numeric text) and assert:ErrorTemplateadorner is applied to the offending control, andValidation.GetErrors(...)carries the message the user sees; pin down what message theTypeConverterexception actually produces on screen;ErrorProvidershows the error icon and text on the offending control, wired the way a production form would wire it (BindingSource/BindingComplete);Document the answers
stringproperty and map to the strong type yourself.Related, outside binding
DataAnnotations validation attributes compose with strong-typed properties (
[Required],[MaxLength]/[MinLength]/[Length],[RegularExpression], and since #126 plain[Range]); details in the comments below.Deferred — other frameworks (implement on request)
[TypeConverter]at all: the target → source path converts withConvert.ChangeTypeonly and never consults converters (verified empirically against headlessMicrosoft.Maui.Controls10.0.80 and cross-checked in the MAUI source; display works for every strong type via the implicit-to-string operators /ToString). Support would mean aStrongTypeValueConverter(IValueConverter, string ↔ strong type viaParse/ToString) in a potentialKalicz.StrongTypes.Mauipackage, plus a headless test suite (the plainnet10.0TFM restores without the MAUI workload; the rig needs a smallIDispatcherProviderstub because bindings look up a dispatcher on source property changes).