Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/numericupdown control #1022

Open
wants to merge 12 commits into
base: main
Choose a base branch
from
Open
Next Next commit
Impement new control NumericUpDown
  • Loading branch information
koal44 committed Mar 27, 2024
commit 09121b8d76822d21f8a8266b5efce3587cdf5709
Original file line number Diff line number Diff line change
@@ -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
{
}
Original file line number Diff line number Diff line change
Expand Up @@ -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)),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<Page
x:Class="Wpf.Ui.Gallery.Views.Pages.BasicInput.NumericUpDownPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="clr-namespace:Wpf.Ui.Gallery.Controls"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Wpf.Ui.Gallery.Views.Pages.BasicInput"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:ui="http://schemas.lepo.co/wpfui/2022/xaml"
Title="RatingPage"
controls:PageControlDocumentation.DocumentationType="{x:Type ui:NumericUpDown}"
d:DataContext="{d:DesignInstance local:NumericUpDownPage,
IsDesignTimeCreatable=False}"
d:DesignHeight="450"
d:DesignWidth="800"
ui:Design.Background="{DynamicResource ApplicationBackgroundBrush}"
ui:Design.Foreground="{DynamicResource TextFillColorPrimaryBrush}"
Foreground="{DynamicResource TextFillColorPrimaryBrush}"
mc:Ignorable="d">

<StackPanel Margin="0,0,0,24">
<controls:ControlExample
Margin="0"
HeaderText="WPF UI NumericUpDown"
XamlCode="&lt;ui:NumericUpDown /&gt;">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<ui:NumericUpDown
Width="100"
IsEnabled="False"
Padding="0"
HorizontalAlignment="Left"
Decimals="0"
Step="1"
Value="0" />
</Grid>
</controls:ControlExample>

</StackPanel>
</Page>
Original file line number Diff line number Diff line change
@@ -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<NumericUpDownViewModel>
{
public NumericUpDownViewModel ViewModel { get; }

public NumericUpDownPage(NumericUpDownViewModel viewModel)
{
ViewModel = viewModel;
DataContext = this;

InitializeComponent();
}
}
170 changes: 170 additions & 0 deletions src/Wpf.Ui/Controls/NumericUpDown/NumericUpDown.cs
Original file line number Diff line number Diff line change
@@ -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);
}

/// <summary>Identifies the <see cref="Value"/> dependency property.</summary>
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);
}

/// <summary>Identifies the <see cref="DisplayValue"/> dependency property.</summary>
protected static readonly DependencyPropertyKey DisplayValuePropertyKey = DependencyProperty.RegisterReadOnly(nameof(DisplayValue), typeof(string), typeof(NumericUpDown), new FrameworkPropertyMetadata(default(string)));

/// <summary>Identifies the <see cref="DisplayValue"/> dependency property.</summary>
public static readonly DependencyProperty DisplayValueProperty = DisplayValuePropertyKey.DependencyProperty;

public double Step
{
get => (double)GetValue(StepProperty);
set => SetValue(StepProperty, value);
}

/// <summary>Identifies the <see cref="Step"/> dependency property.</summary>
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);
}

/// <summary>Identifies the <see cref="Decimals"/> dependency property.</summary>
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);
}

/// <summary>Identifies the <see cref="MaxValue"/> dependency property.</summary>
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);
}

/// <summary>Identifies the <see cref="MinValue"/> dependency property.</summary>
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;
};
}
}
}
119 changes: 119 additions & 0 deletions src/Wpf.Ui/Controls/NumericUpDown/NumericUpDown.xaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="clr-namespace:Wpf.Ui.Controls"
xmlns:converters="clr-namespace:Wpf.Ui.Converters">

<Style x:Key="NumericUpDownButtonStyle" TargetType="RepeatButton">
<Setter Property="Foreground" Value="{DynamicResource NumericUpDownButtonForeground}" />
<Setter Property="Background" Value="{DynamicResource NumericUpDownButtonBackground}" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="RepeatButton">
<Border
x:Name="UpDownButtonBorder"
Padding="6,0"
Background="{TemplateBinding Background}"
BorderBrush="Transparent"
BorderThickness="0"
CornerRadius="{TemplateBinding Border.CornerRadius}">
<Viewbox>
<Path
x:Name="UpDownButtonPath"
Data="{TemplateBinding Path.Data}"
Fill="{TemplateBinding Foreground}" />
</Viewbox>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="UpDownButtonBorder" Property="Background" Value="{DynamicResource NumericUpDownButtonBackgroundPointerOver}" />
</Trigger>
<Trigger Property="IsPressed" Value="True">
<Setter TargetName="UpDownButtonBorder" Property="Background" Value="{DynamicResource NumericUpDownButtonBackgroundPressed}" />
<Setter TargetName="UpDownButtonPath" Property="Path.Fill" Value="{DynamicResource NumericUpDownButtonForegroundPressed}" />
</Trigger>
<Trigger Property="IsEnabled" Value="False">
<Setter TargetName="UpDownButtonBorder" Property="Background" Value="{DynamicResource NumericUpDownButtonBackgroundDisabled}" />
<Setter TargetName="UpDownButtonPath" Property="Path.Fill" Value="{DynamicResource NumericUpDownButtonForegroundDisabled}" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>

