From 795abc992955fd6bf7dcd09c0d25cc95ee3fc64e Mon Sep 17 00:00:00 2001 From: heartacker Date: Wed, 20 Mar 2024 15:48:21 +0800 Subject: [PATCH 01/13] add HorizontalContentAlignment --- .../Controls/NumericUpDown.axaml | 7 +- .../NumericUpDown/NumericUpDownBase.cs | 76 +++++++++++-------- 2 files changed, 47 insertions(+), 36 deletions(-) diff --git a/src/Ursa.Themes.Semi/Controls/NumericUpDown.axaml b/src/Ursa.Themes.Semi/Controls/NumericUpDown.axaml index bb3cb95..7bffee8 100644 --- a/src/Ursa.Themes.Semi/Controls/NumericUpDown.axaml +++ b/src/Ursa.Themes.Semi/Controls/NumericUpDown.axaml @@ -3,7 +3,7 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:u="https://irihi.tech/ursa"> - + @@ -23,7 +23,7 @@ - + @@ -50,6 +50,7 @@ DataValidationErrors.Errors="{ReflectionBinding $parent[NumericUpDown].(DataValidationErrors.Errors)}" FontSize="{TemplateBinding FontSize}" Foreground="{TemplateBinding Foreground}" + HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}" IsReadOnly="{TemplateBinding IsReadOnly}" TextWrapping="NoWrap" InnerLeftContent="{TemplateBinding InnerLeftContent}" @@ -75,7 +76,7 @@ - + - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/demo/Ursa.Demo/Pages/NumericUpDownDemo.axaml.cs b/demo/Ursa.Demo/Pages/NumericUpDownDemo.axaml.cs index 4bdf06d..8ca1387 100644 --- a/demo/Ursa.Demo/Pages/NumericUpDownDemo.axaml.cs +++ b/demo/Ursa.Demo/Pages/NumericUpDownDemo.axaml.cs @@ -1,6 +1,9 @@ using Avalonia; using Avalonia.Controls; using Avalonia.Markup.Xaml; +using System; +using System.Diagnostics; +using Ursa.Controls; using Ursa.Demo.ViewModels; namespace Ursa.Demo.Pages; @@ -11,5 +14,12 @@ public partial class NumericUpDownDemo : UserControl { InitializeComponent(); DataContext = new NumericUpDownDemoViewModel(); + numd.ValueChanged += Numd_ValueChanged; + } + + private void Numd_ValueChanged(object? sender, ValueChangedEventArgs e) + { + Trace.WriteLine($"{(sender as NumericUIntUpDown).Name} {e.OldValue} {e.NewValue}"); + } } \ No newline at end of file diff --git a/demo/Ursa.Demo/ViewModels/NumericUpDownDemoViewModel.cs b/demo/Ursa.Demo/ViewModels/NumericUpDownDemoViewModel.cs index 9840cca..cd229ae 100644 --- a/demo/Ursa.Demo/ViewModels/NumericUpDownDemoViewModel.cs +++ b/demo/Ursa.Demo/ViewModels/NumericUpDownDemoViewModel.cs @@ -1,8 +1,50 @@ -using CommunityToolkit.Mvvm.ComponentModel; +using Avalonia.Controls; +using Avalonia.Layout; +using CommunityToolkit.Mvvm.ComponentModel; +using System; +using System.Globalization; +using Ursa.Controls; +using static System.Runtime.InteropServices.JavaScript.JSType; namespace Ursa.Demo.ViewModels; -public class NumericUpDownDemoViewModel: ObservableObject +public partial class NumericUpDownDemoViewModel : ObservableObject { - + + [ObservableProperty] private uint _Value; + [ObservableProperty] private string _FontFamily = "Consolas"; + [ObservableProperty] private bool _AllowDrag = false; + [ObservableProperty] private bool _IsReadOnly = false; + + [ObservableProperty] private Array _Array_HorizontalContentAlignment; + [ObservableProperty] private HorizontalAlignment _HorizontalContentAlignment = HorizontalAlignment.Center; + [ObservableProperty] private object? _InnerLeftContent = "obj:0x"; + [ObservableProperty] private string _Watermark = "Water mark showed"; + [ObservableProperty] private string _FormatString = "x8"; + [ObservableProperty] private Array _Array_ParsingNumberStyle; + [ObservableProperty] private NumberStyles _ParsingNumberStyle = NumberStyles.AllowHexSpecifier; + [ObservableProperty] private bool _AllowSpin = true; + [ObservableProperty] private bool _ShowButtonSpinner = true; + + [ObservableProperty] private UInt32 _Maximum = UInt32.MaxValue; + [ObservableProperty] private UInt32 _Minimum = UInt32.MinValue; + [ObservableProperty] private UInt32 _Step = 1; + + [ObservableProperty] private bool _IsEnable = true; + + + public NumericUpDownDemoViewModel() + { + Array_HorizontalContentAlignment = Enum.GetValues(typeof(HorizontalAlignment)); + Array_ParsingNumberStyle = Enum.GetValues(typeof(NumberStyles)); + NumericUIntUpDown numericUIntUpDown; + TextBox textBox; + + } + + partial void OnValueChanging(uint oldValue, uint newValue) + { + Console.WriteLine(oldValue); + } + } \ No newline at end of file diff --git a/src/Ursa.Themes.Semi/Controls/NumericUpDown.axaml b/src/Ursa.Themes.Semi/Controls/NumericUpDown.axaml index 11c4b6b..f9533e5 100644 --- a/src/Ursa.Themes.Semi/Controls/NumericUpDown.axaml +++ b/src/Ursa.Themes.Semi/Controls/NumericUpDown.axaml @@ -55,7 +55,6 @@ InnerLeftContent="{TemplateBinding InnerLeftContent}" IsReadOnly="{TemplateBinding IsReadOnly}" TextWrapping="NoWrap" - InnerLeftContent="{TemplateBinding InnerLeftContent}" Theme="{DynamicResource NonErrorTextBox}" Watermark="{TemplateBinding Watermark}" /> Date: Wed, 20 Mar 2024 23:21:23 +0800 Subject: [PATCH 05/13] update mvvm version --- demo/Ursa.Demo/Ursa.Demo.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/demo/Ursa.Demo/Ursa.Demo.csproj b/demo/Ursa.Demo/Ursa.Demo.csproj index 736e24e..7227b71 100644 --- a/demo/Ursa.Demo/Ursa.Demo.csproj +++ b/demo/Ursa.Demo/Ursa.Demo.csproj @@ -18,7 +18,7 @@ - + From aa2127e03dfd101e34f70782142757d6a5a1985f Mon Sep 17 00:00:00 2001 From: heartacker Date: Thu, 21 Mar 2024 12:48:10 +0800 Subject: [PATCH 06/13] use Property.AddOwner --- .../NumericUpDown/NumericUpDownBase.cs | 1289 ++++++++--------- 1 file changed, 644 insertions(+), 645 deletions(-) diff --git a/src/Ursa/Controls/NumericUpDown/NumericUpDownBase.cs b/src/Ursa/Controls/NumericUpDown/NumericUpDownBase.cs index 48abea8..eb53464 100644 --- a/src/Ursa/Controls/NumericUpDown/NumericUpDownBase.cs +++ b/src/Ursa/Controls/NumericUpDown/NumericUpDownBase.cs @@ -1,646 +1,645 @@ -using System.Diagnostics; -using System.Globalization; -using System.Net.Mime; -using Avalonia; -using Avalonia.Controls; -using Avalonia.Controls.Metadata; -using Avalonia.Controls.Primitives; -using Avalonia.Data; -using Avalonia.Data.Converters; -using Avalonia.Input; -using Avalonia.Interactivity; -using Avalonia.Layout; -using Irihi.Avalonia.Shared.Contracts; - -namespace Ursa.Controls; - -[TemplatePart(PART_Spinner, typeof(ButtonSpinner))] -[TemplatePart(PART_TextBox, typeof(TextBox))] -[TemplatePart(PART_DragPanel, typeof(Panel))] -public abstract class NumericUpDown : TemplatedControl, IClearControl -{ - public const string PART_Spinner = "PART_Spinner"; - public const string PART_TextBox = "PART_TextBox"; - public const string PART_DragPanel = "PART_DragPanel"; - - protected internal ButtonSpinner? _spinner; - protected internal TextBox? _textBox; - protected internal Panel? _dragPanel; - - private Point? _point; - protected internal bool _updateFromTextInput; - - public static readonly StyledProperty AllowDragProperty = AvaloniaProperty.Register( - nameof(AllowDrag), defaultValue: false); - - public bool AllowDrag - { - get => GetValue(AllowDragProperty); - set => SetValue(AllowDragProperty, value); - } - - public static readonly StyledProperty IsReadOnlyProperty = AvaloniaProperty.Register( - nameof(IsReadOnly)); - - public bool IsReadOnly - { - get => GetValue(IsReadOnlyProperty); - set => SetValue(IsReadOnlyProperty, value); - } - - public static readonly StyledProperty HorizontalContentAlignmentProperty = AvaloniaProperty.Register( - nameof(HorizontalContentAlignment), HorizontalAlignment.Left); - - public HorizontalAlignment HorizontalContentAlignment - { - get => GetValue(HorizontalContentAlignmentProperty); - set => SetValue(HorizontalContentAlignmentProperty, value); - } - - public static readonly StyledProperty InnerLeftContentProperty = AvaloniaProperty.Register( - nameof(InnerLeftContent)); - - public object? InnerLeftContent - { - get => GetValue(InnerLeftContentProperty); - set => SetValue(InnerLeftContentProperty, value); - } - - public static readonly StyledProperty WatermarkProperty = AvaloniaProperty.Register( - nameof(Watermark)); - - public string? Watermark - { - get => GetValue(WatermarkProperty); - set => SetValue(WatermarkProperty, value); - } - - public static readonly StyledProperty NumberFormatProperty = AvaloniaProperty.Register( - nameof(NumberFormat), defaultValue: NumberFormatInfo.CurrentInfo); - - public NumberFormatInfo? NumberFormat - { - get => GetValue(NumberFormatProperty); - set => SetValue(NumberFormatProperty, value); - } - - public static readonly StyledProperty FormatStringProperty = AvaloniaProperty.Register( - nameof(FormatString), string.Empty); - - public string FormatString - { - get => GetValue(FormatStringProperty); - set => SetValue(FormatStringProperty, value); - } - - public static readonly StyledProperty ParsingNumberStyleProperty = AvaloniaProperty.Register( - nameof(ParsingNumberStyle), defaultValue: NumberStyles.Any); - - public NumberStyles ParsingNumberStyle - { - get => GetValue(ParsingNumberStyleProperty); - set => SetValue(ParsingNumberStyleProperty, value); - } - - public static readonly StyledProperty TextConverterProperty = AvaloniaProperty.Register( - nameof(TextConverter)); - - public IValueConverter? TextConverter - { - get => GetValue(TextConverterProperty); - set => SetValue(TextConverterProperty, value); - } - - public static readonly StyledProperty AllowSpinProperty = AvaloniaProperty.Register( - nameof(AllowSpin), true); - - public bool AllowSpin - { - get => GetValue(AllowSpinProperty); - set => SetValue(AllowSpinProperty, value); - } - - public static readonly StyledProperty ShowButtonSpinnerProperty = - ButtonSpinner.ShowButtonSpinnerProperty.AddOwner(); - - public bool ShowButtonSpinner - { - get => GetValue(ShowButtonSpinnerProperty); - set => SetValue(ShowButtonSpinnerProperty, value); - } - - public event EventHandler? Spinned; - - static NumericUpDown() - { - NumberFormatProperty.Changed.AddClassHandler((o, e) => o.OnFormatChange(e)); - FormatStringProperty.Changed.AddClassHandler((o, e) => o.OnFormatChange(e)); - IsReadOnlyProperty.Changed.AddClassHandler((o, e) => o.ChangeToSetSpinDirection(e)); - TextConverterProperty.Changed.AddClassHandler((o, e) => o.OnFormatChange(e)); - } - - protected void ChangeToSetSpinDirection(AvaloniaPropertyChangedEventArgs avaloniaPropertyChangedEventArgs, bool afterInitialization = false) - { - if (afterInitialization) - { - if (IsInitialized) - { - SetValidSpinDirection(); - } - } - else - { - SetValidSpinDirection(); - } - } - - protected virtual void OnFormatChange(AvaloniaPropertyChangedEventArgs arg) - { - if (IsInitialized) - { - SyncTextAndValue(false, null); - } - } - - protected override void OnApplyTemplate(TemplateAppliedEventArgs e) - { - base.OnApplyTemplate(e); - if (_spinner is not null) - { - _spinner.Spin -= OnSpin; - } - if (_dragPanel is not null) - { - _dragPanel.PointerPressed -= OnDragPanelPointerPressed; - _dragPanel.PointerMoved -= OnDragPanelPointerMoved; - _dragPanel.PointerReleased -= OnDragPanelPointerReleased; - } - _spinner = e.NameScope.Find(PART_Spinner); - _textBox = e.NameScope.Find(PART_TextBox); - _dragPanel = e.NameScope.Find(PART_DragPanel); - if (_spinner is not null) - { - _spinner.Spin += OnSpin; - } - if (_dragPanel is not null) - { - _dragPanel.PointerPressed += OnDragPanelPointerPressed; - _dragPanel.PointerMoved += OnDragPanelPointerMoved; - _dragPanel.PointerReleased += OnDragPanelPointerReleased; - } - - } - - protected override void OnLostFocus(RoutedEventArgs e) - { - CommitInput(true); - base.OnLostFocus(e); - if (AllowDrag && _dragPanel is not null) - { - _dragPanel.IsVisible = true; - } - } - - protected override void OnKeyDown(KeyEventArgs e) - { - if (e.Key == Key.Enter) - { - var commitSuccess = CommitInput(true); - e.Handled = !commitSuccess; - } - - if (e.Key == Key.Escape) - { - if (AllowDrag && _dragPanel is not null) - { - _dragPanel.IsVisible = true; - _dragPanel.Focus(); - } - } - } - - private void OnDragPanelPointerPressed(object sender, PointerPressedEventArgs e) - { - _point = e.GetPosition(this); - if (e.ClickCount == 2 && _dragPanel is not null && AllowDrag) - { - _dragPanel.IsVisible = false; - _textBox.IsReadOnly = false; - } - else - { - _textBox?.Focus(); - _textBox!.IsReadOnly = true; - } - } - - protected override void OnTextInput(TextInputEventArgs e) - { - _textBox?.RaiseEvent(e); - } - - private void OnDragPanelPointerReleased(object sender, PointerReleasedEventArgs e) - { - _point = null; - } - - private void OnDragPanelPointerMoved(object sender, PointerEventArgs e) - { - if (!AllowDrag || IsReadOnly) return; - if (!e.GetCurrentPoint(this).Properties.IsLeftButtonPressed) return; - var point = e.GetPosition(this); - var delta = point - _point; - if (delta is null) - { - return; - } - int d = GetDelta(delta.Value); - if (d > 0) - { - Increase(); - } - else if (d < 0) - { - Decrease(); - } - _point = point; - } - - private int GetDelta(Point point) - { - bool horizontal = Math.Abs(point.X) > Math.Abs(point.Y); - var value = horizontal ? point.X : -point.Y; - return value switch - { - > 0 => 1, - < 0 => -1, - _ => 0 - }; - } - - private void OnSpin(object sender, SpinEventArgs e) - { - if (AllowSpin && !IsReadOnly) - { - var spin = !e.UsingMouseWheel; - spin |= _textBox is { IsFocused: true }; - if (spin) - { - e.Handled = true; - var handler = Spinned; - handler?.Invoke(this, e); - if (e.Direction == SpinDirection.Increase) - { - Increase(); - } - else - { - Decrease(); - } - } - } - } - - protected abstract void SetValidSpinDirection(); - - protected abstract void Increase(); - protected abstract void Decrease(); - - protected virtual bool CommitInput(bool forceTextUpdate = false) - { - return SyncTextAndValue(true, _textBox?.Text, forceTextUpdate); - } - - protected abstract bool SyncTextAndValue(bool fromTextToValue = false, string? text = null, - bool forceTextUpdate = false); - - public abstract void Clear(); -} - -public abstract class NumericUpDownBase : NumericUpDown where T : struct, IComparable -{ - public static readonly StyledProperty ValueProperty = AvaloniaProperty.Register, T?>( - nameof(Value), defaultBindingMode: BindingMode.TwoWay); - - public T? Value - { - get => GetValue(ValueProperty); - set => SetValue(ValueProperty, value); - } - - public static readonly StyledProperty MaximumProperty = AvaloniaProperty.Register, T>( - nameof(Maximum), defaultBindingMode: BindingMode.TwoWay, coerce: CoerceMaximum); - - public T Maximum - { - get => GetValue(MaximumProperty); - set => SetValue(MaximumProperty, value); - } - - public static readonly StyledProperty MinimumProperty = AvaloniaProperty.Register, T>( - nameof(Minimum), defaultBindingMode: BindingMode.TwoWay, coerce: CoerceMinimum); - - public T Minimum - { - get => GetValue(MinimumProperty); - set => SetValue(MinimumProperty, value); - } - - #region Max and Min Coerce - private static T CoerceMaximum(AvaloniaObject instance, T value) - { - if (instance is NumericUpDownBase n) - { - return n.CoerceMaximum(value); - } - - return value; - } - - private T CoerceMaximum(T value) - { - if (value.CompareTo(Minimum) < 0) - { - return Minimum; - } - return value; - } - - private static T CoerceMinimum(AvaloniaObject instance, T value) - { - if (instance is NumericUpDownBase n) - { - return n.CoerceMinimum(value); - } - - return value; - } - - private T CoerceMinimum(T value) - { - if (value.CompareTo(Maximum) > 0) - { - return Maximum; - } - return value; - } - - #endregion - - public static readonly StyledProperty StepProperty = AvaloniaProperty.Register, T>( - nameof(Step)); - - public T Step - { - get => GetValue(StepProperty); - set => SetValue(StepProperty, value); - } - - public static readonly StyledProperty EmptyInputValueProperty = - AvaloniaProperty.Register, T?>( - nameof(EmptyInputValue), defaultValue: null); - - public T? EmptyInputValue - { - get => GetValue(EmptyInputValueProperty); - set => SetValue(EmptyInputValueProperty, value); - } - - /// - /// Defines the event. - /// - public static readonly RoutedEvent> ValueChangedEvent = - RoutedEvent.Register>(nameof(ValueChanged), RoutingStrategies.Bubble); - - /// - /// Raised when the changes. - /// - public event EventHandler>? ValueChanged - { - add => AddHandler(ValueChangedEvent, value); - remove => RemoveHandler(ValueChangedEvent, value); - } - - static NumericUpDownBase() - { - StepProperty.Changed.AddClassHandler>((o, e) => o.ChangeToSetSpinDirection(e)); - MaximumProperty.Changed.AddClassHandler>((o, e) => o.OnConstraintChanged(e)); - MinimumProperty.Changed.AddClassHandler>((o, e) => o.OnConstraintChanged(e)); - ValueProperty.Changed.AddClassHandler>((o, e) => o.OnValueChanged(e)); - } - - private void OnConstraintChanged(AvaloniaPropertyChangedEventArgs avaloniaPropertyChangedEventArgs) - { - if (IsInitialized) - { - SetValidSpinDirection(); - } - if (Value.HasValue) - { - SetCurrentValue(ValueProperty, Clamp(Value, Maximum, Minimum)); - } - } - - private void OnValueChanged(AvaloniaPropertyChangedEventArgs args) - { - if (IsInitialized) - { - SyncTextAndValue(false, null, true); - } - SetValidSpinDirection(); - T? oldValue = args.GetOldValue(); - T? newValue = args.GetNewValue(); - var e = new ValueChangedEventArgs(ValueChangedEvent, oldValue, newValue); - RaiseEvent(e); - } - - protected override void OnApplyTemplate(TemplateAppliedEventArgs e) - { - base.OnApplyTemplate(e); - if (_textBox != null) - { - _textBox.Text = ConvertValueToText(Value); - } - SetValidSpinDirection(); - } - - protected virtual T? Clamp(T? value, T max, T min) - { - if (value is null) - { - return null; - } - if (value.Value.CompareTo(max) > 0) - { - return max; - } - if (value.Value.CompareTo(min) < 0) - { - return min; - } - return value; - } - - protected override void SetValidSpinDirection() - { - var validDirection = ValidSpinDirections.None; - if (!IsReadOnly) - { - if (Value is null) - { - validDirection = ValidSpinDirections.Increase | ValidSpinDirections.Decrease; - } - if (Value.HasValue && Value.Value.CompareTo(Maximum) < 0) - { - validDirection |= ValidSpinDirections.Increase; - } - - if (Value.HasValue && Value.Value.CompareTo(Minimum) > 0) - { - validDirection |= ValidSpinDirections.Decrease; - } - } - if (_spinner != null) - { - _spinner.ValidSpinDirection = validDirection; - } - } - - private bool _isSyncingTextAndValue; - - protected override bool SyncTextAndValue(bool fromTextToValue = false, string? text = null, bool forceTextUpdate = false) - { - if (_isSyncingTextAndValue) return true; - _isSyncingTextAndValue = true; - var parsedTextIsValid = true; - try - { - if (fromTextToValue) - { - try - { - var newValue = ConvertTextToValue(text); - if (EmptyInputValue is not null && newValue is null) - { - newValue = EmptyInputValue; - } - if (!Equals(newValue, Value)) - { - SetCurrentValue(ValueProperty, newValue); - } - } - catch - { - parsedTextIsValid = false; - } - } - - if (!_updateFromTextInput) - { - if (forceTextUpdate) - { - var newText = ConvertValueToText(Value); - if (_textBox != null && !Equals(_textBox.Text, newText)) - { - _textBox.Text = newText; - _textBox.CaretIndex = newText?.Length ?? 0; - } - } - } - - if (_updateFromTextInput && !parsedTextIsValid) - { - if (_spinner is not null) - { - _spinner.ValidSpinDirection = ValidSpinDirections.None; - } - } - else - { - SetValidSpinDirection(); - } - } - finally - { - _isSyncingTextAndValue = false; - } - return parsedTextIsValid; - } - - protected virtual T? ConvertTextToValue(string? text) - { - T? result; - if (string.IsNullOrWhiteSpace(text)) return null; - if (TextConverter != null) - { - var valueFromText = TextConverter.Convert(text, typeof(T?), null, CultureInfo.CurrentCulture); - return (T?)valueFromText; - } - else - { - if (!ParseText(text, out var outputValue)) - { - throw new InvalidDataException("Input string was not in a correct format."); - } - - result = outputValue; - } - return result; - } - - protected virtual string? ConvertValueToText(T? value) - { - if (TextConverter is not null) - { - return TextConverter.ConvertBack(Value, typeof(int), null, CultureInfo.CurrentCulture)?.ToString(); - } - - if (FormatString.Contains("{0")) - { - return string.Format(NumberFormat, FormatString, value); - } - - return ValueToString(Value); - } - - protected override void Increase() - { - T? value; - if (Value is not null) - { - value = Add(Value.Value, Step); - } - else - { - value = IsSet(MinimumProperty) ? Minimum : Zero; - } - SetCurrentValue(ValueProperty, Clamp(value, Maximum, Minimum)); - } - - protected override void Decrease() - { - T? value; - if (Value is not null) - { - value = Minus(Value.Value, Step); - } - else - { - value = IsSet(MaximumProperty) ? Maximum : Zero; - } - - SetCurrentValue(ValueProperty, Clamp(value, Maximum, Minimum)); - } - - protected abstract bool ParseText(string? text, out T number); - protected abstract string? ValueToString(T? value); - protected abstract T Zero { get; } - protected abstract T? Add(T? a, T? b); - protected abstract T? Minus(T? a, T? b); - - public override void Clear() - { - SetCurrentValue(ValueProperty, EmptyInputValue); - SyncTextAndValue(false, forceTextUpdate: true); - } +using System.Diagnostics; +using System.Globalization; +using System.Net.Mime; +using Avalonia; +using Avalonia.Controls; +using Avalonia.Controls.Metadata; +using Avalonia.Controls.Primitives; +using Avalonia.Data; +using Avalonia.Data.Converters; +using Avalonia.Input; +using Avalonia.Interactivity; +using Avalonia.Layout; +using Irihi.Avalonia.Shared.Contracts; + +namespace Ursa.Controls; + +[TemplatePart(PART_Spinner, typeof(ButtonSpinner))] +[TemplatePart(PART_TextBox, typeof(TextBox))] +[TemplatePart(PART_DragPanel, typeof(Panel))] +public abstract class NumericUpDown : TemplatedControl, IClearControl +{ + public const string PART_Spinner = "PART_Spinner"; + public const string PART_TextBox = "PART_TextBox"; + public const string PART_DragPanel = "PART_DragPanel"; + + protected internal ButtonSpinner? _spinner; + protected internal TextBox? _textBox; + protected internal Panel? _dragPanel; + + private Point? _point; + protected internal bool _updateFromTextInput; + + public static readonly StyledProperty AllowDragProperty = AvaloniaProperty.Register( + nameof(AllowDrag), defaultValue: false); + + public bool AllowDrag + { + get => GetValue(AllowDragProperty); + set => SetValue(AllowDragProperty, value); + } + + public static readonly StyledProperty IsReadOnlyProperty = AvaloniaProperty.Register( + nameof(IsReadOnly)); + + public bool IsReadOnly + { + get => GetValue(IsReadOnlyProperty); + set => SetValue(IsReadOnlyProperty, value); + } + + public static readonly StyledProperty HorizontalContentAlignmentProperty = + ContentControl.HorizontalContentAlignmentProperty.AddOwner(); + public HorizontalAlignment HorizontalContentAlignment + { + get => GetValue(HorizontalContentAlignmentProperty); + set => SetValue(HorizontalContentAlignmentProperty, value); + } + + public static readonly StyledProperty InnerLeftContentProperty = AvaloniaProperty.Register( + nameof(InnerLeftContent)); + + public object? InnerLeftContent + { + get => GetValue(InnerLeftContentProperty); + set => SetValue(InnerLeftContentProperty, value); + } + + public static readonly StyledProperty WatermarkProperty = AvaloniaProperty.Register( + nameof(Watermark)); + + public string? Watermark + { + get => GetValue(WatermarkProperty); + set => SetValue(WatermarkProperty, value); + } + + public static readonly StyledProperty NumberFormatProperty = AvaloniaProperty.Register( + nameof(NumberFormat), defaultValue: NumberFormatInfo.CurrentInfo); + + public NumberFormatInfo? NumberFormat + { + get => GetValue(NumberFormatProperty); + set => SetValue(NumberFormatProperty, value); + } + + public static readonly StyledProperty FormatStringProperty = AvaloniaProperty.Register( + nameof(FormatString), string.Empty); + + public string FormatString + { + get => GetValue(FormatStringProperty); + set => SetValue(FormatStringProperty, value); + } + + public static readonly StyledProperty ParsingNumberStyleProperty = AvaloniaProperty.Register( + nameof(ParsingNumberStyle), defaultValue: NumberStyles.Any); + + public NumberStyles ParsingNumberStyle + { + get => GetValue(ParsingNumberStyleProperty); + set => SetValue(ParsingNumberStyleProperty, value); + } + + public static readonly StyledProperty TextConverterProperty = AvaloniaProperty.Register( + nameof(TextConverter)); + + public IValueConverter? TextConverter + { + get => GetValue(TextConverterProperty); + set => SetValue(TextConverterProperty, value); + } + + public static readonly StyledProperty AllowSpinProperty = AvaloniaProperty.Register( + nameof(AllowSpin), true); + + public bool AllowSpin + { + get => GetValue(AllowSpinProperty); + set => SetValue(AllowSpinProperty, value); + } + + public static readonly StyledProperty ShowButtonSpinnerProperty = + ButtonSpinner.ShowButtonSpinnerProperty.AddOwner(); + + public bool ShowButtonSpinner + { + get => GetValue(ShowButtonSpinnerProperty); + set => SetValue(ShowButtonSpinnerProperty, value); + } + + public event EventHandler? Spinned; + + static NumericUpDown() + { + NumberFormatProperty.Changed.AddClassHandler((o, e) => o.OnFormatChange(e)); + FormatStringProperty.Changed.AddClassHandler((o, e) => o.OnFormatChange(e)); + IsReadOnlyProperty.Changed.AddClassHandler((o, e) => o.ChangeToSetSpinDirection(e)); + TextConverterProperty.Changed.AddClassHandler((o, e) => o.OnFormatChange(e)); + } + + protected void ChangeToSetSpinDirection(AvaloniaPropertyChangedEventArgs avaloniaPropertyChangedEventArgs, bool afterInitialization = false) + { + if (afterInitialization) + { + if (IsInitialized) + { + SetValidSpinDirection(); + } + } + else + { + SetValidSpinDirection(); + } + } + + protected virtual void OnFormatChange(AvaloniaPropertyChangedEventArgs arg) + { + if (IsInitialized) + { + SyncTextAndValue(false, null); + } + } + + protected override void OnApplyTemplate(TemplateAppliedEventArgs e) + { + base.OnApplyTemplate(e); + if (_spinner is not null) + { + _spinner.Spin -= OnSpin; + } + if (_dragPanel is not null) + { + _dragPanel.PointerPressed -= OnDragPanelPointerPressed; + _dragPanel.PointerMoved -= OnDragPanelPointerMoved; + _dragPanel.PointerReleased -= OnDragPanelPointerReleased; + } + _spinner = e.NameScope.Find(PART_Spinner); + _textBox = e.NameScope.Find(PART_TextBox); + _dragPanel = e.NameScope.Find(PART_DragPanel); + if (_spinner is not null) + { + _spinner.Spin += OnSpin; + } + if (_dragPanel is not null) + { + _dragPanel.PointerPressed += OnDragPanelPointerPressed; + _dragPanel.PointerMoved += OnDragPanelPointerMoved; + _dragPanel.PointerReleased += OnDragPanelPointerReleased; + } + + } + + protected override void OnLostFocus(RoutedEventArgs e) + { + CommitInput(true); + base.OnLostFocus(e); + if (AllowDrag && _dragPanel is not null) + { + _dragPanel.IsVisible = true; + } + } + + protected override void OnKeyDown(KeyEventArgs e) + { + if (e.Key == Key.Enter) + { + var commitSuccess = CommitInput(true); + e.Handled = !commitSuccess; + } + + if (e.Key == Key.Escape) + { + if (AllowDrag && _dragPanel is not null) + { + _dragPanel.IsVisible = true; + _dragPanel.Focus(); + } + } + } + + private void OnDragPanelPointerPressed(object sender, PointerPressedEventArgs e) + { + _point = e.GetPosition(this); + if (e.ClickCount == 2 && _dragPanel is not null && AllowDrag) + { + _dragPanel.IsVisible = false; + _textBox.IsReadOnly = false; + } + else + { + _textBox?.Focus(); + _textBox!.IsReadOnly = true; + } + } + + protected override void OnTextInput(TextInputEventArgs e) + { + _textBox?.RaiseEvent(e); + } + + private void OnDragPanelPointerReleased(object sender, PointerReleasedEventArgs e) + { + _point = null; + } + + private void OnDragPanelPointerMoved(object sender, PointerEventArgs e) + { + if (!AllowDrag || IsReadOnly) return; + if (!e.GetCurrentPoint(this).Properties.IsLeftButtonPressed) return; + var point = e.GetPosition(this); + var delta = point - _point; + if (delta is null) + { + return; + } + int d = GetDelta(delta.Value); + if (d > 0) + { + Increase(); + } + else if (d < 0) + { + Decrease(); + } + _point = point; + } + + private int GetDelta(Point point) + { + bool horizontal = Math.Abs(point.X) > Math.Abs(point.Y); + var value = horizontal ? point.X : -point.Y; + return value switch + { + > 0 => 1, + < 0 => -1, + _ => 0 + }; + } + + private void OnSpin(object sender, SpinEventArgs e) + { + if (AllowSpin && !IsReadOnly) + { + var spin = !e.UsingMouseWheel; + spin |= _textBox is { IsFocused: true }; + if (spin) + { + e.Handled = true; + var handler = Spinned; + handler?.Invoke(this, e); + if (e.Direction == SpinDirection.Increase) + { + Increase(); + } + else + { + Decrease(); + } + } + } + } + + protected abstract void SetValidSpinDirection(); + + protected abstract void Increase(); + protected abstract void Decrease(); + + protected virtual bool CommitInput(bool forceTextUpdate = false) + { + return SyncTextAndValue(true, _textBox?.Text, forceTextUpdate); + } + + protected abstract bool SyncTextAndValue(bool fromTextToValue = false, string? text = null, + bool forceTextUpdate = false); + + public abstract void Clear(); +} + +public abstract class NumericUpDownBase : NumericUpDown where T : struct, IComparable +{ + public static readonly StyledProperty ValueProperty = AvaloniaProperty.Register, T?>( + nameof(Value), defaultBindingMode: BindingMode.TwoWay); + + public T? Value + { + get => GetValue(ValueProperty); + set => SetValue(ValueProperty, value); + } + + public static readonly StyledProperty MaximumProperty = AvaloniaProperty.Register, T>( + nameof(Maximum), defaultBindingMode: BindingMode.TwoWay, coerce: CoerceMaximum); + + public T Maximum + { + get => GetValue(MaximumProperty); + set => SetValue(MaximumProperty, value); + } + + public static readonly StyledProperty MinimumProperty = AvaloniaProperty.Register, T>( + nameof(Minimum), defaultBindingMode: BindingMode.TwoWay, coerce: CoerceMinimum); + + public T Minimum + { + get => GetValue(MinimumProperty); + set => SetValue(MinimumProperty, value); + } + + #region Max and Min Coerce + private static T CoerceMaximum(AvaloniaObject instance, T value) + { + if (instance is NumericUpDownBase n) + { + return n.CoerceMaximum(value); + } + + return value; + } + + private T CoerceMaximum(T value) + { + if (value.CompareTo(Minimum) < 0) + { + return Minimum; + } + return value; + } + + private static T CoerceMinimum(AvaloniaObject instance, T value) + { + if (instance is NumericUpDownBase n) + { + return n.CoerceMinimum(value); + } + + return value; + } + + private T CoerceMinimum(T value) + { + if (value.CompareTo(Maximum) > 0) + { + return Maximum; + } + return value; + } + + #endregion + + public static readonly StyledProperty StepProperty = AvaloniaProperty.Register, T>( + nameof(Step)); + + public T Step + { + get => GetValue(StepProperty); + set => SetValue(StepProperty, value); + } + + public static readonly StyledProperty EmptyInputValueProperty = + AvaloniaProperty.Register, T?>( + nameof(EmptyInputValue), defaultValue: null); + + public T? EmptyInputValue + { + get => GetValue(EmptyInputValueProperty); + set => SetValue(EmptyInputValueProperty, value); + } + + /// + /// Defines the event. + /// + public static readonly RoutedEvent> ValueChangedEvent = + RoutedEvent.Register>(nameof(ValueChanged), RoutingStrategies.Bubble); + + /// + /// Raised when the changes. + /// + public event EventHandler>? ValueChanged + { + add => AddHandler(ValueChangedEvent, value); + remove => RemoveHandler(ValueChangedEvent, value); + } + + static NumericUpDownBase() + { + StepProperty.Changed.AddClassHandler>((o, e) => o.ChangeToSetSpinDirection(e)); + MaximumProperty.Changed.AddClassHandler>((o, e) => o.OnConstraintChanged(e)); + MinimumProperty.Changed.AddClassHandler>((o, e) => o.OnConstraintChanged(e)); + ValueProperty.Changed.AddClassHandler>((o, e) => o.OnValueChanged(e)); + } + + private void OnConstraintChanged(AvaloniaPropertyChangedEventArgs avaloniaPropertyChangedEventArgs) + { + if (IsInitialized) + { + SetValidSpinDirection(); + } + if (Value.HasValue) + { + SetCurrentValue(ValueProperty, Clamp(Value, Maximum, Minimum)); + } + } + + private void OnValueChanged(AvaloniaPropertyChangedEventArgs args) + { + if (IsInitialized) + { + SyncTextAndValue(false, null, true); + } + SetValidSpinDirection(); + T? oldValue = args.GetOldValue(); + T? newValue = args.GetNewValue(); + var e = new ValueChangedEventArgs(ValueChangedEvent, oldValue, newValue); + RaiseEvent(e); + } + + protected override void OnApplyTemplate(TemplateAppliedEventArgs e) + { + base.OnApplyTemplate(e); + if (_textBox != null) + { + _textBox.Text = ConvertValueToText(Value); + } + SetValidSpinDirection(); + } + + protected virtual T? Clamp(T? value, T max, T min) + { + if (value is null) + { + return null; + } + if (value.Value.CompareTo(max) > 0) + { + return max; + } + if (value.Value.CompareTo(min) < 0) + { + return min; + } + return value; + } + + protected override void SetValidSpinDirection() + { + var validDirection = ValidSpinDirections.None; + if (!IsReadOnly) + { + if (Value is null) + { + validDirection = ValidSpinDirections.Increase | ValidSpinDirections.Decrease; + } + if (Value.HasValue && Value.Value.CompareTo(Maximum) < 0) + { + validDirection |= ValidSpinDirections.Increase; + } + + if (Value.HasValue && Value.Value.CompareTo(Minimum) > 0) + { + validDirection |= ValidSpinDirections.Decrease; + } + } + if (_spinner != null) + { + _spinner.ValidSpinDirection = validDirection; + } + } + + private bool _isSyncingTextAndValue; + + protected override bool SyncTextAndValue(bool fromTextToValue = false, string? text = null, bool forceTextUpdate = false) + { + if (_isSyncingTextAndValue) return true; + _isSyncingTextAndValue = true; + var parsedTextIsValid = true; + try + { + if (fromTextToValue) + { + try + { + var newValue = ConvertTextToValue(text); + if (EmptyInputValue is not null && newValue is null) + { + newValue = EmptyInputValue; + } + if (!Equals(newValue, Value)) + { + SetCurrentValue(ValueProperty, newValue); + } + } + catch + { + parsedTextIsValid = false; + } + } + + if (!_updateFromTextInput) + { + if (forceTextUpdate) + { + var newText = ConvertValueToText(Value); + if (_textBox != null && !Equals(_textBox.Text, newText)) + { + _textBox.Text = newText; + _textBox.CaretIndex = newText?.Length ?? 0; + } + } + } + + if (_updateFromTextInput && !parsedTextIsValid) + { + if (_spinner is not null) + { + _spinner.ValidSpinDirection = ValidSpinDirections.None; + } + } + else + { + SetValidSpinDirection(); + } + } + finally + { + _isSyncingTextAndValue = false; + } + return parsedTextIsValid; + } + + protected virtual T? ConvertTextToValue(string? text) + { + T? result; + if (string.IsNullOrWhiteSpace(text)) return null; + if (TextConverter != null) + { + var valueFromText = TextConverter.Convert(text, typeof(T?), null, CultureInfo.CurrentCulture); + return (T?)valueFromText; + } + else + { + if (!ParseText(text, out var outputValue)) + { + throw new InvalidDataException("Input string was not in a correct format."); + } + + result = outputValue; + } + return result; + } + + protected virtual string? ConvertValueToText(T? value) + { + if (TextConverter is not null) + { + return TextConverter.ConvertBack(Value, typeof(int), null, CultureInfo.CurrentCulture)?.ToString(); + } + + if (FormatString.Contains("{0")) + { + return string.Format(NumberFormat, FormatString, value); + } + + return ValueToString(Value); + } + + protected override void Increase() + { + T? value; + if (Value is not null) + { + value = Add(Value.Value, Step); + } + else + { + value = IsSet(MinimumProperty) ? Minimum : Zero; + } + SetCurrentValue(ValueProperty, Clamp(value, Maximum, Minimum)); + } + + protected override void Decrease() + { + T? value; + if (Value is not null) + { + value = Minus(Value.Value, Step); + } + else + { + value = IsSet(MaximumProperty) ? Maximum : Zero; + } + + SetCurrentValue(ValueProperty, Clamp(value, Maximum, Minimum)); + } + + protected abstract bool ParseText(string? text, out T number); + protected abstract string? ValueToString(T? value); + protected abstract T Zero { get; } + protected abstract T? Add(T? a, T? b); + protected abstract T? Minus(T? a, T? b); + + public override void Clear() + { + SetCurrentValue(ValueProperty, EmptyInputValue); + SyncTextAndValue(false, forceTextUpdate: true); + } } \ No newline at end of file From ac2c563cd28e28bead666d5fbac3a1dae5cf8b1a Mon Sep 17 00:00:00 2001 From: heartacker Date: Thu, 21 Mar 2024 14:42:02 +0800 Subject: [PATCH 07/13] TrimString to support 1. _ 2. 0x 3. h 4. h' TODO 1. bin/oct --- .../NumericUpDown/NumericUpDownBase.cs | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/src/Ursa/Controls/NumericUpDown/NumericUpDownBase.cs b/src/Ursa/Controls/NumericUpDown/NumericUpDownBase.cs index eb53464..673d5c5 100644 --- a/src/Ursa/Controls/NumericUpDown/NumericUpDownBase.cs +++ b/src/Ursa/Controls/NumericUpDown/NumericUpDownBase.cs @@ -318,6 +318,34 @@ public abstract class NumericUpDown : TemplatedControl, IClearControl public abstract class NumericUpDownBase : NumericUpDown where T : struct, IComparable { + protected static string TrimString(string? text, NumberStyles numberStyles) + { + text = text!.Trim(); + if (text.Contains("_")) // support _ like 0x1024_1024(hex), 10_24 (normal) + { + text = text.Replace("_", ""); + } + + if ((numberStyles & NumberStyles.AllowHexSpecifier) != 0) + { + + if (text.StartsWith("0X") || text.StartsWith("0x")) // support 0x hex while user input + { + text = text.Substring(2); + } + else if (text.StartsWith("h") || text.StartsWith("H")) // support hex while user input + { + text = text.Substring(1); + } + else if (text.StartsWith("h'") || text.StartsWith("H'")) // support hex while user input + { + text = text.Substring(2); + } + } + + return text; + } + public static readonly StyledProperty ValueProperty = AvaloniaProperty.Register, T?>( nameof(Value), defaultBindingMode: BindingMode.TwoWay); @@ -577,6 +605,7 @@ public abstract class NumericUpDownBase : NumericUpDown where T : struct, ICo } else { + text = TrimString(text, ParsingNumberStyle); if (!ParseText(text, out var outputValue)) { throw new InvalidDataException("Input string was not in a correct format."); From c3ed31659b4aa07ad5b4709f230c559ac2c99f5b Mon Sep 17 00:00:00 2001 From: heartacker Date: Thu, 21 Mar 2024 15:19:30 +0800 Subject: [PATCH 08/13] fix #172 show not drag to add/cut while clamp --- .../NumericUpDown/NumericUpDownBase.cs | 62 +++++++++++-------- 1 file changed, 37 insertions(+), 25 deletions(-) diff --git a/src/Ursa/Controls/NumericUpDown/NumericUpDownBase.cs b/src/Ursa/Controls/NumericUpDown/NumericUpDownBase.cs index 673d5c5..ec4aeff 100644 --- a/src/Ursa/Controls/NumericUpDown/NumericUpDownBase.cs +++ b/src/Ursa/Controls/NumericUpDown/NumericUpDownBase.cs @@ -30,6 +30,12 @@ public abstract class NumericUpDown : TemplatedControl, IClearControl private Point? _point; protected internal bool _updateFromTextInput; + + protected internal bool _canIncrease = true; + + protected internal bool _canDecrease = true; + + public static readonly StyledProperty AllowDragProperty = AvaloniaProperty.Register( nameof(AllowDrag), defaultValue: false); @@ -256,11 +262,13 @@ public abstract class NumericUpDown : TemplatedControl, IClearControl int d = GetDelta(delta.Value); if (d > 0) { - Increase(); + if (_canIncrease) + Increase(); } else if (d < 0) { - Decrease(); + if (_canDecrease) + Decrease(); } _point = point; } @@ -332,7 +340,7 @@ public abstract class NumericUpDownBase : NumericUpDown where T : struct, ICo if (text.StartsWith("0X") || text.StartsWith("0x")) // support 0x hex while user input { text = text.Substring(2); - } + } else if (text.StartsWith("h") || text.StartsWith("H")) // support hex while user input { text = text.Substring(1); @@ -511,6 +519,8 @@ public abstract class NumericUpDownBase : NumericUpDown where T : struct, ICo protected override void SetValidSpinDirection() { var validDirection = ValidSpinDirections.None; + _canIncrease = false; + _canDecrease = false; if (!IsReadOnly) { if (Value is null) @@ -520,11 +530,13 @@ public abstract class NumericUpDownBase : NumericUpDown where T : struct, ICo if (Value.HasValue && Value.Value.CompareTo(Maximum) < 0) { validDirection |= ValidSpinDirections.Increase; + _canIncrease = true; } if (Value.HasValue && Value.Value.CompareTo(Minimum) > 0) { validDirection |= ValidSpinDirections.Decrease; + _canDecrease = true; } } if (_spinner != null) @@ -553,28 +565,28 @@ public abstract class NumericUpDownBase : NumericUpDown where T : struct, ICo } if (!Equals(newValue, Value)) { - SetCurrentValue(ValueProperty, newValue); - } - } - catch - { - parsedTextIsValid = false; - } - } - - if (!_updateFromTextInput) - { - if (forceTextUpdate) - { - var newText = ConvertValueToText(Value); - if (_textBox != null && !Equals(_textBox.Text, newText)) - { - _textBox.Text = newText; - _textBox.CaretIndex = newText?.Length ?? 0; - } - } - } - + SetCurrentValue(ValueProperty, newValue); + } + } + catch + { + parsedTextIsValid = false; + } + } + + if (!_updateFromTextInput) + { + if (forceTextUpdate) + { + var newText = ConvertValueToText(Value); + if (_textBox != null && !Equals(_textBox.Text, newText)) + { + _textBox.Text = newText; + _textBox.CaretIndex = newText?.Length ?? 0; + } + } + } + if (_updateFromTextInput && !parsedTextIsValid) { if (_spinner is not null) From f41d2993380e9f7719b1f93dca4eec5444441a41 Mon Sep 17 00:00:00 2001 From: heartacker Date: Thu, 21 Mar 2024 15:30:53 +0800 Subject: [PATCH 09/13] should not SyncTextAndValue while fromTextToValue and clamp --- src/Ursa/Controls/NumericUpDown/NumericUpDownBase.cs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/Ursa/Controls/NumericUpDown/NumericUpDownBase.cs b/src/Ursa/Controls/NumericUpDown/NumericUpDownBase.cs index ec4aeff..c4c96e3 100644 --- a/src/Ursa/Controls/NumericUpDown/NumericUpDownBase.cs +++ b/src/Ursa/Controls/NumericUpDown/NumericUpDownBase.cs @@ -565,7 +565,14 @@ public abstract class NumericUpDownBase : NumericUpDown where T : struct, ICo } if (!Equals(newValue, Value)) { - SetCurrentValue(ValueProperty, newValue); + if (Equals(Clamp(newValue, Maximum, Minimum), newValue)) + { + SetCurrentValue(ValueProperty, newValue); + } + else + { + parsedTextIsValid = false; + } } } catch From b22c712cc3a4f643a1c2670c0f3680981d527a32 Mon Sep 17 00:00:00 2001 From: heartacker Date: Thu, 21 Mar 2024 15:31:13 +0800 Subject: [PATCH 10/13] revert BG --- src/Ursa.Themes.Semi/Controls/NumericUpDown.axaml | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Ursa.Themes.Semi/Controls/NumericUpDown.axaml b/src/Ursa.Themes.Semi/Controls/NumericUpDown.axaml index f9533e5..d15bc7c 100644 --- a/src/Ursa.Themes.Semi/Controls/NumericUpDown.axaml +++ b/src/Ursa.Themes.Semi/Controls/NumericUpDown.axaml @@ -47,7 +47,6 @@ MinHeight="{DynamicResource NumericUpDownWrapperDefaultHeight}" HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}" AcceptsReturn="False" - Background="Transparent" CornerRadius="{TemplateBinding CornerRadius}" DataValidationErrors.Errors="{ReflectionBinding $parent[NumericUpDown].(DataValidationErrors.Errors)}" FontSize="{TemplateBinding FontSize}" From 569e5c104c4f1be760d20d3877ac6b85fac45f0b Mon Sep 17 00:00:00 2001 From: heartacker Date: Thu, 21 Mar 2024 15:48:16 +0800 Subject: [PATCH 11/13] new demo --- demo/Ursa.Demo/Pages/NumericUpDownDemo.axaml | 193 +++++++++--------- .../ViewModels/NumericUpDownDemoViewModel.cs | 2 +- 2 files changed, 96 insertions(+), 99 deletions(-) diff --git a/demo/Ursa.Demo/Pages/NumericUpDownDemo.axaml b/demo/Ursa.Demo/Pages/NumericUpDownDemo.axaml index 14336ac..753a232 100644 --- a/demo/Ursa.Demo/Pages/NumericUpDownDemo.axaml +++ b/demo/Ursa.Demo/Pages/NumericUpDownDemo.axaml @@ -16,90 +16,107 @@ - - - - - - - - - - - + + + + + + + - + + + + + + + + + - - - + - - + + + - - - - + + + + + + + + - - - - diff --git a/demo/Ursa.Demo/ViewModels/NumericUpDownDemoViewModel.cs b/demo/Ursa.Demo/ViewModels/NumericUpDownDemoViewModel.cs index cd229ae..c257b00 100644 --- a/demo/Ursa.Demo/ViewModels/NumericUpDownDemoViewModel.cs +++ b/demo/Ursa.Demo/ViewModels/NumericUpDownDemoViewModel.cs @@ -20,7 +20,7 @@ public partial class NumericUpDownDemoViewModel : ObservableObject [ObservableProperty] private HorizontalAlignment _HorizontalContentAlignment = HorizontalAlignment.Center; [ObservableProperty] private object? _InnerLeftContent = "obj:0x"; [ObservableProperty] private string _Watermark = "Water mark showed"; - [ObservableProperty] private string _FormatString = "x8"; + [ObservableProperty] private string _FormatString = "X8"; [ObservableProperty] private Array _Array_ParsingNumberStyle; [ObservableProperty] private NumberStyles _ParsingNumberStyle = NumberStyles.AllowHexSpecifier; [ObservableProperty] private bool _AllowSpin = true; From a183d420dee3a171d3bab290be00a2ed4d270f73 Mon Sep 17 00:00:00 2001 From: heartacker Date: Thu, 21 Mar 2024 15:53:50 +0800 Subject: [PATCH 12/13] polish --- demo/Ursa.Demo/ViewModels/NumericUpDownDemoViewModel.cs | 2 +- src/Ursa/Controls/NumericUpDown/NumericUpDownBase.cs | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/demo/Ursa.Demo/ViewModels/NumericUpDownDemoViewModel.cs b/demo/Ursa.Demo/ViewModels/NumericUpDownDemoViewModel.cs index c257b00..9cf7651 100644 --- a/demo/Ursa.Demo/ViewModels/NumericUpDownDemoViewModel.cs +++ b/demo/Ursa.Demo/ViewModels/NumericUpDownDemoViewModel.cs @@ -39,7 +39,7 @@ public partial class NumericUpDownDemoViewModel : ObservableObject Array_ParsingNumberStyle = Enum.GetValues(typeof(NumberStyles)); NumericUIntUpDown numericUIntUpDown; TextBox textBox; - + } partial void OnValueChanging(uint oldValue, uint newValue) diff --git a/src/Ursa/Controls/NumericUpDown/NumericUpDownBase.cs b/src/Ursa/Controls/NumericUpDown/NumericUpDownBase.cs index c4c96e3..5a3af7a 100644 --- a/src/Ursa/Controls/NumericUpDown/NumericUpDownBase.cs +++ b/src/Ursa/Controls/NumericUpDown/NumericUpDownBase.cs @@ -336,7 +336,6 @@ public abstract class NumericUpDownBase : NumericUpDown where T : struct, ICo if ((numberStyles & NumberStyles.AllowHexSpecifier) != 0) { - if (text.StartsWith("0X") || text.StartsWith("0x")) // support 0x hex while user input { text = text.Substring(2); From 4b9bcd48b040ae0cc10afb973aac510bf42e3346 Mon Sep 17 00:00:00 2001 From: heartacker Date: Thu, 21 Mar 2024 15:55:05 +0800 Subject: [PATCH 13/13] update all to net 8? --- demo/Ursa.Demo.Android/Ursa.Demo.Android.csproj | 2 +- demo/Ursa.Demo.Browser/Ursa.Demo.Browser.csproj | 2 +- demo/Ursa.Demo.iOS/Ursa.Demo.iOS.csproj | 2 +- demo/Ursa.Demo/Ursa.Demo.csproj | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/demo/Ursa.Demo.Android/Ursa.Demo.Android.csproj b/demo/Ursa.Demo.Android/Ursa.Demo.Android.csproj index 4071d05..f5730c1 100644 --- a/demo/Ursa.Demo.Android/Ursa.Demo.Android.csproj +++ b/demo/Ursa.Demo.Android/Ursa.Demo.Android.csproj @@ -1,7 +1,7 @@ Exe - net7.0-android + net8.0-android 21 enable com.irihitech.Ursa diff --git a/demo/Ursa.Demo.Browser/Ursa.Demo.Browser.csproj b/demo/Ursa.Demo.Browser/Ursa.Demo.Browser.csproj index f51b5fb..31ef39b 100644 --- a/demo/Ursa.Demo.Browser/Ursa.Demo.Browser.csproj +++ b/demo/Ursa.Demo.Browser/Ursa.Demo.Browser.csproj @@ -1,6 +1,6 @@  - net7.0 + net8.0 browser-wasm AppBundle\main.js Exe diff --git a/demo/Ursa.Demo.iOS/Ursa.Demo.iOS.csproj b/demo/Ursa.Demo.iOS/Ursa.Demo.iOS.csproj index 5f70ccb..65fad79 100644 --- a/demo/Ursa.Demo.iOS/Ursa.Demo.iOS.csproj +++ b/demo/Ursa.Demo.iOS/Ursa.Demo.iOS.csproj @@ -1,7 +1,7 @@  Exe - net6.0-ios + net8.0-ios 13.0 enable diff --git a/demo/Ursa.Demo/Ursa.Demo.csproj b/demo/Ursa.Demo/Ursa.Demo.csproj index 7227b71..e4926a7 100644 --- a/demo/Ursa.Demo/Ursa.Demo.csproj +++ b/demo/Ursa.Demo/Ursa.Demo.csproj @@ -1,4 +1,4 @@ - + net8.0 enable