Browse Source

Merge pull request #5981 from puppetsw/NumericUpDown_Fix_5318

NumericUpDown control changes
pull/6245/head
Max Katz 5 years ago
committed by GitHub
parent
commit
0749f9a34f
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 43
      samples/ControlCatalog/Pages/NumericUpDownPage.xaml
  2. 15
      samples/ControlCatalog/Pages/NumericUpDownPage.xaml.cs
  3. 32
      src/Avalonia.Base/Utilities/MathUtilities.cs
  4. 30
      src/Avalonia.Controls/ApiCompatBaseline.txt
  5. 148
      src/Avalonia.Controls/NumericUpDown/NumericUpDown.cs
  6. 6
      src/Avalonia.Controls/NumericUpDown/NumericUpDownValueChangedEventArgs.cs

43
samples/ControlCatalog/Pages/NumericUpDownPage.xaml

@ -2,7 +2,8 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sys="clr-namespace:System;assembly=netstandard" xmlns:sys="clr-namespace:System;assembly=netstandard"
x:Class="ControlCatalog.Pages.NumericUpDownPage"> x:Class="ControlCatalog.Pages.NumericUpDownPage">
<StackPanel Orientation="Vertical" Spacing="4"> <StackPanel Orientation="Vertical" Spacing="4"
MaxWidth="800">
<TextBlock Margin="2" Classes="h1">Numeric up-down control</TextBlock> <TextBlock Margin="2" Classes="h1">Numeric up-down control</TextBlock>
<TextBlock Margin="2" Classes="h2" TextWrapping="Wrap">Numeric up-down control provides a TextBox with button spinners that allow incrementing and decrementing numeric values by using the spinner buttons, keyboard up/down arrows, or mouse wheel.</TextBlock> <TextBlock Margin="2" Classes="h2" TextWrapping="Wrap">Numeric up-down control provides a TextBox with button spinners that allow incrementing and decrementing numeric values by using the spinner buttons, keyboard up/down arrows, or mouse wheel.</TextBlock>
@ -68,24 +69,36 @@
<NumericUpDown Grid.Row="3" Grid.Column="1" Value="{Binding #upDown.Value}" VerticalAlignment="Center" <NumericUpDown Grid.Row="3" Grid.Column="1" Value="{Binding #upDown.Value}" VerticalAlignment="Center"
Margin="2" HorizontalAlignment="Center"/> Margin="2" HorizontalAlignment="Center"/>
</Grid> </Grid>
</Grid> </Grid>
<StackPanel Margin="2,10,2,2" Orientation="Horizontal" Spacing="10"> <WrapPanel Margin="2,10,2,2">
<TextBlock FontSize="14" FontWeight="Bold" VerticalAlignment="Center">Usage of NumericUpDown:</TextBlock> <StackPanel Orientation="Vertical" Margin="10">
<NumericUpDown Name="upDown" Minimum="0" Maximum="10" Increment="0.5" <Label Target="upDown" FontSize="14" FontWeight="Bold" VerticalAlignment="Center">Usage of decimal NumericUpDown:</Label>
CultureInfo="en-US" VerticalAlignment="Center" <NumericUpDown Name="upDown" Minimum="0" Maximum="10" Increment="0.5"
Watermark="Enter text" FormatString="{Binding SelectedFormat.Value}"/> CultureInfo="en-US" VerticalAlignment="Center" Value="{Binding DecimalValue}"
Watermark="Enter text" FormatString="{Binding SelectedFormat.Value}"/>
</StackPanel>
<StackPanel Orientation="Vertical" Margin="10">
<Label Target="DoubleUpDown" FontSize="14" FontWeight="Bold" VerticalAlignment="Center">Usage of double NumericUpDown:</Label>
<NumericUpDown Name="DoubleUpDown" Minimum="0" Maximum="10" Increment="0.5"
CultureInfo="en-US" VerticalAlignment="Center" Value="{Binding DoubleValue}"
Watermark="Enter text" FormatString="{Binding SelectedFormat.Value}"/>
</StackPanel>
<TextBlock FontSize="14" FontWeight="Bold" VerticalAlignment="Center">NumericUpDown with Validation Errors:</TextBlock> <StackPanel Orientation="Vertical" Margin="10">
<NumericUpDown Minimum="0" Maximum="10" Increment="0.5" <Label Target="ValidationUpDown" FontSize="14" FontWeight="Bold" VerticalAlignment="Center">NumericUpDown with Validation Errors:</Label>
CultureInfo="en-US" VerticalAlignment="Center" <NumericUpDown x:Name="ValidationUpDown" Minimum="0" Maximum="10" Increment="0.5"
Watermark="Enter text" FormatString="{Binding SelectedFormat.Value}"> CultureInfo="en-US" VerticalAlignment="Center"
<DataValidationErrors.Error> Watermark="Enter text" FormatString="{Binding SelectedFormat.Value}">
<sys:Exception /> <DataValidationErrors.Error>
</DataValidationErrors.Error> <sys:Exception />
</NumericUpDown> </DataValidationErrors.Error>
</StackPanel> </NumericUpDown>
</StackPanel>
</WrapPanel>
</StackPanel> </StackPanel>
</UserControl> </UserControl>

