How to use different Data Types in a Custom Control

Mesh Ka 345 Reputation points
2024-02-13T08:49:57.5066667+00:00

I have a Custom NumericUpDown control in my WPF C# .NET 8 application that is working nicely, But now i want to update it so that it can use different Numbers DataTypes like int and decimal so that i can re-use my custom NumericUpDown control instead of creating two different custom Controls just because of DataTypes.
I am really confused how to do that, Help.
Here is the Generic.xaml:

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
                    xmlns:local="clr-namespace:NumericUpDownControl">
    <Style TargetType="{x:Type local:NumericUpDown}">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type local:NumericUpDown}">
                    <Grid FlowDirection="LeftToRight" HorizontalAlignment="Stretch" VerticalAlignment="Center">
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="*" />
                            <ColumnDefinition Width="Auto" />
                        </Grid.ColumnDefinitions>
                        <TextBox x:Name="NUDTextBox"
                             Text="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Value}"
                             materialDesign:HintAssist.Hint="Height"
                             materialDesign:HintAssist.FontFamily="Century Gothic"
                             materialDesign:HintAssist.IsFloating="False"
                             Margin="0 -5 10 0"
                             FontSize="20"
                             Grid.Column="0"
                             HorizontalContentAlignment="Right"/>
                        <StackPanel Grid.Column="1" Orientation="Vertical">
                            <RepeatButton x:Name="NUDButtonUP"
                                      Background="Transparent"
                                      BorderThickness="0"
                                      Height="15"
                                      Width="15"
                                      VerticalAlignment="Top">
                                <RepeatButton.Template>
                                    <ControlTemplate TargetType="RepeatButton">
                                        <materialDesign:PackIcon Kind="Triangle"
                                                             Height="10"
                                                             Width="10"
                                                             Margin="0,3,0,0"
                                                             Foreground="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Foreground}"/>
                                        <ControlTemplate.Triggers>
                                            <Trigger Property="IsMouseOver" Value="True">
                                                <Setter Property="Foreground" Value="Blue" />
                                            </Trigger>
                                            <Trigger Property="IsPressed" Value="True">
                                                <Setter Property="Foreground" Value="Red" />
                                            </Trigger>
                                            <Trigger Property="IsEnabled" Value="False">
                                                <Setter Property="Foreground" Value="Gray" />
                                            </Trigger>
                                        </ControlTemplate.Triggers>
                                    </ControlTemplate>
                                </RepeatButton.Template>
                                <RepeatButton.Style>
                                    <Style TargetType="RepeatButton">
                                        <Setter Property="Foreground" Value="Lime" />
                                    </Style>
                                </RepeatButton.Style>
                            </RepeatButton>
 
                            <RepeatButton x:Name="NUDButtonDown"
                                      Background="Transparent"
                                      BorderThickness="0"
                                      Height="15"
                                      Width="15"
                                      VerticalAlignment="Top">
                                <RepeatButton.Template>
                                    <ControlTemplate TargetType="RepeatButton">
                                        <materialDesign:PackIcon Kind="TriangleDown"
                                                             Height="10"
                                                             Width="10"
                                                             Margin="0,0,0,0"
                                                             Foreground="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Foreground}" />
                                        <ControlTemplate.Triggers>
                                            <Trigger Property="IsMouseOver" Value="True">
                                                <Setter Property="Foreground" Value="Blue" />
                                            </Trigger>
                                            <Trigger Property="IsPressed" Value="True">
                                                <Setter Property="Foreground" Value="Red" />
                                            </Trigger>
                                            <Trigger Property="IsEnabled" Value="False">
                                                <Setter Property="Foreground" Value="Gray" />
                                            </Trigger>
                                        </ControlTemplate.Triggers>
                                    </ControlTemplate>
                                </RepeatButton.Template>
                                <RepeatButton.Style>
                                    <Style TargetType="RepeatButton">
                                        <Setter Property="Foreground" Value="Lime" />
                                    </Style>
                                </RepeatButton.Style>
                            </RepeatButton>
                        </StackPanel>
                    </Grid>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</ResourceDictionary>

 And Here is the NumericUpDown.cs:

using System;
using System.Reflection;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Input;
 
namespace NumericUpDownControl
{
    public class NumericUpDown : Control
    {
        private TextBox NUDTextBox;
        private RepeatButton NUDButtonUP;
        private RepeatButton NUDButtonDown;
 
