From 09121b8d76822d21f8a8266b5efce3587cdf5709 Mon Sep 17 00:00:00 2001 From: koal44 Date: Mon, 25 Mar 2024 21:14:18 -0700 Subject: [PATCH 1/9] Impement new control NumericUpDown --- .../BasicInput/NumericUpDownViewModel.cs | 10 ++ .../ViewModels/Windows/MainWindowViewModel.cs | 1 + .../Pages/BasicInput/NumericUpDownPage.xaml | 44 +++++ .../BasicInput/NumericUpDownPage.xaml.cs | 24 +++ .../Controls/NumericUpDown/NumericUpDown.cs | 170 ++++++++++++++++++ .../Controls/NumericUpDown/NumericUpDown.xaml | 119 ++++++++++++ src/Wpf.Ui/Resources/Theme/Dark.xaml | 11 ++ src/Wpf.Ui/Resources/Wpf.Ui.xaml | 1 + 8 files changed, 380 insertions(+) create mode 100644 src/Wpf.Ui.Gallery/ViewModels/Pages/BasicInput/NumericUpDownViewModel.cs create mode 100644 src/Wpf.Ui.Gallery/Views/Pages/BasicInput/NumericUpDownPage.xaml create mode 100644 src/Wpf.Ui.Gallery/Views/Pages/BasicInput/NumericUpDownPage.xaml.cs create mode 100644 src/Wpf.Ui/Controls/NumericUpDown/NumericUpDown.cs create mode 100644 src/Wpf.Ui/Controls/NumericUpDown/NumericUpDown.xaml diff --git a/src/Wpf.Ui.Gallery/ViewModels/Pages/BasicInput/NumericUpDownViewModel.cs b/src/Wpf.Ui.Gallery/ViewModels/Pages/BasicInput/NumericUpDownViewModel.cs new file mode 100644 index 000000000..e4fc7a882 --- /dev/null +++ b/src/Wpf.Ui.Gallery/ViewModels/Pages/BasicInput/NumericUpDownViewModel.cs @@ -0,0 +1,10 @@ +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, You can obtain one at https://opensource.org/licenses/MIT. +// Copyright (C) Leszek Pomianowski and WPF UI Contributors. +// All Rights Reserved. + +namespace Wpf.Ui.Gallery.ViewModels.Pages.BasicInput; + +public class NumericUpDownViewModel : ObservableObject +{ +} diff --git a/src/Wpf.Ui.Gallery/ViewModels/Windows/MainWindowViewModel.cs b/src/Wpf.Ui.Gallery/ViewModels/Windows/MainWindowViewModel.cs index e45879a95..297150731 100644 --- a/src/Wpf.Ui.Gallery/ViewModels/Windows/MainWindowViewModel.cs +++ b/src/Wpf.Ui.Gallery/ViewModels/Windows/MainWindowViewModel.cs @@ -56,6 +56,7 @@ public partial class MainWindowViewModel : ObservableObject new NavigationViewItem(nameof(ToggleSwitch), typeof(ToggleSwitchPage)), new NavigationViewItem(nameof(CheckBox), typeof(CheckBoxPage)), new NavigationViewItem(nameof(ComboBox), typeof(ComboBoxPage)), + new NavigationViewItem(nameof(NumericUpDown), typeof(NumericUpDownPage)), new NavigationViewItem(nameof(RadioButton), typeof(RadioButtonPage)), new NavigationViewItem(nameof(RatingControl), typeof(RatingPage)), new NavigationViewItem(nameof(ThumbRate), typeof(ThumbRatePage)), diff --git a/src/Wpf.Ui.Gallery/Views/Pages/BasicInput/NumericUpDownPage.xaml b/src/Wpf.Ui.Gallery/Views/Pages/BasicInput/NumericUpDownPage.xaml new file mode 100644 index 000000000..0b56dc509 --- /dev/null +++ b/src/Wpf.Ui.Gallery/Views/Pages/BasicInput/NumericUpDownPage.xaml @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + diff --git a/src/Wpf.Ui.Gallery/Views/Pages/BasicInput/NumericUpDownPage.xaml.cs b/src/Wpf.Ui.Gallery/Views/Pages/BasicInput/NumericUpDownPage.xaml.cs new file mode 100644 index 000000000..3ecf28786 --- /dev/null +++ b/src/Wpf.Ui.Gallery/Views/Pages/BasicInput/NumericUpDownPage.xaml.cs @@ -0,0 +1,24 @@ +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, You can obtain one at https://opensource.org/licenses/MIT. +// Copyright (C) Leszek Pomianowski and WPF UI Contributors. +// All Rights Reserved. + +using Wpf.Ui.Controls; +using Wpf.Ui.Gallery.ControlsLookup; +using Wpf.Ui.Gallery.ViewModels.Pages.BasicInput; + +namespace Wpf.Ui.Gallery.Views.Pages.BasicInput; + +[GalleryPage("NumericUpDown control", SymbolRegular.NumberSymbol16)] +public partial class NumericUpDownPage : INavigableView +{ + public NumericUpDownViewModel ViewModel { get; } + + public NumericUpDownPage(NumericUpDownViewModel viewModel) + { + ViewModel = viewModel; + DataContext = this; + + InitializeComponent(); + } +} diff --git a/src/Wpf.Ui/Controls/NumericUpDown/NumericUpDown.cs b/src/Wpf.Ui/Controls/NumericUpDown/NumericUpDown.cs new file mode 100644 index 000000000..cf14e49ab --- /dev/null +++ b/src/Wpf.Ui/Controls/NumericUpDown/NumericUpDown.cs @@ -0,0 +1,170 @@ +using RepeatButton = System.Windows.Controls.Primitives.RepeatButton; + +namespace Wpf.Ui.Controls; + +[TemplatePart(Name = "PART_UpButton", Type = typeof(RepeatButton))] +[TemplatePart(Name = "PART_DownButton", Type = typeof(RepeatButton))] +public class NumericUpDown : System.Windows.Controls.Control +{ + static NumericUpDown() + { + DefaultStyleKeyProperty.OverrideMetadata(typeof(NumericUpDown), new FrameworkPropertyMetadata(typeof(NumericUpDown))); + + BorderThicknessProperty.OverrideMetadata(typeof(NumericUpDown), new FrameworkPropertyMetadata(new Thickness(1))); + } + + public NumericUpDown() + { + Loaded += (s, e) => UpdateDisplayValue(); + } + + public double Value + { + get => (double)GetValue(ValueProperty); + set => SetValue(ValueProperty, value); + } + + /// Identifies the dependency property. + public static readonly DependencyProperty ValueProperty = DependencyProperty.Register(nameof(Value), typeof(double), typeof(NumericUpDown), new FrameworkPropertyMetadata(0.0, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnValueChanged, CoerceValue)); + + private static void OnValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + if (d is NumericUpDown self) + { + self.OnValueChanged(e); + } + } + + protected virtual void OnValueChanged(DependencyPropertyChangedEventArgs e) + { + UpdateDisplayValue(); + } + + private static object CoerceValue(DependencyObject d, object baseValue) + { + if (d is NumericUpDown self && baseValue is double doubleval) + { + return self.CoerceMyValue(doubleval); + } + + return 0.0; + } + + public string DisplayValue + { + get => (string)GetValue(DisplayValueProperty); + } + + /// Identifies the dependency property. + protected static readonly DependencyPropertyKey DisplayValuePropertyKey = DependencyProperty.RegisterReadOnly(nameof(DisplayValue), typeof(string), typeof(NumericUpDown), new FrameworkPropertyMetadata(default(string))); + + /// Identifies the dependency property. + public static readonly DependencyProperty DisplayValueProperty = DisplayValuePropertyKey.DependencyProperty; + + public double Step + { + get => (double)GetValue(StepProperty); + set => SetValue(StepProperty, value); + } + + /// Identifies the dependency property. + public static readonly DependencyProperty StepProperty = DependencyProperty.Register(nameof(Step), typeof(double), typeof(NumericUpDown), new PropertyMetadata(0.1d)); + + public int Decimals + { + get => (int)GetValue(DecimalsProperty); + set => SetValue(DecimalsProperty, value); + } + + /// Identifies the dependency property. + public static readonly DependencyProperty DecimalsProperty = DependencyProperty.Register(nameof(Decimals), typeof(int), typeof(NumericUpDown), new PropertyMetadata(2, OnDecimalsChanged)); + + private static void OnDecimalsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + if (d is NumericUpDown self) + { + self.CoerceValue(ValueProperty); + } + } + + public double MaxValue + { + get => (double)GetValue(MaxValueProperty); + set => SetValue(MaxValueProperty, value); + } + + /// Identifies the dependency property. + public static readonly DependencyProperty MaxValueProperty = DependencyProperty.Register(nameof(MaxValue), typeof(double), typeof(NumericUpDown), new PropertyMetadata(100.0, OnMaxValueChanged)); + + private static void OnMaxValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + if (d is NumericUpDown self) + { + if (self.MinValue > self.MaxValue) + { + self.MinValue = self.MaxValue; + } + + self.CoerceValue(ValueProperty); + } + } + + public double MinValue + { + get => (double)GetValue(MinValueProperty); + set => SetValue(MinValueProperty, value); + } + + /// Identifies the dependency property. + public static readonly DependencyProperty MinValueProperty = DependencyProperty.Register(nameof(MinValue), typeof(double), typeof(NumericUpDown), new PropertyMetadata(0.0, OnMinValueChanged)); + + private static void OnMinValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + if (d is NumericUpDown self) + { + if (self.MaxValue < self.MinValue) + { + self.MaxValue = self.MinValue; + } + + self.CoerceValue(ValueProperty); + } + } + + private double CoerceMyValue(double val) + { + double clampedValue = Math.Max(MinValue, Math.Min(MaxValue, val)); + double roundedValue = Math.Round(clampedValue, Decimals); + + return roundedValue; + } + + protected virtual void UpdateDisplayValue() + { + CultureInfo culture = CultureInfo.CurrentCulture; + string format = "F" + Decimals; + string displayValue = Value.ToString(format, culture); + SetValue(DisplayValuePropertyKey, displayValue); + } + + public override void OnApplyTemplate() + { + base.OnApplyTemplate(); + + if (GetTemplateChild("PART_DownButton") is RepeatButton downButton) + { + downButton.Click += (sender, e) => + { + Value -= Step; + }; + } + + if (GetTemplateChild("PART_UpButton") is RepeatButton upButton) + { + upButton.Click += (sender, e) => + { + Value += Step; + }; + } + } +} \ No newline at end of file diff --git a/src/Wpf.Ui/Controls/NumericUpDown/NumericUpDown.xaml b/src/Wpf.Ui/Controls/NumericUpDown/NumericUpDown.xaml new file mode 100644 index 000000000..498ac7650 --- /dev/null +++ b/src/Wpf.Ui/Controls/NumericUpDown/NumericUpDown.xaml @@ -0,0 +1,119 @@ + + + + + + + + + + + + - - - - - + +