15
samples/ControlCatalog/Pages/NumericUpDownPage.xaml.cs

@ -32,11 +32,26 @@ namespace ControlCatalog.Pages
private FormatObject _selectedFormat; private FormatObject _selectedFormat;
private IList<Location> _spinnerLocations; private IList<Location> _spinnerLocations;
private double _doubleValue;
private decimal _decimalValue;
public NumbersPageViewModel() public NumbersPageViewModel()
{ {
SelectedFormat = Formats.FirstOrDefault(); SelectedFormat = Formats.FirstOrDefault();
} }
public double DoubleValue
{
get { return _doubleValue; }
set { this.RaiseAndSetIfChanged(ref _doubleValue, value); }
}
public decimal DecimalValue
{
get { return _decimalValue; }
set { this.RaiseAndSetIfChanged(ref _decimalValue, value); }
}
public IList<FormatObject> Formats public IList<FormatObject> Formats
{ {
get get

32
src/Avalonia.Base/Utilities/MathUtilities.cs

@ -224,6 +224,34 @@ namespace Avalonia.Utilities
} }
} }
/// <summary>
/// Clamps a value between a minimum and maximum value.
/// </summary>
/// <param name="val">The value.</param>
/// <param name="min">The minimum value.</param>
/// <param name="max">The maximum value.</param>
/// <returns>The clamped value.</returns>
public static decimal Clamp(decimal val, decimal min, decimal max)
{
if (min > max)
{
ThrowCannotBeGreaterThanException(min, max);
}
if (val < min)
{
return min;
}
else if (val > max)
{
return max;
}
else
{
return val;
}
}
/// <summary> /// <summary>
/// Clamps a value between a minimum and maximum value. /// Clamps a value between a minimum and maximum value.
/// </summary> /// </summary>
@ -281,8 +309,8 @@ namespace Avalonia.Utilities
{ {
return angle * 2 * Math.PI; return angle * 2 * Math.PI;
} }
private static void ThrowCannotBeGreaterThanException(double min, double max) private static void ThrowCannotBeGreaterThanException<T>(T min, T max)
{ {
throw new ArgumentException($"{min} cannot be greater than {max}."); throw new ArgumentException($"{min} cannot be greater than {max}.");
} }

30
src/Avalonia.Controls/ApiCompatBaseline.txt

