Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
UpDown control support decimals, and the value for each increase or d…
…ecrease can be set.
  • Loading branch information
futuresBoy committed Aug 30, 2024
commit 39f80256f48f0589ba9414a3aa548fb286f42d53
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
Expand Up @@ -25,10 +25,17 @@
<StackPanel Margin="0,16,0,0"
DockPanel.Dock="Top"
Orientation="Horizontal">

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

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

<smtx:XamlDisplay Margin="10,5"
Expand Down
30 changes: 30 additions & 0 deletions src/MaterialDesignThemes.Wpf/DecimalUpDown.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
namespace MaterialDesignThemes.Wpf;

public class DecimalUpDown : UpDownBase<decimal, DecimalArithmetic>
{
public DecimalUpDown()
{
ValueStep = 1m;
}
}

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 string ConvertToString(decimal value) => value.ToString();

public decimal MinValue() => decimal.MinValue;

public decimal MaxValue() => decimal.MaxValue;

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, out decimal value) => decimal.TryParse(text, out value);
}
272 changes: 16 additions & 256 deletions src/MaterialDesignThemes.Wpf/NumericUpDown.cs
Original file line number Diff line number Diff line change
@@ -1,272 +1,32 @@
using System.ComponentModel;
using System.Globalization;

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 : UpDownBase<int, IntArithmetic>
{
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 decimal Minimum
public NumericUpDown()
{
get => (decimal)GetValue(MinimumProperty);
set => SetValue(MinimumProperty, value);
ValueStep = 1;
}
public static readonly DependencyProperty MinimumProperty =
DependencyProperty.Register(nameof(Minimum), typeof(decimal), typeof(NumericUpDown), new PropertyMetadata(decimal.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 decimal Maximum
{
get => (decimal)GetValue(MaximumProperty);
set => SetValue(MaximumProperty, value);
}

public static readonly DependencyProperty MaximumProperty =
DependencyProperty.Register(nameof(Maximum), typeof(decimal), typeof(NumericUpDown), new PropertyMetadata(decimal.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 decimal numericValue)
{
return Math.Max(numericUpDown.Minimum, numericValue);
}
return value;
}

#endregion DependencyProperty : MaximumProperty

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

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

private static void OnNumericValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is NumericUpDown numericUpDown)
{
var args = new RoutedPropertyChangedEventArgs<decimal>((decimal)e.OldValue, (decimal)e.NewValue)
{
RoutedEvent = ValueChangedEvent
};
numericUpDown.RaiseEvent(args);
if (numericUpDown._textBoxField is { } textBox)
{
textBox.Text = ((decimal)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 decimal numericValue)
{
numericValue = Math.Min(numericUpDown.Maximum, numericValue);
numericValue = Math.Max(numericUpDown.Minimum, numericValue);
return numericValue;
}
return value;
}
#endregion ValueProperty

#region DependencyProperty : ValueStep
/// <summary>
/// The size of value for each increase or decrease
/// </summary>
public decimal ValueMinStep
{
get { return (decimal)GetValue(ValueMinStepProperty); }
set { SetValue(ValueMinStepProperty, value); }
}

// Using a DependencyProperty as the backing store for ValueStep. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ValueMinStepProperty =
DependencyProperty.Register("ValueMinStep", typeof(decimal), typeof(NumericUpDown), new PropertyMetadata(1m));
#endregion

#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;
public class IntArithmetic : IArithmetic<int>
{
public int Add(int value1, int value2) => value1 + value2;

if (_increaseButton != null)
_increaseButton.Click += IncreaseButtonOnClick;
public int Subtract(int value1, int value2) => value1 - value2;

if (_decreaseButton != null)
_decreaseButton.Click += DecreaseButtonOnClick;
public int Compare(int value1, int value2) => value1.CompareTo(value2);

if (_textBoxField != null)
{
_textBoxField.LostFocus += OnTextBoxFocusLost;
_textBoxField.Text = Value.ToString(CultureInfo.CurrentUICulture);
}
public string ConvertToString(int value) => value.ToString();

base.OnApplyTemplate();
}
public int MinValue() => int.MinValue;

private void OnTextBoxFocusLost(object sender, EventArgs e)
{
if (_textBoxField is { } textBoxField)
{
if (decimal.TryParse(textBoxField.Text, out decimal numericValue))
{
SetCurrentValue(ValueProperty, numericValue);
}
else
{
//undo
}
}
}
public int MaxValue() => int.MaxValue;

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

private void DecreaseButtonOnClick(object sender, RoutedEventArgs e) => OnDecrease();
public int Min(int value1, int value2) => Math.Min(value1, value2);

private void OnIncrease()
{
SetCurrentValue(ValueProperty, Value + ValueMinStep);
}
public bool TryParse(string text, out int value) => int.TryParse(text, out value);
}

private void OnDecrease()
{
SetCurrentValue(ValueProperty, Value - ValueMinStep);
}

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);
}

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);
}
}
2 changes: 1 addition & 1 deletion src/MaterialDesignThemes.Wpf/Themes/Generic.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@
<Style TargetType="{x:Type local:TimePicker}" BasedOn="{StaticResource MaterialDesignTimePicker}" />
<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:UpDownBase}" BasedOn="{StaticResource MaterialDesignNumericUpDown}" />

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