Skip to content

Commit

Permalink
NumericUpDown control support decimals value, and the value for each …
Browse files Browse the repository at this point in the history
…increase or decrease can be set. (MaterialDesignInXAML#3661)

* Support decimals value, and the size of value for each increase or decrease can be set.

* UpDown control support decimals, and the value for each increase or decrease can be set.

* Updates to UpDownBase to support generic math on NET8

TODO: Need to fix XAMLTest to support the common generic base class so the generator stops choking

* Updating UI tests

Updated new XAMLTest library
Added DeciamlUpDown tests

---------

Co-authored-by: Kevin Bost <kevin@intellitect.com>
Co-authored-by: Kevin Bost <kitokeboo@gmail.com>
  • Loading branch information
3 people authored and JLdgu committed Nov 8, 2024
1 parent 4f915a2 commit 7318ebb
Show file tree
Hide file tree
Showing 13 changed files with 617 additions and 290 deletions.
4 changes: 2 additions & 2 deletions Directory.packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,9 @@
<PackageVersion Include="System.Net.Http" Version="4.3.4" />
<PackageVersion Include="System.Text.RegularExpressions" Version="4.3.1" />
<PackageVersion Include="VirtualizingWrapPanel" Version="1.5.8" />
<PackageVersion Include="XAMLTest" Version="1.2.1" />
<PackageVersion Include="XAMLTest" Version="1.2.2" />
<PackageVersion Include="xunit" Version="2.6.2" />
<PackageVersion Include="xunit.runner.visualstudio" Version="2.5.4" />
<PackageVersion Include="Xunit.StaFact" Version="1.1.11" />
</ItemGroup>
</Project>
</Project>
4 changes: 3 additions & 1 deletion src/MainDemo.Wpf/Domain/MainWindowViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -475,7 +475,9 @@ private static IEnumerable<DemoItem> GenerateDemoItems(ISnackbarMessageQueue sna
{
DocumentationLink.DemoPageLink<NumericUpDown>(),
DocumentationLink.StyleLink(nameof(NumericUpDown)),
DocumentationLink.ApiLink<NumericUpDown>()
DocumentationLink.ApiLink<NumericUpDown>(),
DocumentationLink.ApiLink<DecimalUpDown>(),
DocumentationLink.ApiLink<UpDownBase>()
});
}

Expand Down
9 changes: 8 additions & 1 deletion src/MainDemo.Wpf/NumericUpDown.xaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<UserControl x:Class="MaterialDesignDemo.NumericUpDown"
<UserControl x:Class="MaterialDesignDemo.NumericUpDown"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
Expand All @@ -25,12 +25,19 @@
<StackPanel Margin="0,16,0,0"
DockPanel.Dock="Top"
Orientation="Horizontal">

<smtx:XamlDisplay Margin="10,5"
VerticalAlignment="Top"
UniqueKey="numericUpDown_default">
<materialDesign:NumericUpDown />
</smtx:XamlDisplay>

<smtx:XamlDisplay Margin="10,5"
VerticalAlignment="Top"
UniqueKey="decimalUpDown_default">
<materialDesign:DecimalUpDown ValueStep="0.5" Minimum="-2.5" Maximum="2.5" />
</smtx:XamlDisplay>

<smtx:XamlDisplay Margin="10,5"
VerticalAlignment="Top"
UniqueKey="numericUpDown_customButtonContent">
Expand Down
42 changes: 42 additions & 0 deletions src/MaterialDesignThemes.Wpf/DecimalUpDown.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
#if !NET8_0_OR_GREATER
using System.Globalization;
#endif

namespace MaterialDesignThemes.Wpf;

public class DecimalUpDown
#if NET8_0_OR_GREATER
: UpDownBase<decimal>
#else
: UpDownBase<decimal, DecimalArithmetic>
#endif
{
static DecimalUpDown()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(DecimalUpDown), new FrameworkPropertyMetadata(typeof(DecimalUpDown)));
}
}

#if !NET8_0_OR_GREATER
public class DecimalArithmetic : IArithmetic<decimal>
{
public decimal Add(decimal value1, decimal value2) => value1 + value2;

public decimal Subtract(decimal value1, decimal value2) => value1 - value2;

public int Compare(decimal value1, decimal value2) => value1.CompareTo(value2);

public decimal MinValue() => decimal.MinValue;

public decimal MaxValue() => decimal.MaxValue;

public decimal One() => 1m;

public decimal Max(decimal value1, decimal value2) => Math.Max(value1, value2);

public decimal Min(decimal value1, decimal value2) => Math.Min(value1, value2);

public bool TryParse(string text, IFormatProvider? formatProvider, out decimal value)
=> decimal.TryParse(text, NumberStyles.Number, formatProvider, out value);
}
#endif
264 changes: 23 additions & 241 deletions src/MaterialDesignThemes.Wpf/NumericUpDown.cs
Original file line number Diff line number Diff line change
@@ -1,259 +1,41 @@
using System.ComponentModel;
#if !NET8_0_OR_GREATER
using System.Globalization;
#endif