<Style
x:Key="NumericUpButtonStyle"
BasedOn="{StaticResource NumericUpDownButtonStyle}"
TargetType="RepeatButton">
<Setter Property="Border.CornerRadius" Value="0,4,0,0" />
<Setter Property="Path.Data" Value="M4,0 L0,4 L8,4 z" />
</Style>

<Style
x:Key="NumericDownButtonStyle"
BasedOn="{StaticResource NumericUpDownButtonStyle}"
TargetType="RepeatButton">
<Setter Property="Border.CornerRadius" Value="0,0,4,0" />
<Setter Property="Path.Data" Value="M0,0 L8,0 L4,4 z" />
</Style>


<Style x:Key="NumericUpDownStyle" TargetType="{x:Type controls:NumericUpDown}">
<Setter Property="Foreground" Value="{DynamicResource NumericUpDownForeground}" />
<Setter Property="Background" Value="{DynamicResource NumericUpDownBackground}" />
<!-- styling the border requires control's cornerradius to match nested button's cornerradius depending on borderthickness -->
<!--<Setter Property="BorderBrush" Value="{DynamicResource NumericUpDownBorderBrush}" />
<Setter Property="BorderThickness" Value="3" />-->
<Setter Property="FontSize" Value="{DynamicResource ControlContentThemeFontSize}" />
<Setter Property="MinHeight" Value="{DynamicResource TextControlThemeMinHeight}" />
<Setter Property="MinWidth" Value="{DynamicResource TextControlThemeMinWidth}" />
<Setter Property="Padding" Value="{DynamicResource TextControlThemePadding}" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type controls:NumericUpDown}">
<Border
MinWidth="{TemplateBinding MinWidth}"
MinHeight="{TemplateBinding MinHeight}"
Background="{TemplateBinding Background}"
BorderThickness="0"
CornerRadius="4">
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition Width="22" />
</Grid.ColumnDefinitions>
<TextBlock
Grid.RowSpan="2"
Grid.Column="0"
Padding="{TemplateBinding Padding}"
VerticalAlignment="Center"
FontSize="{TemplateBinding FontSize}"
FontWeight="Medium"
Foreground="{TemplateBinding Foreground}"
Text="{Binding DisplayValue, RelativeSource={RelativeSource AncestorType={x:Type controls:NumericUpDown}}}"
TextAlignment="Center" />
<RepeatButton
x:Name="PART_UpButton"
Grid.Row="0"
Grid.Column="1"
Style="{StaticResource NumericUpButtonStyle}" />
<RepeatButton
x:Name="PART_DownButton"
Grid.Row="1"
Grid.Column="1"
Style="{StaticResource NumericDownButtonStyle}" />
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>

<Style BasedOn="{StaticResource NumericUpDownStyle}" TargetType="{x:Type controls:NumericUpDown}" />

</ResourceDictionary>
11 changes: 11 additions & 0 deletions src/Wpf.Ui/Resources/Theme/Dark.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -529,6 +529,17 @@
<SolidColorBrush x:Key="NavigationViewItemForegroundLeftFluent" Color="{StaticResource TextFillColorPrimary}" />
<SolidColorBrush x:Key="NavigationViewItemForegroundPointerOverLeftFluent" Color="{StaticResource TextFillColorPrimary}" />

<!-- NumericUpDown -->
<SolidColorBrush x:Key="NumericUpDownForeground" Color="{StaticResource TextFillColorPrimary}" />
<SolidColorBrush x:Key="NumericUpDownBackground" Color="{StaticResource ControlFillColorDefault}" />
<SolidColorBrush x:Key="NumericUpDownButtonBackground" Color="{StaticResource ControlFillColorDefault}" />
<SolidColorBrush x:Key="NumericUpDownButtonBackgroundPointerOver" Color="{StaticResource ControlFillColorSecondary}" />
<SolidColorBrush x:Key="NumericUpDownButtonBackgroundPressed" Color="{StaticResource ControlFillColorTertiary}" />
<SolidColorBrush x:Key="NumericUpDownButtonBackgroundDisabled" Color="{StaticResource ControlFillColorDisabled}" />
<SolidColorBrush x:Key="NumericUpDownButtonForeground" Color="{StaticResource TextFillColorPrimary}" />
<SolidColorBrush x:Key="NumericUpDownButtonForegroundPressed" Color="{StaticResource TextFillColorSecondary}" />
<SolidColorBrush x:Key="NumericUpDownButtonForegroundDisabled" Color="{StaticResource TextFillColorDisabled}" />

<!-- ProgressBar -->
<SolidColorBrush x:Key="ProgressBarForeground" Color="{DynamicResource SystemAccentColorPrimary}" />
<SolidColorBrush x:Key="ProgressBarBackground" Color="{StaticResource ControlStrongStrokeColorDefault}" />
Expand Down
Loading