@ -4,14 +4,38 @@ InterfacesShouldHaveSameMembers : Interface member 'public System.Boolean Avalon
InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Controls.IMenuItem.StaysOpenOnClick.set(System.Boolean)' is present in the implementation but not in the contract. InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Controls.IMenuItem.StaysOpenOnClick.set(System.Boolean)' is present in the implementation but not in the contract.
InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Controls.INativeMenuExporterEventsImplBridge.RaiseClosed()' is present in the implementation but not in the contract. InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Controls.INativeMenuExporterEventsImplBridge.RaiseClosed()' is present in the implementation but not in the contract.
InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Controls.INativeMenuExporterEventsImplBridge.RaiseOpening()' is present in the implementation but not in the contract. InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Controls.INativeMenuExporterEventsImplBridge.RaiseOpening()' is present in the implementation but not in the contract.
MembersMustExist : Member 'public Avalonia.DirectProperty<Avalonia.Controls.NumericUpDown, System.Double> Avalonia.DirectProperty<Avalonia.Controls.NumericUpDown, System.Double> Avalonia.Controls.NumericUpDown.ValueProperty' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'public Avalonia.StyledProperty<System.Double> Avalonia.StyledProperty<System.Double> Avalonia.Controls.NumericUpDown.IncrementProperty' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'public Avalonia.StyledProperty<System.Double> Avalonia.StyledProperty<System.Double> Avalonia.Controls.NumericUpDown.MaximumProperty' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'public Avalonia.StyledProperty<System.Double> Avalonia.StyledProperty<System.Double> Avalonia.Controls.NumericUpDown.MinimumProperty' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'public System.Double Avalonia.Controls.NumericUpDown.Increment.get()' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'public void Avalonia.Controls.NumericUpDown.Increment.set(System.Double)' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'public System.Double Avalonia.Controls.NumericUpDown.Maximum.get()' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'public void Avalonia.Controls.NumericUpDown.Maximum.set(System.Double)' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'public System.Double Avalonia.Controls.NumericUpDown.Minimum.get()' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'public void Avalonia.Controls.NumericUpDown.Minimum.set(System.Double)' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'protected System.Double Avalonia.Controls.NumericUpDown.OnCoerceIncrement(System.Double)' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'protected System.Double Avalonia.Controls.NumericUpDown.OnCoerceMaximum(System.Double)' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'protected System.Double Avalonia.Controls.NumericUpDown.OnCoerceMinimum(System.Double)' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'protected System.Double Avalonia.Controls.NumericUpDown.OnCoerceValue(System.Double)' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'protected void Avalonia.Controls.NumericUpDown.OnIncrementChanged(System.Double, System.Double)' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'protected void Avalonia.Controls.NumericUpDown.OnMaximumChanged(System.Double, System.Double)' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'protected void Avalonia.Controls.NumericUpDown.OnMinimumChanged(System.Double, System.Double)' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'protected void Avalonia.Controls.NumericUpDown.OnValueChanged(System.Double, System.Double)' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'protected void Avalonia.Controls.NumericUpDown.RaiseValueChangedEvent(System.Double, System.Double)' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'public System.Double Avalonia.Controls.NumericUpDown.Value.get()' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'public void Avalonia.Controls.NumericUpDown.Value.set(System.Double)' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'public void Avalonia.Controls.NumericUpDownValueChangedEventArgs..ctor(Avalonia.Interactivity.RoutedEvent, System.Double, System.Double)' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'public System.Double Avalonia.Controls.NumericUpDownValueChangedEventArgs.NewValue.get()' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'public System.Double Avalonia.Controls.NumericUpDownValueChangedEventArgs.OldValue.get()' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'public Avalonia.StyledProperty<System.Boolean> Avalonia.StyledProperty<System.Boolean> Avalonia.Controls.ScrollViewer.AllowAutoHideProperty' does not exist in the implementation but it does exist in the contract. MembersMustExist : Member 'public Avalonia.StyledProperty<System.Boolean> Avalonia.StyledProperty<System.Boolean> Avalonia.Controls.ScrollViewer.AllowAutoHideProperty' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'public Avalonia.AvaloniaProperty<Avalonia.Media.Stretch> Avalonia.AvaloniaProperty<Avalonia.Media.Stretch> Avalonia.Controls.Viewbox.StretchProperty' does not exist in the implementation but it does exist in the contract. MembersMustExist : Member 'public Avalonia.AvaloniaProperty<Avalonia.Media.Stretch> Avalonia.AvaloniaProperty<Avalonia.Media.Stretch> Avalonia.Controls.Viewbox.StretchProperty' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'public void Avalonia.Controls.Embedding.Offscreen.OffscreenTopLevelImplBase.SetCursor(Avalonia.Platform.IPlatformHandle)' does not exist in the implementation but it does exist in the contract. MembersMustExist : Member 'public void Avalonia.Controls.Embedding.Offscreen.OffscreenTopLevelImplBase.SetCursor(Avalonia.Platform.IPlatformHandle)' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'public Avalonia.AvaloniaProperty Avalonia.AvaloniaProperty Avalonia.Controls.Notifications.NotificationCard.CloseOnClickProperty' does not exist in the implementation but it does exist in the contract. MembersMustExist : Member 'public Avalonia.AvaloniaProperty Avalonia.AvaloniaProperty Avalonia.Controls.Notifications.NotificationCard.CloseOnClickProperty' does not exist in the implementation but it does exist in the contract.
EnumValuesMustMatch : Enum value 'Avalonia.Platform.ExtendClientAreaChromeHints Avalonia.Platform.ExtendClientAreaChromeHints.Default' is (System.Int32)2 in the implementation but (System.Int32)1 in the contract. EnumValuesMustMatch : Enum value 'Avalonia.Platform.ExtendClientAreaChromeHints Avalonia.Platform.ExtendClientAreaChromeHints.Default' is (System.Int32)2 in the implementation but (System.Int32)1 in the contract.
InterfacesShouldHaveSameMembers : Interface member 'public System.Nullable<Avalonia.Size> Avalonia.Platform.ITopLevelImpl.FrameSize' is present in the implementation but not in the contract.
InterfacesShouldHaveSameMembers : Interface member 'public System.Nullable<Avalonia.Size> Avalonia.Platform.ITopLevelImpl.FrameSize.get()' is present in the implementation but not in the contract.
InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Platform.ITopLevelImpl.SetCursor(Avalonia.Platform.ICursorImpl)' is present in the implementation but not in the contract. InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Platform.ITopLevelImpl.SetCursor(Avalonia.Platform.ICursorImpl)' is present in the implementation but not in the contract.
InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Platform.ITopLevelImpl.SetCursor(Avalonia.Platform.IPlatformHandle)' is present in the contract but not in the implementation. InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Platform.ITopLevelImpl.SetCursor(Avalonia.Platform.IPlatformHandle)' is present in the contract but not in the implementation.
MembersMustExist : Member 'public void Avalonia.Platform.ITopLevelImpl.SetCursor(Avalonia.Platform.IPlatformHandle)' does not exist in the implementation but it does exist in the contract. MembersMustExist : Member 'public void Avalonia.Platform.ITopLevelImpl.SetCursor(Avalonia.Platform.IPlatformHandle)' does not exist in the implementation but it does exist in the contract.
InterfacesShouldHaveSameMembers : Interface member 'public System.Nullable<Avalonia.Size> Avalonia.Platform.ITopLevelImpl.FrameSize' is present in the implementation but not in the contract. Total Issues: 39
InterfacesShouldHaveSameMembers : Interface member 'public System.Nullable<Avalonia.Size> Avalonia.Platform.ITopLevelImpl.FrameSize.get()' is present in the implementation but not in the contract.
Total Issues: 15