namespace MaterialDesignThemes.Wpf;

[TemplatePart(Name = IncreaseButtonPartName, Type = typeof(RepeatButton))]
[TemplatePart(Name = DecreaseButtonPartName, Type = typeof(RepeatButton))]
[TemplatePart(Name = TextBoxPartName, Type = typeof(TextBox))]
public class NumericUpDown : Control
public class NumericUpDown
#if NET8_0_OR_GREATER
: UpDownBase<int>
#else
: UpDownBase<int, IntArithmetic>
#endif
{
public const string IncreaseButtonPartName = "PART_IncreaseButton";
public const string DecreaseButtonPartName = "PART_DecreaseButton";
public const string TextBoxPartName = "PART_TextBox";

private TextBox? _textBoxField;
private RepeatButton? _decreaseButton;
private RepeatButton? _increaseButton;

static NumericUpDown()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(NumericUpDown), new FrameworkPropertyMetadata(typeof(NumericUpDown)));
}
}

#region DependencyProperties



public object? IncreaseContent
{
get => GetValue(IncreaseContentProperty);
set => SetValue(IncreaseContentProperty, value);
}

// Using a DependencyProperty as the backing store for IncreaseContent. This enables animation, styling, binding, etc...
public static readonly DependencyProperty IncreaseContentProperty =
DependencyProperty.Register(nameof(IncreaseContent), typeof(object), typeof(NumericUpDown), new PropertyMetadata(null));

public object? DecreaseContent
{
get => GetValue(DecreaseContentProperty);
set => SetValue(DecreaseContentProperty, value);
}

// Using a DependencyProperty as the backing store for DecreaseContent. This enables animation, styling, binding, etc...
public static readonly DependencyProperty DecreaseContentProperty =
DependencyProperty.Register(nameof(DecreaseContent), typeof(object), typeof(NumericUpDown), new PropertyMetadata(null));

#region DependencyProperty : MinimumProperty
public int Minimum
{
get => (int)GetValue(MinimumProperty);
set => SetValue(MinimumProperty, value);
}
public static readonly DependencyProperty MinimumProperty =
DependencyProperty.Register(nameof(Minimum), typeof(int), typeof(NumericUpDown), new PropertyMetadata(int.MinValue, OnMinimumChanged));

private static void OnMinimumChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
NumericUpDown ctrl = (NumericUpDown)d;
ctrl.CoerceValue(ValueProperty);
ctrl.CoerceValue(MaximumProperty);
}

#endregion DependencyProperty : MinimumProperty

#region DependencyProperty : MaximumProperty

public int Maximum
{
get => (int)GetValue(MaximumProperty);
set => SetValue(MaximumProperty, value);
}

public static readonly DependencyProperty MaximumProperty =
DependencyProperty.Register(nameof(Maximum), typeof(int), typeof(NumericUpDown), new PropertyMetadata(int.MaxValue, OnMaximumChanged, CoerceMaximum));

private static void OnMaximumChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
NumericUpDown ctrl = (NumericUpDown)d;
ctrl.CoerceValue(ValueProperty);
}

private static object? CoerceMaximum(DependencyObject d, object? value)
{
if (d is NumericUpDown numericUpDown &&
value is int numericValue)
{
return Math.Max(numericUpDown.Minimum, numericValue);
}
return value;
}

#endregion DependencyProperty : MaximumProperty

#region DependencyProperty : ValueProperty
public int Value
{
get => (int)GetValue(ValueProperty);
set => SetValue(ValueProperty, value);
}

public static readonly DependencyProperty ValueProperty =
DependencyProperty.Register(nameof(Value), typeof(int), typeof(NumericUpDown), new FrameworkPropertyMetadata(0, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnNumericValueChanged, CoerceNumericValue));

private static void OnNumericValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is NumericUpDown numericUpDown)
{
var args = new RoutedPropertyChangedEventArgs<int>((int)e.OldValue, (int)e.NewValue)
{
RoutedEvent = ValueChangedEvent
};
numericUpDown.RaiseEvent(args);
if (numericUpDown._textBoxField is { } textBox)
{
textBox.Text = ((int)e.NewValue).ToString(CultureInfo.CurrentUICulture);
}

if (numericUpDown._increaseButton is { } increaseButton)
{
increaseButton.IsEnabled = numericUpDown.Value != numericUpDown.Maximum;
}

if (numericUpDown._decreaseButton is { } decreaseButton)
{
decreaseButton.IsEnabled = numericUpDown.Value != numericUpDown.Minimum;
}
}
}

private static object? CoerceNumericValue(DependencyObject d, object? value)
{
if (d is NumericUpDown numericUpDown &&
value is int numericValue)
{
numericValue = Math.Min(numericUpDown.Maximum, numericValue);
numericValue = Math.Max(numericUpDown.Minimum, numericValue);
return numericValue;
}
return value;
}
#endregion ValueProperty

#region DependencyProperty : AllowChangeOnScroll