        static NumericUpDown()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(NumericUpDown), new FrameworkPropertyMetadata(typeof(NumericUpDown)));
        }
 
        public static readonly DependencyProperty ValueProperty =
            DependencyProperty.Register("Value", typeof(double), typeof(NumericUpDown), new FrameworkPropertyMetadata(1.0, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));
 
        public double Value
        {
            get { return (double)GetValue(ValueProperty); }
            set { SetValue(ValueProperty, value); }
        }
 
        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();
 
            NUDTextBox = GetTemplateChild("NUDTextBox") as TextBox;
            NUDButtonUP = GetTemplateChild("NUDButtonUP") as RepeatButton;
            NUDButtonDown = GetTemplateChild("NUDButtonDown") as RepeatButton;
 
            if (NUDTextBox != null)
            {
                NUDTextBox.PreviewKeyDown += NUDTextBox_PreviewKeyDown;
                NUDTextBox.PreviewKeyUp += NUDTextBox_PreviewKeyUp;
                NUDTextBox.TextChanged += NUDTextBox_TextChanged;
            }
 
            if (NUDButtonUP != null)
            {
                NUDButtonUP.Click += NUDButtonUP_Click;
            }
 
            if (NUDButtonDown != null)
            {
                NUDButtonDown.Click += NUDButtonDown_Click;
            }
        }
 
        private void NUDButtonUP_Click(object sender, RoutedEventArgs e)
        {
            double number;
            if (NUDTextBox.Text != "") number = Convert.ToDouble(NUDTextBox.Text);
            else number = 0;
 
            if (number < MaxValue)
                NUDTextBox.Text = Convert.ToString(number + IncrementValue);
            else
                NUDButtonUP.IsEnabled = false;
 
            ValidateValue();
        }
 
        private void NUDButtonDown_Click(object sender, RoutedEventArgs e)
        {
            double number;
            if (NUDTextBox.Text != "") number = Convert.ToDouble(NUDTextBox.Text);
            else number = 0;
            if (number > MinValue)
                NUDTextBox.Text = Convert.ToString(number - IncrementValue);
            else
                NUDButtonUP.IsEnabled = false;
 
            ValidateValue();
        }
 
        private void NUDTextBox_PreviewKeyDown(object sender, KeyEventArgs e)
        {
            if (e.Key == Key.Up)
            {
                NUDButtonUP.RaiseEvent(new RoutedEventArgs(Button.ClickEvent));
                typeof(Button).GetMethod("set_IsPressed", BindingFlags.Instance | BindingFlags.NonPublic).Invoke(NUDButtonUP, new object[] { true });
            }
 
            if (e.Key == Key.Down)
            {
                NUDButtonDown.RaiseEvent(new RoutedEventArgs(Button.ClickEvent));
                typeof(Button).GetMethod("set_IsPressed", BindingFlags.Instance | BindingFlags.NonPublic).Invoke(NUDButtonDown, new object[] { true });
            }
 
        }
 
        private void NUDTextBox_PreviewKeyUp(object sender, KeyEventArgs e)
        {
            if (e.Key == Key.Up)
                typeof(Button).GetMethod("set_IsPressed", BindingFlags.Instance | BindingFlags.NonPublic).Invoke(NUDButtonUP, new object[] { false });
 
            if (e.Key == Key.Down)
                typeof(Button).GetMethod("set_IsPressed", BindingFlags.Instance | BindingFlags.NonPublic).Invoke(NUDButtonDown, new object[] { false });
        }
 
        private void NUDTextBox_TextChanged(object sender, TextChangedEventArgs e)
        {
            ValidateValue();
            NUDTextBox.SelectionStart = NUDTextBox.Text.Length;
        }
 
        private void ValidateValue()
        {
            double number = 0;
            if (NUDTextBox.Text != "")
                if (!double.TryParse(NUDTextBox.Text, out number)) NUDTextBox.Text = MinValue.ToString();
            if (number >= MaxValue)
            {
                NUDTextBox.Text = MaxValue.ToString();
                NUDButtonUP.IsEnabled = false; // disable UP button
            }
            else
            {
                NUDButtonUP.IsEnabled = true; // enable UP button
            }
            if (number <= MinValue)
            {
                NUDTextBox.Text = MinValue.ToString();
                NUDButtonDown.IsEnabled = false; // disable DOWN button
            }
            else
            {
                NUDButtonDown.IsEnabled = true; // enable DOWN button
            }
        }
 
        public double MinValue { get; set; } = 1.0;
        public double MaxValue { get; set; } = 3.0;
        public double IncrementValue { get; set; } = 0.1;
    }
}

 
and this is an example of how i wanted to use it:

<Window x:Class="NumericUpDownControl.MainWindow"
        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"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:NumericUpDownControl"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Grid>
        <StackPanel>
            <local:NumericUpDown 
                                Value="1.3"
                                MinValue="1.0"
                                MaxValue="3.0"
                                IncrementValue="0.1"
                                />
            <local:NumericUpDown 
                                Value="2"
                                MinValue="1"
                                MaxValue="10"
                                IncrementValue="1"
                                />
        </StackPanel>
    </Grid>
</Window>
.NET
.NET
Microsoft Technologies based on the .NET software framework.
3,401 questions
Windows Presentation Foundation
Windows Presentation Foundation
A part of the .NET Framework that provides a unified programming model for building line-of-business desktop applications on Windows.
2,678 questions
C#
C#
An object-oriented and type-safe programming language that has its roots in the C family of languages and includes support for component-oriented programming.
10,291 questions
XAML
XAML
A language based on Extensible Markup Language (XML) that enables developers to specify a hierarchy of objects with a set of properties and logic.
767 questions
{count} votes