From 0b01a51c9e83a779ea273277d9efc8a90e444fea Mon Sep 17 00:00:00 2001 From: hacklex Date: Sun, 28 Aug 2022 18:41:21 +0300 Subject: [PATCH] TextConverter property for finer control over NumericUpDown text display (#8839) * Added TextConverter property to NumericUpDown * Added a hex NumericUpDown sample to ControlCatalog * Requested changes to conversion calls * Fixed NumericUpDown::OnTextConverterChanged logic --- .../ControlCatalog/Converter/HexConverter.cs | 34 +++++++++++ .../Pages/NumericUpDownPage.xaml | 12 ++++ .../NumericUpDown/NumericUpDown.cs | 61 +++++++++++++++++++ 3 files changed, 107 insertions(+) create mode 100644 samples/ControlCatalog/Converter/HexConverter.cs diff --git a/samples/ControlCatalog/Converter/HexConverter.cs b/samples/ControlCatalog/Converter/HexConverter.cs new file mode 100644 index 0000000000..83a52212f6 --- /dev/null +++ b/samples/ControlCatalog/Converter/HexConverter.cs @@ -0,0 +1,34 @@ +using System; +using System.Globalization; +using Avalonia; +using Avalonia.Data.Converters; + +namespace ControlCatalog.Converter; + +public class HexConverter : IValueConverter +{ + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + var str = value?.ToString(); + if (str == null) + return AvaloniaProperty.UnsetValue; + if (int.TryParse(str, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out int x)) + return (decimal)x; + return AvaloniaProperty.UnsetValue; + + } + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + try + { + if (value is decimal d) + return ((int)d).ToString("X8"); + return AvaloniaProperty.UnsetValue; + } + catch + { + return AvaloniaProperty.UnsetValue; + } + } +} diff --git a/samples/ControlCatalog/Pages/NumericUpDownPage.xaml b/samples/ControlCatalog/Pages/NumericUpDownPage.xaml index 1f4d1e6018..045ba4a059 100644 --- a/samples/ControlCatalog/Pages/NumericUpDownPage.xaml +++ b/samples/ControlCatalog/Pages/NumericUpDownPage.xaml @@ -1,6 +1,7 @@  @@ -97,6 +98,17 @@ + + + + + + + + + + diff --git a/src/Avalonia.Controls/NumericUpDown/NumericUpDown.cs b/src/Avalonia.Controls/NumericUpDown/NumericUpDown.cs index dc2b2cd7cc..190dab67aa 100644 --- a/src/Avalonia.Controls/NumericUpDown/NumericUpDown.cs +++ b/src/Avalonia.Controls/NumericUpDown/NumericUpDown.cs @@ -5,6 +5,7 @@ using System.Linq; using Avalonia.Controls.Metadata; using Avalonia.Controls.Primitives; using Avalonia.Data; +using Avalonia.Data.Converters; using Avalonia.Input; using Avalonia.Interactivity; using Avalonia.Layout; @@ -96,6 +97,13 @@ namespace Avalonia.Controls AvaloniaProperty.RegisterDirect(nameof(Text), o => o.Text, (o, v) => o.Text = v, defaultBindingMode: BindingMode.TwoWay, enableDataValidation: true); + /// + /// Defines the property. + /// + public static readonly DirectProperty TextConverterProperty = + AvaloniaProperty.RegisterDirect(nameof(TextConverter), + updown => updown.TextConverter, (o, v) => o.TextConverter = v, null, BindingMode.OneWay, false); + /// /// Defines the property. /// @@ -125,6 +133,7 @@ namespace Avalonia.Controls private decimal? _value; private string? _text; + private IValueConverter? _textConverter; private bool _internalValueSet; private bool _clipValueToMinMax; private bool _isSyncingTextAndValueProperties; @@ -235,6 +244,8 @@ namespace Avalonia.Controls /// /// Gets or sets the parsing style (AllowLeadingWhite, Float, AllowHexSpecifier, ...). By default, Any. + /// Note that Hex style does not work with decimal. + /// For hexadecimal display, use . /// public NumberStyles ParsingNumberStyle { @@ -251,6 +262,17 @@ namespace Avalonia.Controls set { SetAndRaise(TextProperty, ref _text, value); } } + /// + /// Gets or sets the custom bidirectional Text-Value converter. + /// Non-null converter overrides , providing finer control over + /// string representation of the underlying value. + /// + public IValueConverter? TextConverter + { + get { return _textConverter; } + set { SetAndRaise(TextConverterProperty, ref _textConverter, value); } + } + /// /// Gets or sets the value. /// @@ -319,6 +341,7 @@ namespace Avalonia.Controls MaximumProperty.Changed.Subscribe(OnMaximumChanged); MinimumProperty.Changed.Subscribe(OnMinimumChanged); TextProperty.Changed.Subscribe(OnTextChanged); + TextConverterProperty.Changed.Subscribe(OnTextConverterChanged); ValueProperty.Changed.Subscribe(OnValueChanged); } @@ -485,6 +508,19 @@ namespace Avalonia.Controls SyncTextAndValueProperties(true, Text); } } + + /// + /// Called when the property value changed. + /// + /// The old value. + /// The new value. + protected virtual void OnTextConverterChanged(IValueConverter? oldValue, IValueConverter? newValue) + { + if (IsInitialized) + { + SyncTextAndValueProperties(false, null); + } + } /// /// Called when the property value changed. @@ -612,6 +648,10 @@ namespace Avalonia.Controls /// private string? ConvertValueToText() { + if (TextConverter != null) + { + return TextConverter.ConvertBack(Value, typeof(string), null, CultureInfo.CurrentCulture)?.ToString(); + } //Manage FormatString of type "{}{0:N2} °" (in xaml) or "{0:N2} °" in code-behind. if (FormatString.Contains("{0")) { @@ -788,6 +828,21 @@ namespace Avalonia.Controls } } + /// + /// Called when the property value changed. + /// + /// The event args. + private static void OnTextConverterChanged(AvaloniaPropertyChangedEventArgs e) + { + if (e.Sender is NumericUpDown upDown) + { + var oldValue = (IValueConverter?)e.OldValue; + var newValue = (IValueConverter?)e.NewValue; + upDown.OnTextConverterChanged(oldValue, newValue); + } + } + + /// /// Called when the property value changed. /// @@ -1012,6 +1067,12 @@ namespace Avalonia.Controls return null; } + if (TextConverter != null) + { + var valueFromText = TextConverter.Convert(text, typeof(decimal?), null, CultureInfo.CurrentCulture); + return (decimal?)valueFromText; + } + if (IsPercent(FormatString)) { result = ParsePercent(text, NumberFormat);