public bool AllowChangeOnScroll
{
get => (bool)GetValue(AllowChangeOnScrollProperty);
set => SetValue(AllowChangeOnScrollProperty, value);
}

public static readonly DependencyProperty AllowChangeOnScrollProperty =
DependencyProperty.Register(nameof(AllowChangeOnScroll), typeof(bool), typeof(NumericUpDown), new PropertyMetadata(false));

#endregion

#endregion DependencyProperties

#region Event : ValueChangedEvent
[Category("Behavior")]
public static readonly RoutedEvent ValueChangedEvent = EventManager.RegisterRoutedEvent(nameof(ValueChanged), RoutingStrategy.Bubble, typeof(RoutedPropertyChangedEventHandler<int>), typeof(NumericUpDown));

public event RoutedPropertyChangedEventHandler<int> ValueChanged
{
add => AddHandler(ValueChangedEvent, value);
remove => RemoveHandler(ValueChangedEvent, value);
}
#endregion Event : ValueChangedEvent

public override void OnApplyTemplate()
{
if (_increaseButton != null)
_increaseButton.Click -= IncreaseButtonOnClick;

if (_decreaseButton != null)
_decreaseButton.Click -= DecreaseButtonOnClick;
if (_textBoxField != null)
_textBoxField.TextChanged -= OnTextBoxFocusLost;

_increaseButton = GetTemplateChild(IncreaseButtonPartName) as RepeatButton;
_decreaseButton = GetTemplateChild(DecreaseButtonPartName) as RepeatButton;
_textBoxField = GetTemplateChild(TextBoxPartName) as TextBox;

if (_increaseButton != null)
_increaseButton.Click += IncreaseButtonOnClick;

if (_decreaseButton != null)
_decreaseButton.Click += DecreaseButtonOnClick;

if (_textBoxField != null)
{
_textBoxField.LostFocus += OnTextBoxFocusLost;
_textBoxField.Text = Value.ToString(CultureInfo.CurrentUICulture);
}

base.OnApplyTemplate();
}
#if !NET8_0_OR_GREATER
public class IntArithmetic : IArithmetic<int>
{
public int Add(int value1, int value2) => value1 + value2;

private void OnTextBoxFocusLost(object sender, EventArgs e)
{
if (_textBoxField is { } textBoxField)
{
if (int.TryParse(textBoxField.Text, NumberStyles.Integer, CultureInfo.CurrentUICulture, out int numericValue))
{
SetCurrentValue(ValueProperty, numericValue);
}
else
{
textBoxField.Text = Value.ToString(CultureInfo.CurrentUICulture);
}
}
}
public int Subtract(int value1, int value2) => value1 - value2;

private void IncreaseButtonOnClick(object sender, RoutedEventArgs e) => OnIncrease();
public int Compare(int value1, int value2) => value1.CompareTo(value2);

private void DecreaseButtonOnClick(object sender, RoutedEventArgs e) => OnDecrease();
public int MinValue() => int.MinValue;

private void OnIncrease()
{
SetCurrentValue(ValueProperty, Value + 1);
}
public int MaxValue() => int.MaxValue;
public int One() => 1;

private void OnDecrease()
{
SetCurrentValue(ValueProperty, Value - 1);
}
public int Max(int value1, int value2) => Math.Max(value1, value2);

protected override void OnPreviewKeyDown(KeyEventArgs e)
{
if (e.Key == Key.Up)
{
OnIncrease();
e.Handled = true;
}
else if (e.Key == Key.Down)
{
OnDecrease();
e.Handled = true;
}
base.OnPreviewKeyDown(e);
}
public int Min(int value1, int value2) => Math.Min(value1, value2);

protected override void OnPreviewMouseWheel(MouseWheelEventArgs e)
{
if (IsKeyboardFocusWithin && AllowChangeOnScroll)
{
if (e.Delta > 0)
{
OnIncrease();
}
else if (e.Delta < 0)
{
OnDecrease();
}
e.Handled = true;
}
base.OnPreviewMouseWheel(e);
}
public bool TryParse(string text, IFormatProvider? formatProvider, out int value)
=> int.TryParse(text, NumberStyles.Integer, formatProvider, out value);
}
#endif
1 change: 1 addition & 0 deletions src/MaterialDesignThemes.Wpf/Themes/Generic.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
<Style TargetType="{x:Type local:AutoSuggestBox}" BasedOn="{StaticResource MaterialDesignAutoSuggestBox}" />
<Style TargetType="{x:Type local:SplitButton}" BasedOn="{StaticResource MaterialDesignRaisedSplitButton}" />
<Style TargetType="{x:Type local:NumericUpDown}" BasedOn="{StaticResource MaterialDesignNumericUpDown}" />
<Style TargetType="{x:Type local:DecimalUpDown}" BasedOn="{StaticResource MaterialDesignNumericUpDown}" />

<converters:BrushToRadialGradientBrushConverter x:Key="BrushToRadialGradientBrushConverter" />
<converters:DrawerOffsetConverter x:Key="DrawerOffsetConverter" />
Expand Down
Loading

0 comments on commit 7318ebb

Please sign in to comment.