148
src/Avalonia.Controls/NumericUpDown/NumericUpDown.cs

@ -45,10 +45,18 @@ namespace Avalonia.Controls
/// <summary> /// <summary>
/// Defines the <see cref="CultureInfo"/> property. /// Defines the <see cref="CultureInfo"/> property.
/// </summary> /// </summary>
[Obsolete]
public static readonly DirectProperty<NumericUpDown, CultureInfo> CultureInfoProperty = public static readonly DirectProperty<NumericUpDown, CultureInfo> CultureInfoProperty =
AvaloniaProperty.RegisterDirect<NumericUpDown, CultureInfo>(nameof(CultureInfo), o => o.CultureInfo, AvaloniaProperty.RegisterDirect<NumericUpDown, CultureInfo>(nameof(CultureInfo), o => o.CultureInfo,
(o, v) => o.CultureInfo = v, CultureInfo.CurrentCulture); (o, v) => o.CultureInfo = v, CultureInfo.CurrentCulture);
/// <summary>
/// Defines the <see cref="NumberFormat"/> property.
/// </summary>
public static readonly DirectProperty<NumericUpDown, NumberFormatInfo> NumberFormatProperty =
AvaloniaProperty.RegisterDirect<NumericUpDown, NumberFormatInfo>(nameof(NumberFormat), o => o.NumberFormat,
(o, v) => o.NumberFormat = v, NumberFormatInfo.CurrentInfo);
/// <summary> /// <summary>
/// Defines the <see cref="FormatString"/> property. /// Defines the <see cref="FormatString"/> property.
/// </summary> /// </summary>
@ -58,8 +66,8 @@ namespace Avalonia.Controls
/// <summary> /// <summary>
/// Defines the <see cref="Increment"/> property. /// Defines the <see cref="Increment"/> property.
/// </summary> /// </summary>
public static readonly StyledProperty<double> IncrementProperty = public static readonly StyledProperty<decimal> IncrementProperty =
AvaloniaProperty.Register<NumericUpDown, double>(nameof(Increment), 1.0d, coerce: OnCoerceIncrement); AvaloniaProperty.Register<NumericUpDown, decimal>(nameof(Increment), 1.0m, coerce: OnCoerceIncrement);
/// <summary> /// <summary>
/// Defines the <see cref="IsReadOnly"/> property. /// Defines the <see cref="IsReadOnly"/> property.
@ -70,14 +78,14 @@ namespace Avalonia.Controls
/// <summary> /// <summary>
/// Defines the <see cref="Maximum"/> property. /// Defines the <see cref="Maximum"/> property.
/// </summary> /// </summary>
public static readonly StyledProperty<double> MaximumProperty = public static readonly StyledProperty<decimal> MaximumProperty =
AvaloniaProperty.Register<NumericUpDown, double>(nameof(Maximum), double.MaxValue, coerce: OnCoerceMaximum); AvaloniaProperty.Register<NumericUpDown, decimal>(nameof(Maximum), decimal.MaxValue, coerce: OnCoerceMaximum);
/// <summary> /// <summary>
/// Defines the <see cref="Minimum"/> property. /// Defines the <see cref="Minimum"/> property.
/// </summary> /// </summary>
public static readonly StyledProperty<double> MinimumProperty = public static readonly StyledProperty<decimal> MinimumProperty =
AvaloniaProperty.Register<NumericUpDown, double>(nameof(Minimum), double.MinValue, coerce: OnCoerceMinimum); AvaloniaProperty.Register<NumericUpDown, decimal>(nameof(Minimum), decimal.MinValue, coerce: OnCoerceMinimum);
/// <summary> /// <summary>
/// Defines the <see cref="ParsingNumberStyle"/> property. /// Defines the <see cref="ParsingNumberStyle"/> property.
@ -96,8 +104,8 @@ namespace Avalonia.Controls
/// <summary> /// <summary>
/// Defines the <see cref="Value"/> property. /// Defines the <see cref="Value"/> property.
/// </summary> /// </summary>
public static readonly DirectProperty<NumericUpDown, double> ValueProperty = public static readonly DirectProperty<NumericUpDown, decimal> ValueProperty =
AvaloniaProperty.RegisterDirect<NumericUpDown, double>(nameof(Value), updown => updown.Value, AvaloniaProperty.RegisterDirect<NumericUpDown, decimal>(nameof(Value), updown => updown.Value,
(updown, v) => updown.Value = v, defaultBindingMode: BindingMode.TwoWay, enableDataValidation: true); (updown, v) => updown.Value = v, defaultBindingMode: BindingMode.TwoWay, enableDataValidation: true);
/// <summary> /// <summary>
@ -106,7 +114,6 @@ namespace Avalonia.Controls
public static readonly StyledProperty<string> WatermarkProperty = public static readonly StyledProperty<string> WatermarkProperty =
AvaloniaProperty.Register<NumericUpDown, string>(nameof(Watermark)); AvaloniaProperty.Register<NumericUpDown, string>(nameof(Watermark));
/// <summary> /// <summary>
/// Defines the <see cref="HorizontalContentAlignment"/> property. /// Defines the <see cref="HorizontalContentAlignment"/> property.
/// </summary> /// </summary>
@ -121,7 +128,7 @@ namespace Avalonia.Controls
private IDisposable _textBoxTextChangedSubscription; private IDisposable _textBoxTextChangedSubscription;
private double _value; private decimal _value;
private string _text; private string _text;
private bool _internalValueSet; private bool _internalValueSet;
private bool _clipValueToMinMax; private bool _clipValueToMinMax;
@ -129,7 +136,8 @@ namespace Avalonia.Controls
private bool _isTextChangedFromUI; private bool _isTextChangedFromUI;
private CultureInfo _cultureInfo; private CultureInfo _cultureInfo;
private NumberStyles _parsingNumberStyle = NumberStyles.Any; private NumberStyles _parsingNumberStyle = NumberStyles.Any;
private NumberFormatInfo _numberFormat;
/// <summary> /// <summary>
/// Gets the Spinner template part. /// Gets the Spinner template part.
/// </summary> /// </summary>
@ -179,10 +187,25 @@ namespace Avalonia.Controls
/// <summary> /// <summary>
/// Gets or sets the current CultureInfo. /// Gets or sets the current CultureInfo.
/// </summary> /// </summary>
[Obsolete("CultureInfo is obsolete, please use NumberFormat instead.")]
public CultureInfo CultureInfo public CultureInfo CultureInfo
{ {
get { return _cultureInfo; } get { return _cultureInfo; }
set { SetAndRaise(CultureInfoProperty, ref _cultureInfo, value); } set
{
SetAndRaise(CultureInfoProperty, ref _cultureInfo, value);
//Set and Raise the NumberFormatProperty when CultureInfo is changed.
SetAndRaise(NumberFormatProperty, ref _numberFormat, value?.NumberFormat);
}
}
/// <summary>
/// Gets or sets the current NumberFormatInfo
/// </summary>
public NumberFormatInfo NumberFormat
{
get { return _numberFormat; }
set { SetAndRaise(NumberFormatProperty, ref _numberFormat, value); }
} }
/// <summary> /// <summary>
@ -197,7 +220,7 @@ namespace Avalonia.Controls
/// <summary> /// <summary>
/// Gets or sets the amount in which to increment the <see cref="Value"/>. /// Gets or sets the amount in which to increment the <see cref="Value"/>.
/// </summary> /// </summary>
public double Increment public decimal Increment
{ {
get { return GetValue(IncrementProperty); } get { return GetValue(IncrementProperty); }
set { SetValue(IncrementProperty, value); } set { SetValue(IncrementProperty, value); }
@ -215,7 +238,7 @@ namespace Avalonia.Controls
/// <summary> /// <summary>
/// Gets or sets the maximum allowed value. /// Gets or sets the maximum allowed value.
/// </summary> /// </summary>
public double Maximum public decimal Maximum
{ {
get { return GetValue(MaximumProperty); } get { return GetValue(MaximumProperty); }
set { SetValue(MaximumProperty, value); } set { SetValue(MaximumProperty, value); }
@ -224,7 +247,7 @@ namespace Avalonia.Controls
/// <summary> /// <summary>
/// Gets or sets the minimum allowed value. /// Gets or sets the minimum allowed value.
/// </summary> /// </summary>
public double Minimum public decimal Minimum
{ {
get { return GetValue(MinimumProperty); } get { return GetValue(MinimumProperty); }
set { SetValue(MinimumProperty, value); } set { SetValue(MinimumProperty, value); }
@ -251,7 +274,7 @@ namespace Avalonia.Controls
/// <summary> /// <summary>
/// Gets or sets the value. /// Gets or sets the value.
/// </summary> /// </summary>
public double Value public decimal Value
{ {
get { return _value; } get { return _value; }
set set
@ -270,7 +293,6 @@ namespace Avalonia.Controls
set { SetValue(WatermarkProperty, value); } set { SetValue(WatermarkProperty, value); }
} }
/// <summary> /// <summary>
/// Gets or sets the horizontal alignment of the content within the control. /// Gets or sets the horizontal alignment of the content within the control.
/// </summary> /// </summary>
@ -311,6 +333,7 @@ namespace Avalonia.Controls
static NumericUpDown() static NumericUpDown()
{ {
CultureInfoProperty.Changed.Subscribe(OnCultureInfoChanged); CultureInfoProperty.Changed.Subscribe(OnCultureInfoChanged);
NumberFormatProperty.Changed.Subscribe(OnNumberFormatChanged);
FormatStringProperty.Changed.Subscribe(FormatStringChanged); FormatStringProperty.Changed.Subscribe(FormatStringChanged);
IncrementProperty.Changed.Subscribe(IncrementChanged); IncrementProperty.Changed.Subscribe(IncrementChanged);
IsReadOnlyProperty.Changed.Subscribe(OnIsReadOnlyChanged); IsReadOnlyProperty.Changed.Subscribe(OnIsReadOnlyChanged);
@ -397,6 +420,19 @@ namespace Avalonia.Controls
} }
} }
/// <summary>
/// Called when the <see cref="NumberFormat"/> property value changed.
/// </summary>
/// <param name="oldValue">The old value.</param>
/// <param name="newValue">The new value.</param>
protected virtual void OnNumberFormatChanged(NumberFormatInfo oldValue, NumberFormatInfo newValue)
{
if (IsInitialized)
{
SyncTextAndValueProperties(false, null);
}
}
/// <summary> /// <summary>
/// Called when the <see cref="FormatString"/> property value changed. /// Called when the <see cref="FormatString"/> property value changed.
/// </summary> /// </summary>
@ -415,7 +451,7 @@ namespace Avalonia.Controls
/// </summary> /// </summary>
/// <param name="oldValue">The old value.</param> /// <param name="oldValue">The old value.</param>
/// <param name="newValue">The new value.</param> /// <param name="newValue">The new value.</param>
protected virtual void OnIncrementChanged(double oldValue, double newValue) protected virtual void OnIncrementChanged(decimal oldValue, decimal newValue)
{ {
if (IsInitialized) if (IsInitialized)
{ {
@ -438,7 +474,7 @@ namespace Avalonia.Controls
/// </summary> /// </summary>
/// <param name="oldValue">The old value.</param> /// <param name="oldValue">The old value.</param>
/// <param name="newValue">The new value.</param> /// <param name="newValue">The new value.</param>
protected virtual void OnMaximumChanged(double oldValue, double newValue) protected virtual void OnMaximumChanged(decimal oldValue, decimal newValue)
{ {
if (IsInitialized) if (IsInitialized)
{ {
@ -455,7 +491,7 @@ namespace Avalonia.Controls
/// </summary> /// </summary>
/// <param name="oldValue">The old value.</param> /// <param name="oldValue">The old value.</param>
/// <param name="newValue">The new value.</param> /// <param name="newValue">The new value.</param>
protected virtual void OnMinimumChanged(double oldValue, double newValue) protected virtual void OnMinimumChanged(decimal oldValue, decimal newValue)
{ {
if (IsInitialized) if (IsInitialized)
{ {
@ -485,7 +521,7 @@ namespace Avalonia.Controls
/// </summary> /// </summary>
/// <param name="oldValue">The old value.</param> /// <param name="oldValue">The old value.</param>
/// <param name="newValue">The new value.</param> /// <param name="newValue">The new value.</param>
protected virtual void OnValueChanged(double oldValue, double newValue) protected virtual void OnValueChanged(decimal oldValue, decimal newValue)
{ {
if (!_internalValueSet && IsInitialized) if (!_internalValueSet && IsInitialized)
{ {
@ -501,7 +537,7 @@ namespace Avalonia.Controls
/// Called when the <see cref="Increment"/> property has to be coerced. /// Called when the <see cref="Increment"/> property has to be coerced.
/// </summary> /// </summary>
/// <param name="baseValue">The value.</param> /// <param name="baseValue">The value.</param>
protected virtual double OnCoerceIncrement(double baseValue) protected virtual decimal OnCoerceIncrement(decimal baseValue)
{ {
return baseValue; return baseValue;
} }
@ -510,7 +546,7 @@ namespace Avalonia.Controls
/// Called when the <see cref="Maximum"/> property has to be coerced. /// Called when the <see cref="Maximum"/> property has to be coerced.
/// </summary> /// </summary>
/// <param name="baseValue">The value.</param> /// <param name="baseValue">The value.</param>
protected virtual double OnCoerceMaximum(double baseValue) protected virtual decimal OnCoerceMaximum(decimal baseValue)
{ {
return Math.Max(baseValue, Minimum); return Math.Max(baseValue, Minimum);
} }
@ -519,7 +555,7 @@ namespace Avalonia.Controls
/// Called when the <see cref="Minimum"/> property has to be coerced. /// Called when the <see cref="Minimum"/> property has to be coerced.
/// </summary> /// </summary>
/// <param name="baseValue">The value.</param> /// <param name="baseValue">The value.</param>
protected virtual double OnCoerceMinimum(double baseValue) protected virtual decimal OnCoerceMinimum(decimal baseValue)
{ {
return Math.Min(baseValue, Maximum); return Math.Min(baseValue, Maximum);
} }
@ -528,7 +564,7 @@ namespace Avalonia.Controls
/// Called when the <see cref="Value"/> property has to be coerced. /// Called when the <see cref="Value"/> property has to be coerced.
/// </summary> /// </summary>
/// <param name="baseValue">The value.</param> /// <param name="baseValue">The value.</param>
protected virtual double OnCoerceValue(double baseValue) protected virtual decimal OnCoerceValue(decimal baseValue)
{ {
return baseValue; return baseValue;
} }
@ -562,7 +598,7 @@ namespace Avalonia.Controls
/// </summary> /// </summary>
/// <param name="oldValue">The old value.</param> /// <param name="oldValue">The old value.</param>
/// <param name="newValue">The new value.</param> /// <param name="newValue">The new value.</param>
protected virtual void RaiseValueChangedEvent(double oldValue, double newValue) protected virtual void RaiseValueChangedEvent(decimal oldValue, decimal newValue)
{ {
var e = new NumericUpDownValueChangedEventArgs(ValueChangedEvent, oldValue, newValue); var e = new NumericUpDownValueChangedEventArgs(ValueChangedEvent, oldValue, newValue);
RaiseEvent(e); RaiseEvent(e);
@ -571,9 +607,9 @@ namespace Avalonia.Controls
/// <summary> /// <summary>
/// Converts the formatted text to a value. /// Converts the formatted text to a value.
/// </summary> /// </summary>
private double ConvertTextToValue(string text) private decimal ConvertTextToValue(string text)
{ {
double result = 0; decimal result = 0;
if (string.IsNullOrEmpty(text)) if (string.IsNullOrEmpty(text))
{ {
@ -609,10 +645,10 @@ namespace Avalonia.Controls
//Manage FormatString of type "{}{0:N2} °" (in xaml) or "{0:N2} °" in code-behind. //Manage FormatString of type "{}{0:N2} °" (in xaml) or "{0:N2} °" in code-behind.
if (FormatString.Contains("{0")) if (FormatString.Contains("{0"))
{ {
return string.Format(CultureInfo, FormatString, Value); return string.Format(NumberFormat, FormatString, Value);
} }
return Value.ToString(FormatString, CultureInfo); return Value.ToString(FormatString, NumberFormat);
} }
/// <summary> /// <summary>
@ -674,6 +710,20 @@ namespace Avalonia.Controls
} }
} }
/// <summary>
/// Called when the <see cref="NumberFormat"/> property value changed.
/// </summary>
/// <param name="e">The event args.</param>
private static void OnNumberFormatChanged(AvaloniaPropertyChangedEventArgs e)
{
if (e.Sender is NumericUpDown upDown)
{
var oldValue = (NumberFormatInfo)e.OldValue;
var newValue = (NumberFormatInfo)e.NewValue;
upDown.OnNumberFormatChanged(oldValue, newValue);
}
}
/// <summary> /// <summary>
/// Called when the <see cref="Increment"/> property value changed. /// Called when the <see cref="Increment"/> property value changed.
/// </summary> /// </summary>
@ -682,8 +732,8 @@ namespace Avalonia.Controls
{ {
if (e.Sender is NumericUpDown upDown) if (e.Sender is NumericUpDown upDown)
{ {
var oldValue = (double)e.OldValue; var oldValue = (decimal)e.OldValue;
var newValue = (double)e.NewValue; var newValue = (decimal)e.NewValue;
upDown.OnIncrementChanged(oldValue, newValue); upDown.OnIncrementChanged(oldValue, newValue);
} }
} }
@ -724,8 +774,8 @@ namespace Avalonia.Controls
{ {
if (e.Sender is NumericUpDown upDown) if (e.Sender is NumericUpDown upDown)
{ {
var oldValue = (double)e.OldValue; var oldValue = (decimal)e.OldValue;
var newValue = (double)e.NewValue; var newValue = (decimal)e.NewValue;
upDown.OnMaximumChanged(oldValue, newValue); upDown.OnMaximumChanged(oldValue, newValue);
} }
} }
@ -738,8 +788,8 @@ namespace Avalonia.Controls
{ {
if (e.Sender is NumericUpDown upDown) if (e.Sender is NumericUpDown upDown)
{ {
var oldValue = (double)e.OldValue; var oldValue = (decimal)e.OldValue;
var newValue = (double)e.NewValue; var newValue = (decimal)e.NewValue;
upDown.OnMinimumChanged(oldValue, newValue); upDown.OnMinimumChanged(oldValue, newValue);
} }
} }
@ -766,13 +816,13 @@ namespace Avalonia.Controls
{ {
if (e.Sender is NumericUpDown upDown) if (e.Sender is NumericUpDown upDown)
{ {
var oldValue = (double)e.OldValue; var oldValue = (decimal)e.OldValue;
var newValue = (double)e.NewValue; var newValue = (decimal)e.NewValue;
upDown.OnValueChanged(oldValue, newValue); upDown.OnValueChanged(oldValue, newValue);
} }
} }
private void SetValueInternal(double value) private void SetValueInternal(decimal value)
{ {
_internalValueSet = true; _internalValueSet = true;
try try
@ -785,7 +835,7 @@ namespace Avalonia.Controls
} }
} }
private static double OnCoerceMaximum(IAvaloniaObject instance, double value) private static decimal OnCoerceMaximum(IAvaloniaObject instance, decimal value)
{ {
if (instance is NumericUpDown upDown) if (instance is NumericUpDown upDown)
{ {
@ -795,7 +845,7 @@ namespace Avalonia.Controls
return value; return value;
} }
private static double OnCoerceMinimum(IAvaloniaObject instance, double value) private static decimal OnCoerceMinimum(IAvaloniaObject instance, decimal value)
{ {
if (instance is NumericUpDown upDown) if (instance is NumericUpDown upDown)
{ {
@ -805,7 +855,7 @@ namespace Avalonia.Controls
return value; return value;
} }
private static double OnCoerceIncrement(IAvaloniaObject instance, double value) private static decimal OnCoerceIncrement(IAvaloniaObject instance, decimal value)
{ {
if (instance is NumericUpDown upDown) if (instance is NumericUpDown upDown)
{ {
@ -977,23 +1027,23 @@ namespace Avalonia.Controls
return parsedTextIsValid; return parsedTextIsValid;
} }
private double ConvertTextToValueCore(string currentValueText, string text) private decimal ConvertTextToValueCore(string currentValueText, string text)
{ {
double result; decimal result;
if (IsPercent(FormatString)) if (IsPercent(FormatString))
{ {
result = decimal.ToDouble(ParsePercent(text, CultureInfo)); result = ParsePercent(text, NumberFormat);
} }
else else
{ {
// Problem while converting new text // Problem while converting new text
if (!double.TryParse(text, ParsingNumberStyle, CultureInfo, out var outputValue)) if (!decimal.TryParse(text, ParsingNumberStyle, NumberFormat, out var outputValue))
{ {
var shouldThrow = true; var shouldThrow = true;
// Check if CurrentValueText is also failing => it also contains special characters. ex : 90° // Check if CurrentValueText is also failing => it also contains special characters. ex : 90°
if (!double.TryParse(currentValueText, ParsingNumberStyle, CultureInfo, out var _)) if (!decimal.TryParse(currentValueText, ParsingNumberStyle, NumberFormat, out var _))
{ {
// extract non-digit characters // extract non-digit characters
var currentValueTextSpecialCharacters = currentValueText.Where(c => !char.IsDigit(c)); var currentValueTextSpecialCharacters = currentValueText.Where(c => !char.IsDigit(c));
@ -1006,7 +1056,7 @@ namespace Avalonia.Controls
text = text.Replace(character.ToString(), string.Empty); text = text.Replace(character.ToString(), string.Empty);
} }
// if without the special characters, parsing is good, do not throw // if without the special characters, parsing is good, do not throw
if (double.TryParse(text, ParsingNumberStyle, CultureInfo, out outputValue)) if (decimal.TryParse(text, ParsingNumberStyle, NumberFormat, out outputValue))
{ {
shouldThrow = false; shouldThrow = false;
} }
@ -1023,7 +1073,7 @@ namespace Avalonia.Controls
return result; return result;
} }
private void ValidateMinMax(double value) private void ValidateMinMax(decimal value)
{ {
if (value < Minimum) if (value < Minimum)
{ {

6
src/Avalonia.Controls/NumericUpDown/NumericUpDownValueChangedEventArgs.cs

@ -4,13 +4,13 @@ namespace Avalonia.Controls
{ {
public class NumericUpDownValueChangedEventArgs : RoutedEventArgs public class NumericUpDownValueChangedEventArgs : RoutedEventArgs
{ {
public NumericUpDownValueChangedEventArgs(RoutedEvent routedEvent, double oldValue, double newValue) : base(routedEvent) public NumericUpDownValueChangedEventArgs(RoutedEvent routedEvent, decimal oldValue, decimal newValue) : base(routedEvent)
{ {
OldValue = oldValue; OldValue = oldValue;
NewValue = newValue; NewValue = newValue;
} }
public double OldValue { get; } public decimal OldValue { get; }
public double NewValue { get; } public decimal NewValue { get; }
} }
} }

Loading…
Cancel
Save