{"record":{"id":"30222bbe33fb6863","repo":"lepoco/wpfui","slug":"nameof-numberformatter-must-implement-typeof-i","errorCode":null,"errorMessage":"{nameof(NumberFormatter)} must implement {typeof(INumberParser)}","messagePattern":"(.+?) must implement (.+?)","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"src/Wpf.Ui/Controls/NumberBox/NumberBox.cs","lineNumber":521,"sourceCode":"\n    private static INumberFormatter GetRegionalSettingsAwareDecimalFormatter()\n    {\n        return new ValidateNumberFormatter();\n    }\n\n    private static void OnValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)\n    {\n        if (d is NumberBox numberBox)\n        {\n            numberBox.OnValueChanged(d, (double?)e.OldValue);\n        }\n    }\n\n    private static void OnNumberFormatterChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)\n    {\n        if (e.NewValue is not INumberParser)\n        {\n            throw new InvalidOperationException(\n                $\"{nameof(NumberFormatter)} must implement {typeof(INumberParser)}\"\n            );\n        }\n    }\n\n    private static void OnMaxDecimalPlacesChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)\n    {\n        if (d is not NumberBox numberBox)\n        {\n            return;\n        }\n    \n        if (numberBox.Value is double currentValue)\n        {\n            numberBox.SetCurrentValue(ValueProperty, Math.Round(currentValue, numberBox.MaxDecimalPlaces));\n        }\n    }\n","sourceCodeStart":503,"sourceCodeEnd":539,"githubUrl":"https://github.com/lepoco/wpfui/blob/ffebacd61058170cf63864b7d5aa730cffff848a/src/Wpf.Ui/Controls/NumberBox/NumberBox.cs#L503-L539","documentation":"NumberBox exposes NumberFormatter as INumberFormatter, but the property-changed callback OnNumberFormatterChanged additionally requires the assigned object to implement INumberParser (so the control can parse user-typed text back into numbers). Assigning any INumberFormatter that lacks INumberParser throws InvalidOperationException at the moment SetValue runs. The built-in ValidateNumberFormatter implements both interfaces; custom formatters must too.","triggerScenarios":"Calling numberBox.NumberFormatter = myFormatter where myFormatter implements INumberFormatter but not INumberParser; passing a WinUI/Windows.Globalization.NumberFormatting INumberFormatter that only formats.","commonSituations":"Substituting a third-party formatter that formats-only; deriving from a formatter base that does not implement parsing; partial port of a formatter where the ParseNum/ParseDouble methods were left out.","solutions":["Implement INumberParser (ParseDouble/ParseInt/ParseUInt) on your custom formatter in addition to INumberFormatter.","Reuse the built-in ValidateNumberFormatter which already implements both.","Decorate/wrap the format-only formatter inside a class that delegates parsing to a parser you control."],"exampleFix":"// before\npublic class MoneyFormatter : INumberFormatter\n{\n    public string FormatDouble(double value) => value.ToString(\"C\");\n}\nbox.NumberFormatter = new MoneyFormatter(); // throws\n\n// after\npublic class MoneyFormatter : INumberFormatter, INumberParser\n{\n    public string FormatDouble(double value) => value.ToString(\"C\");\n    public double? ParseDouble(string text) => double.TryParse(text, NumberStyles.Currency, CultureInfo.CurrentCulture, out var v) ? v : null;\n    public int? ParseInt(string text) => (int?)ParseDouble(text);\n    public uint? ParseUInt(string text) => (uint?)ParseDouble(text);\n}","handlingStrategy":"type-guard","validationCode":"if (formatter is not INumberParser)\n{\n    throw new ArgumentException(\"NumberFormatter must also implement INumberParser.\", nameof(formatter));\n}\nbox.NumberFormatter = formatter;","typeGuard":"static bool IsCompatibleFormatter(object? f) => f is INumberFormatter and INumberParser;","tryCatchPattern":"try { box.NumberFormatter = customFormatter; }\ncatch (InvalidOperationException ex) when (ex.Message.Contains(\"INumberParser\"))\n{\n    _logger.LogError(ex, \"Formatter does not implement INumberParser\");\n}","preventionTips":["Implement both INumberFormatter and INumberParser on custom formatters.","Prefer the built-in ValidateNumberFormatter unless you need custom formatting.","Add a unit test asserting your formatter implements INumberParser."],"tags":["numberbox","validation","formatter","wpf"],"backgroundTag":null,"analyzedSha":"ffebacd61058170cf63864b7d5aa730cffff848a","analyzedAt":"2026-08-13T21:36:01.370Z","schemaVersion":2},"datasetVersion":"2026-08-14T00:17:13.853Z"}