154

Getting into the first serious WPF project. It seems like there are a lot of basic controls flat out missing. Specifically, I am looking for the Numeric UpDown control. Was there an out of band release that I missed? Really don't feel like writing my own control.

I do not want to use the WindowsFormHost and plop a WinForm ctl on it. I want it to be fully WPF without any legacy junk.

Thanks

H H
  • 263,252
  • 30
  • 330
  • 514
AngryHacker
  • 59,598
  • 102
  • 325
  • 594
  • Thanks a lot. I saw this one as well. Kevin has a pretty amazing collection. – AngryHacker May 08 '09 at 20:01
  • For reference the control source code for Kevin's is [here](https://github.com/thinkpixellab/bot/blob/master/net40-client/Bot/NumericUpDown.cs). – Jon Peterson Jan 04 '13 at 22:50
  • 27
    This single comment is the biggest driver of traffic to my blog for all time. Even to this day. Hilarious. – Kevin Moore Jan 17 '13 at 03:04
  • 5
    The Extented WPF Toolkit has one: [NumericUpDown](http://wpftoolkit.codeplex.com/wikipage?title=NumericUpDown&referringTitle=Home) ![alt text](http://i.stack.imgur.com/GUAcd.png) – Eduardo Molteni Nov 19 '10 at 09:53
  • 1
    possible duplicate of [Good NumericUpDown equivalent in WPF?](http://stackoverflow.com/questions/382676/good-numericupdown-equivalent-in-wpf) – J c Feb 20 '14 at 07:39
  • 4
    The question is "where is the WPF numeric UpDown control". None of these answers answer that question. I would expect the kind of answer to be of the form "they decided to get rid of it in wpf because ... ". I do not know this answer, however – Assimilater Jul 05 '17 at 19:23
  • 1
    @Assimilater I would also be interested to know why Microsoft decided to not implement this control for WPF, it seems like it would be something a lot of users would be familiar with, and familiarity can be important for user interface design. – jrh Nov 13 '17 at 13:49
  • 5
    The WPF Toolkit makes way more problems than it solves, it contains quite a few obvious bugs; and there is no NumericUpDown control from Microsoft... hell, is WPF even alive anymore? – j00hi Jan 04 '18 at 00:52

16 Answers16

65

Simply use the IntegerUpDown control in the Extended.Wpf.Toolkit You can use it like this:

  1. Add to your XAML the following namespace:

    xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit"

  2. In your XAML where you want the control use:

    <xctk:IntegerUpDown Name="myUpDownControl" />

Zonus
  • 2,313
  • 2
  • 26
  • 48
Stacked
  • 6,892
  • 7
  • 57
  • 73
  • 8
    For those wondering / not comfortable with third party closed source controls, note that the free version of this library is released under the Microsoft Public License, which basically means you get the full source code behind it (read the full license text for details). The UpDown controls are part of the free version. There is also a paid version of the library that contains a lot more controls / themes. – jrh Nov 13 '17 at 14:01
  • Is there a way to have these custom WPF controls show up in the VS Toolbox for convenience? I couldn't find them under 'Tools -> Choose Toolbox Items'. – aviator Mar 20 '18 at 20:10
  • 7
    It should be noted that starting with version v3.7.0, the open sourced version of wpftoolkit is provided under a proprietary non-commercial license rather than MS-PL ([reference](https://github.com/xceedsoftware/wpftoolkit/wiki/Improvements-in-v3.7.0)) – Jordoff Dec 19 '19 at 00:30
  • 2
    Xceed updated their announcement to say that [https://github.com/xceedsoftware/wpftoolkit](they will continue to publish version 3.8.x under the MS-PL until 31 Dec 2020). "This will be the last version to be released under such license, and the next release will be under our new community license, which only allows non-commercial use." – skst Oct 19 '20 at 01:45
  • Xceed are charging money for the PLUS version (Which supports .net core 3) – Rafael Herscovici Mar 05 '21 at 10:24
  • 5
    Xceed have changed their licencing since V4 now must be <10 end users! – Sam Mackrill Feb 18 '22 at 11:31
  • `The name "IntegerUpDown" does not exist in the namespace "http://schemas.xceed.com/wpf/xaml/toolkit".` – Dominique Apr 18 '23 at 12:08
53

I made my own;

the xaml

<Grid  Height="23" Margin="152,63,11,0" VerticalAlignment="Top">
    <TextBox x:Name="txtNum" x:FieldModifier="private" Text="0" TextChanged="txtNum_TextChanged" Margin="3,2,13,3" />
    <Button x:Name="cmdUp" x:FieldModifier="private" FontSize="10" Padding="0,-4,0,0" Content="▲" Width="10" Click="cmdUp_Click" Margin="33,2,1,13" />
    <Button x:Name="cmdDown" x:FieldModifier="private" FontSize="10" Padding="0,-4,0,0" Content="▼" Width="10" Click="cmdDown_Click" Margin="33,12,1,3" />
</Grid>

and the code behind

private int _numValue = 0;

public int NumValue
{
    get {  return _numValue; }
    set
    {
        _numValue = value;
        txtNum.Text = value.ToString();
    }
}

public NumberUpDown()
{
    InitializeComponent();
    txtNum.Text = _numValue.ToString();
}

private void cmdUp_Click(object sender, RoutedEventArgs e)
{
    NumValue++;
}

private void cmdDown_Click(object sender, RoutedEventArgs e)
{
    NumValue--;
}

private void txtNum_TextChanged(object sender, TextChangedEventArgs e)
{
    if (txtNum == null)
    {
        return;
    }

    if (!int.TryParse(txtNum.Text, out _numValue))
        txtNum.Text = _numValue.ToString();
}
T.Todua
  • 53,146
  • 19
  • 236
  • 237
Michael
  • 641
  • 5
  • 4
  • the textChanged event does not exist for a textbox in WPF .net 4 – AliR Oct 05 '13 at 06:29
  • This method has some limitations, 1) the TextBox does not stretch horizontally with the control like the Winforms one does, and less importantly, 2) unlike the Winforms NumericUpDown, this control allows vertical stretching, which is harmless but it [looks a bit silly](https://i.stack.imgur.com/O0Mo0.png). Note that the solution in [Sonorx's answer](https://stackoverflow.com/a/5321605/4975230) does not have these issues. – jrh Nov 13 '17 at 13:54
  • 4
    NumValue should be a dependency property to allow binding, also other properties like MinValue and MaxValue would be nice. – Konrad Jul 11 '19 at 12:09
  • 1
    This solution is lame. It's a custom control with no support for wpf data binding, routed events and commands. – Xam Nov 13 '19 at 23:33
  • Very WPF-ish :) – Alexandru Dicu Feb 26 '21 at 11:10
22

This is example of my own UserControl with Up and Down key catching.

Xaml code:

<Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="*" />
        <ColumnDefinition Width="13" />
    </Grid.ColumnDefinitions>
    <Grid.RowDefinitions>
        <RowDefinition Height="13" />
        <RowDefinition Height="13" />
    </Grid.RowDefinitions>
    <TextBox Name="NUDTextBox"  Grid.Column="0" Grid.Row="0" Grid.RowSpan="2" TextAlignment="Right" PreviewKeyDown="NUDTextBox_PreviewKeyDown" PreviewKeyUp="NUDTextBox_PreviewKeyUp" TextChanged="NUDTextBox_TextChanged"/>
    <RepeatButton Name="NUDButtonUP"  Grid.Column="1" Grid.Row="0" FontSize="8" FontFamily="Marlett" VerticalContentAlignment="Center" HorizontalContentAlignment="Center" Click="NUDButtonUP_Click">5</RepeatButton>
    <RepeatButton Name="NUDButtonDown"  Grid.Column="1" Grid.Row="1" FontSize="8"  FontFamily="Marlett" VerticalContentAlignment="Center" HorizontalContentAlignment="Center" Height="13" VerticalAlignment="Bottom" Click="NUDButtonDown_Click">6</RepeatButton>
</Grid>

And the code:

public partial class NumericUpDown : UserControl
{
    int minvalue = 0, 
        maxvalue = 100,
        startvalue = 10;
    public NumericUpDown()
    {
        InitializeComponent();
        NUDTextBox.Text = startvalue.ToString();
    }

    private void NUDButtonUP_Click(object sender, RoutedEventArgs e)
    {
        int number;
        if (NUDTextBox.Text != "") number = Convert.ToInt32(NUDTextBox.Text);
        else number = 0;
        if (number < maxvalue)
            NUDTextBox.Text = Convert.ToString(number + 1); 
    }

    private void NUDButtonDown_Click(object sender, RoutedEventArgs e)
    {
        int number;
        if (NUDTextBox.Text != "") number = Convert.ToInt32(NUDTextBox.Text);
        else number = 0;
        if (number > minvalue)
            NUDTextBox.Text = Convert.ToString(number - 1); 
    }

    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)
    {
        int number = 0;
        if (NUDTextBox.Text!="")
            if (!int.TryParse(NUDTextBox.Text, out number)) NUDTextBox.Text = startvalue.ToString();
        if (number > maxvalue)  NUDTextBox.Text = maxvalue.ToString();
        if (number < minvalue) NUDTextBox.Text = minvalue.ToString();
        NUDTextBox.SelectionStart = NUDTextBox.Text.Length;

    }

}
Sonorx
  • 1,379
  • 10
  • 17
  • 2
    Is the font "Marlett" guaranteed to exist on the user's system? – Qwertie Apr 10 '13 at 23:33
  • 1
    @Qwertie It is by default in Windows since 3.11, AFAIR. – Spook Feb 02 '15 at 11:37
  • This is a good solution and seems to work with UWP XAML too. Just had a bit of issue with the PreviewKeyDown / PreviewKeyUP methods. – raddevus Sep 14 '16 at 18:24
  • Great solution. Just add `this.DataContext = this;` to constructor and change private fields to public properties and you can fiddle with these directly in a consuming XAML (see [this howto](https://www.wpf-tutorial.com/usercontrols-and-customcontrols/creating-using-a-usercontrol/)) – z33k Nov 05 '20 at 14:33
  • 1
    You might want to use these Unicode characters instead: ▲ ▼ – Bip901 Sep 19 '21 at 14:36
12

The given answers are OK. However, I wanted the buttons to auto hide, when mouse leave the control. Here is my code based on vercin answer above:

Style

<Style TargetType="{x:Type v:IntegerTextBox}">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type v:IntegerTextBox}">
                    <Grid Background="Transparent">
                        <Grid.RowDefinitions>
                            <RowDefinition Height="*"/>
                            <RowDefinition Height="*"/>
                        </Grid.RowDefinitions>
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="*"/>
                            <ColumnDefinition Width="Auto"/>
                        </Grid.ColumnDefinitions>
                        <TextBox Name="tbmain" Grid.ColumnSpan="2" Grid.RowSpan="2"
                                 Text="{Binding Value, Mode=TwoWay, NotifyOnSourceUpdated=True, 
                            NotifyOnValidationError=True, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type v:IntegerTextBox}}}" 
                                               Style="{StaticResource ValidationStyle}" />
                        <RepeatButton Name="PART_UpButton" BorderThickness="0" Grid.Column="1" Grid.Row="0"
                                      Width="13" Background="Transparent">
                            <Path Fill="Black" Data="M 0 3 L 6 3 L 3 0 Z"/>
                        </RepeatButton>
                        <RepeatButton Name="PART_DownButton" BorderThickness="0" Grid.Column="1" Grid.Row="1"
                                      Width="13" Background="Transparent">
                            <Path Fill="Black" Data="M 0 0 L 3 3 L 6 0 Z"/>
                        </RepeatButton>

                    </Grid>
                    <ControlTemplate.Triggers>
                        <Trigger Property="IsMouseOver"  Value="False">
                            <Setter Property="Visibility" TargetName="PART_UpButton" Value="Collapsed"/>
                            <Setter Property="Visibility" TargetName="PART_DownButton" Value="Collapsed"/>
                        </Trigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

Code

public partial class IntegerTextBox : UserControl
{
    public IntegerTextBox()
    {
        InitializeComponent();
    }

    public int Maximum
    {
        get { return (int)GetValue(MaximumProperty); }
        set { SetValue(MaximumProperty, value); }
    }
    public readonly static DependencyProperty MaximumProperty = DependencyProperty.Register(
        "Maximum", typeof(int), typeof(IntegerTextBox), new UIPropertyMetadata(int.MaxValue));



    public int Minimum
    {
        get { return (int)GetValue(MinimumProperty); }
        set { SetValue(MinimumProperty, value); }
    }
    public readonly static DependencyProperty MinimumProperty = DependencyProperty.Register(
        "Minimum", typeof(int), typeof(IntegerTextBox), new UIPropertyMetadata(int.MinValue));


    public int Value
    {
        get { return (int)GetValue(ValueProperty); }
        set { SetCurrentValue(ValueProperty, value); }
    }
    public readonly static DependencyProperty ValueProperty = DependencyProperty.Register(
        "Value", typeof(int), typeof(IntegerTextBox), new UIPropertyMetadata(0, (o,e)=>
        {
            IntegerTextBox tb = (IntegerTextBox)o;
            tb.RaiseValueChangedEvent(e);
        }));

    public event EventHandler<DependencyPropertyChangedEventArgs> ValueChanged;
    private void RaiseValueChangedEvent(DependencyPropertyChangedEventArgs e)
    {
        ValueChanged?.Invoke(this, e);
    }


    public int Step
    {
        get { return (int)GetValue(StepProperty); }
        set { SetValue(StepProperty, value); }
    }
    public readonly static DependencyProperty StepProperty = DependencyProperty.Register(
        "Step", typeof(int), typeof(IntegerTextBox), new UIPropertyMetadata(1));



    RepeatButton _UpButton;
    RepeatButton _DownButton;
    public override void OnApplyTemplate()
    {
        base.OnApplyTemplate();
        _UpButton = Template.FindName("PART_UpButton", this) as RepeatButton;
        _DownButton = Template.FindName("PART_DownButton", this) as RepeatButton;
        _UpButton.Click += btup_Click;
        _DownButton.Click += btdown_Click;
    }


    private void btup_Click(object sender, RoutedEventArgs e)
    {
        if (Value < Maximum)
        {
            Value += Step;
            if (Value > Maximum)
                Value = Maximum;
        }
    }

    private void btdown_Click(object sender, RoutedEventArgs e)
    {
        if (Value > Minimum)
        {
            Value -= Step;
            if (Value < Minimum)
                Value = Minimum;
        }
    }

}
Community
  • 1
  • 1
rmojab63
  • 3,513
  • 1
  • 15
  • 28
11

Let's enjoy some hacky things:
Here is a Style of Slider as a NumericUpDown, simple and easy to use, without any hidden code or third party library.

<Style TargetType="{x:Type Slider}">
    <Style.Resources>
        <Style x:Key="RepeatButtonStyle" TargetType="{x:Type RepeatButton}">
            <Setter Property="Focusable" Value="false" />
            <Setter Property="IsTabStop" Value="false" />
            <Setter Property="Padding" Value="0" />
            <Setter Property="Width" Value="20" />
        </Style>
    </Style.Resources>
    <Setter Property="Stylus.IsPressAndHoldEnabled" Value="false" />
    <Setter Property="SmallChange" Value="1" />
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type Slider}">
                <Grid>
                    <Grid.RowDefinitions>
                        <RowDefinition />
                        <RowDefinition />
                    </Grid.RowDefinitions>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition />
                        <ColumnDefinition Width="Auto" />
                    </Grid.ColumnDefinitions>
                    <TextBox Grid.RowSpan="2" Height="Auto"
                                Margin="0" Padding="0"
                                VerticalAlignment="Stretch" VerticalContentAlignment="Center"
                                Text="{Binding Value, RelativeSource={RelativeSource Mode=TemplatedParent}, Mode=TwoWay}">
                        <TextBox.InputBindings>
                            <KeyBinding Gesture="Up" Command="{x:Static Slider.IncreaseSmall}" />
                            <KeyBinding Gesture="Down" Command="{x:Static Slider.DecreaseSmall}" />
                            <KeyBinding Gesture="PageUp" Command="{x:Static Slider.IncreaseLarge}" />
                            <KeyBinding Gesture="PageDown" Command="{x:Static Slider.DecreaseLarge}" />
                        </TextBox.InputBindings>
                    </TextBox>
                    <RepeatButton Grid.Row="0" Grid.Column="1"
                                    Command="{x:Static Slider.IncreaseSmall}"
                                    Style="{StaticResource RepeatButtonStyle}">
                        <Path Data="M4,0 L0,4 8,4 Z" Fill="Black" />
                    </RepeatButton>
                    <RepeatButton Grid.Row="1" Grid.Column="1"
                                    Command="{x:Static Slider.DecreaseSmall}"
                                    Style="{StaticResource RepeatButtonStyle}">
                        <Path Data="M0,0 L4,4 8,0 Z" Fill="Black" />
                    </RepeatButton>
                    <Border x:Name="TrackBackground" Visibility="Collapsed">
                        <Rectangle x:Name="PART_SelectionRange" Visibility="Collapsed" />
                    </Border>
                    <Thumb x:Name="Thumb" Visibility="Collapsed" />
                </Grid>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

If you want apply input restrictions on TextBox indeed, try this:

public static class InputLimit
{
    public static string GetDecimalValueProxy(TextBox obj) => (string)obj.GetValue(DecimalValueProxyProperty);

    public static void SetDecimalValueProxy(TextBox obj, string value) => obj.SetValue(DecimalValueProxyProperty, value);

    // Using a DependencyProperty as the backing store for DecimalValueProxy.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty DecimalValueProxyProperty =
        DependencyProperty.RegisterAttached("DecimalValueProxy", typeof(string), typeof(InputLimit),
            new FrameworkPropertyMetadata("0", FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, null, CoerceDecimalValueProxy));

    private static object CoerceDecimalValueProxy(DependencyObject d, object baseValue)
    {
        if (decimal.TryParse(baseValue as string, out _)) return baseValue;
        return DependencyProperty.UnsetValue;
    }
}

And modify xaml TextBox part to:

<TextBox Grid.RowSpan="2" Height="Auto"
            Margin="0" Padding="0"
            VerticalAlignment="Stretch" VerticalContentAlignment="Center"
            local:InputLimit.DecimalValueProxy="{Binding Value, RelativeSource={RelativeSource Mode=TemplatedParent}, Mode=TwoWay}"
            Text="{Binding RelativeSource={RelativeSource Self}, Path=(local:InputLimit.DecimalValueProxy), Mode=TwoWay}">

EDIT:
But the above code has a nasty bug...
Look at the code:

Text="{Binding Value, RelativeSource={RelativeSource Mode=TemplatedParent}, Mode=TwoWay}"

The TextBox will update value after it lost focus, but if you add UpdateSourceTrigger=PropertyChanged in Binding, you will cannot type dot(.) into TextBox.

You can use next converter code to fix it:

public class SafeDotConverter : MarkupExtension, IValueConverter
{
    private bool hasDot;
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        var dbl = (double)value;
        if (hasDot && Math.Truncate(dbl) == dbl)
        {
            return $"{dbl}.";
        }
        return value.ToString();
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        if (value is string str)
        {
            hasDot = str.EndsWith(".");

            if (double.TryParse(str, out var val))
                return val;
        }
        return DependencyProperty.UnsetValue;
    }

    public override object ProvideValue(IServiceProvider serviceProvider)
    {
        return this;
    }
}

Use in Xaml:

Text="{Binding Value, RelativeSource={RelativeSource Mode=TemplatedParent}, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, Converter={local:SafeDotConverter}}"
  • 2
    I am so sorry I can't upvote this for at least 10 times. Great XAML solution only. It has some limitations, but being XAML only, those limitations don't matter anymore. – Alexandru Dicu Feb 26 '21 at 11:09
  • 1
    @AlexandruDicu I've add some BLACK MAGIC make it support input restrictions. – Mr. Squirrel.Downy Jun 02 '21 at 09:39
  • The xaml solution is a real cool stuff! Handling key events is also helpful. I think it could be better if it handle mousewheel event too. – J.C Oct 13 '22 at 07:27
  • @J.C But honestly it's a very, very hacky solution, and if you just want to do a little personal gadget and not some commercial software, it's probably a good choice. But don't expect to use it on commercial software, it has a ton of logic bugs need to be care and you'll go mad. – Mr. Squirrel.Downy Oct 13 '22 at 08:53
  • @J.C and if you want it work with mouse wheel, you can add a `MouseBinding` in `InputBindings` and read this: https://stackoverflow.com/questions/2271342/mousebinding-the-mousewheel-to-zoom-in-wpf-and-mvvm – Mr. Squirrel.Downy Oct 13 '22 at 09:04
  • @Mr.Squirrel.Downy Thanks for advice, I use this solution on my experimental project right now, and I'll beware of that. – J.C Oct 14 '22 at 09:15
10
<ResourceDictionary
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:numericButton2">


    <Style TargetType="{x:Type local:NumericUpDown}">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type local:NumericUpDown}">              
                        <Grid>
                            <Grid.RowDefinitions>
                                <RowDefinition Height="*"/>
                                <RowDefinition Height="*"/>
                                <RowDefinition Height="*"/>
                            </Grid.RowDefinitions>
                            <RepeatButton Grid.Row="0" Name="Part_UpButton"/>
                            <ContentPresenter Grid.Row="1"></ContentPresenter>
                            <RepeatButton Grid.Row="2" Name="Part_DownButton"/>
                        </Grid>                  
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</ResourceDictionary>

    <Window x:Class="numericButton2.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:local="clr-namespace:numericButton2"
            Title="MainWindow" Height="350" Width="525">
        <Grid>
            <local:NumericUpDown Margin="181,94,253,161" x:Name="ufuk" StepValue="4" Minimum="0" Maximum="20">            
            </local:NumericUpDown>
            <TextBlock Margin="211,112,279,0" Text="{Binding ElementName=ufuk, Path=Value}" Height="20" VerticalAlignment="Top"></TextBlock>
        </Grid>
    </Window>
public class NumericUpDown : Control
{
    private RepeatButton _UpButton;
    private RepeatButton _DownButton;
    public readonly static DependencyProperty MaximumProperty;
    public readonly static DependencyProperty MinimumProperty;
    public readonly static DependencyProperty ValueProperty;
    public readonly static DependencyProperty StepProperty;   
    static NumericUpDown()
    {
        DefaultStyleKeyProperty.OverrideMetadata(typeof(NumericUpDown), new FrameworkPropertyMetadata(typeof(NumericUpDown)));
        MaximumProperty = DependencyProperty.Register("Maximum", typeof(int), typeof(NumericUpDown), new UIPropertyMetadata(10));
        MinimumProperty = DependencyProperty.Register("Minimum", typeof(int), typeof(NumericUpDown), new UIPropertyMetadata(0));
        StepProperty = DependencyProperty.Register("StepValue", typeof(int), typeof(NumericUpDown), new FrameworkPropertyMetadata(5));
        ValueProperty = DependencyProperty.Register("Value", typeof(int), typeof(NumericUpDown), new FrameworkPropertyMetadata(0));
    }
    #region DpAccessior
    public int Maximum
    {
        get { return (int)GetValue(MaximumProperty); }
        set { SetValue(MaximumProperty, value); }
    }
    public int Minimum
    {
        get { return (int)GetValue(MinimumProperty); }
        set { SetValue(MinimumProperty, value); }
    }
    public int Value
    {
        get { return (int)GetValue(ValueProperty); }
        set { SetCurrentValue(ValueProperty, value); }
    }
    public int StepValue
    {
        get { return (int)GetValue(StepProperty); }
        set { SetValue(StepProperty, value); }
    }
    #endregion
    public override void OnApplyTemplate()
    {
        base.OnApplyTemplate();
        _UpButton = Template.FindName("Part_UpButton", this) as RepeatButton;
        _DownButton = Template.FindName("Part_DownButton", this) as RepeatButton;
        _UpButton.Click += _UpButton_Click;
        _DownButton.Click += _DownButton_Click;
    }

    void _DownButton_Click(object sender, RoutedEventArgs e)
    {
        if (Value > Minimum)
        {
            Value -= StepValue;
            if (Value < Minimum)
                Value = Minimum;
        }
    }

    void _UpButton_Click(object sender, RoutedEventArgs e)
    {
        if (Value < Maximum)
        {
            Value += StepValue;
            if (Value > Maximum)
                Value = Maximum;
        }
    }

}
Roger Far
  • 2,178
  • 3
  • 36
  • 67
vercin
  • 210
  • 4
  • 12
  • 1
    OK.... very very late to this party, but stumbled on it, and just in case... this way, if someone binds to the Value property of your NumericUpDown, your setting the Value in the button handlers will break the binding, unless they have made the binding mode TwoWay. It's easier for the user of this control if you make the Value Property's metadata bind two-way by default, e.g. at registration time with 'new FrameworkPropertyMetadata(0, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault)'. – J S Mar 22 '21 at 23:12
  • Why can't _UpButton and _DownButton just be the names of the RepeatButtons in XAML? Why must they be defined as members in code-behind? – Eric Eggers Jul 11 '23 at 23:56
7

You can use NumericUpDown control for WPF written by me as a part of WPFControls library.

Alex Titarenko
  • 571
  • 7
  • 9
  • 1
    A nice little library and shows how a numeric up/down control should be written. Only issue I had was having to re-reference the controls assembly in the demo and install MSHTML from nuget. Thanks! – sergeantKK Mar 07 '18 at 10:48
  • 1
    Great job with the library. Very useful. Thanks. – namg_engr Mar 23 '18 at 04:31
  • 1
    A very useful library that easily transfers to .NETCore 3.1. Clearly written by someone with a good understanding of the inner workings of wpf. Saved me many hours of work. Thanks. – Alan Wayne Jun 18 '22 at 14:31
6

Use VerticalScrollBar with the TextBlock control in WPF. In your code behind, add the following code:

In the constructor, define an event handler for the scrollbar:

scrollBar1.ValueChanged += new RoutedPropertyChangedEventHandler<double>(scrollBar1_ValueChanged);
scrollBar1.Minimum = 0;
scrollBar1.Maximum = 1;
scrollBar1.SmallChange = 0.1;

Then in the event handler, add:

void scrollBar1_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
{
    FteHolderText.Text = scrollBar1.Value.ToString();
}

Here is the original snippet from my code... make necessary changes.. :)

public NewProjectPlan()
{
    InitializeComponent();

    this.Loaded += new RoutedEventHandler(NewProjectPlan_Loaded);

    scrollBar1.ValueChanged += new RoutedPropertyChangedEventHandler<double>(scrollBar1_ValueChanged);
    scrollBar1.Minimum = 0;
    scrollBar1.Maximum = 1;
    scrollBar1.SmallChange = 0.1;

    // etc...
}

void scrollBar1_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
{
    FteHolderText.Text = scrollBar1.Value.ToString();
}
g t
  • 7,287
  • 7
  • 50
  • 85
Abhinav Wase
  • 61
  • 1
  • 1
  • 1
    your solution is very smart, however the buttons are working in reverse order (button up decreases the value while button down increases it). I've found another thread that may be a help for solving that issue: https://stackoverflow.com/a/27246296/637968 - just rotate the scrollbar – Mike Jun 12 '18 at 10:21
5

Apologize for keep answering 9 years questions.

I have follow @Michael's answer and it works.

I do it as UserControl where I can drag and drop like a Controls elements. I use MaterialDesign Theme from Nuget to get the Chevron icon and button ripple effect.

The running NumericUpDown from Micheal with modification will be as below:-

enter image description here

The code for user control:-

TemplateNumericUpDown.xaml

<UserControl x:Class="UserControlTemplate.TemplateNumericUpDown"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:UserControlTemplate"
             xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
             mc:Ignorable="d" MinHeight="48">
    <Grid Background="{DynamicResource {x:Static SystemColors.WindowFrameBrushKey}}">
        <Grid.ColumnDefinitions>
            <ColumnDefinition/>
            <ColumnDefinition Width="60"/>
        </Grid.ColumnDefinitions>
        <TextBox x:Name="txtNum" x:FieldModifier="private" Text="{Binding Path=NumValue}" TextChanged="TxtNum_TextChanged" FontSize="36" BorderThickness="0" VerticalAlignment="Center" Padding="5,0"/>
        <Grid Grid.Column="1">
            <Grid.RowDefinitions>
                <RowDefinition Height="30*"/>
                <RowDefinition Height="30*"/>
            </Grid.RowDefinitions>
            <Grid Background="#FF673AB7">
                <Viewbox HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Height="Auto" Width="Auto">
                    <materialDesign:PackIcon Kind="ChevronUp" Foreground="White" Height="32.941" Width="32"/>
                </Viewbox>
                <Button x:Name="cmdUp" x:FieldModifier="private" Click="CmdUp_Click" Height="Auto" BorderBrush="{x:Null}" Background="{x:Null}"/>
            </Grid>
            <Grid Grid.Row="1" Background="#FF673AB7">
                <Viewbox HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Height="Auto" Width="Auto">
                    <materialDesign:PackIcon Kind="ChevronDown" Foreground="White" Height="32.942" Width="32"/>
                </Viewbox>
                <Button x:Name="cmdDown" x:FieldModifier="private" Click="CmdDown_Click" Height="Auto" BorderBrush="{x:Null}" Background="{x:Null}"/>
            </Grid>
        </Grid>
    </Grid>
</UserControl>

TemplateNumericUpDown.cs

using System.Windows;
using System.Windows.Controls;

namespace UserControlTemplate
{
    /// <summary>
    /// Interaction logic for TemplateNumericUpDown.xaml
    /// </summary>
    public partial class TemplateNumericUpDown : UserControl
    {
        private int _numValue = 0;
        public TemplateNumericUpDown()
        {
            InitializeComponent();
            txtNum.Text = _numValue.ToString();
        }
        public int NumValue
        {
            get { return _numValue; }
            set
            {
                if (value >= 0)
                {
                    _numValue = value;
                    txtNum.Text = value.ToString();
                }
            }
        }

        private void CmdUp_Click(object sender, RoutedEventArgs e)
        {
            NumValue++;
        }

        private void CmdDown_Click(object sender, RoutedEventArgs e)
        {
            NumValue--;
        }

        private void TxtNum_TextChanged(object sender, TextChangedEventArgs e)
        {
            if (txtNum == null)
            {
                return;
            }

            if (!int.TryParse(txtNum.Text, out _numValue))
                txtNum.Text = _numValue.ToString();
        }
    }
}

On MyPageDesign.xaml, drag and drop created usercontrol will having <UserControlTemplate:TemplateNumericUpDown HorizontalAlignment="Left" Height="100" VerticalAlignment="Top" Width="100"/>

enter image description here

To get the value from the template, I use

string Value1 = JournalNumStart.NumValue;
string Value2 = JournalNumEnd.NumValue;

I'm not in good skill yet to binding the Height of the control based from FontSize element, so I set the from my page fontsize manually in usercontrol.

** Note:- I have change the "Archieve" name to Archive on my program =)

Palle Due
  • 5,929
  • 4
  • 17
  • 32
Luiey
  • 843
  • 2
  • 23
  • 50
  • Usability of up/down buttons is horrible: they are too small to be handy. They need to be AT LEAST same height as the text box (and be square). I know it takes a bit more space, but it gives you AT LEAST usable buttons. Otherwise there is no reason to insert 'em at all. – Vincent Nov 08 '19 at 10:45
4

Here is another open source control that has many different input methods (mouse drag, mouse wheel, cursor keys, textbox editing), supports many data types and use cases:

https://github.com/Dirkster99/NumericUpDownLib

user8276908
  • 1,051
  • 8
  • 20
3

I have a naive solution but useful. Here is the code:

<Grid Name="TVGrid" Background="#7F000000">  <ScrollBar Background="Black" Orientation="Vertical" Height="35" HorizontalAlignment="Left" Margin="215,254,0,0" Minimum="0" Maximum="10" LargeChange="10" Value="{Binding ElementName=channeltext2, Path=Text}" x:Name="scroll" VerticalAlignment="Top" Width="12" RenderTransformOrigin="0.5,0.5" ValueChanged="scroll_ValueChanged" >  
        <ScrollBar.RenderTransform>  
            <TransformGroup>  
                <ScaleTransform/>  
                <SkewTransform/>  
                <RotateTransform Angle="-180"/>  
                <TranslateTransform/>  
            </TransformGroup>  
        </ScrollBar.RenderTransform>  
    </ScrollBar>  
    <TextBox Name="channeltext" HorizontalContentAlignment="Center" FontSize="20"  Background="Black" Foreground="White" Height="35" HorizontalAlignment="Left" Margin="147,254,0,0" VerticalAlignment="Top" Width="53" Text="0" />  
    <TextBox Name="channeltext2" Visibility="Hidden" HorizontalContentAlignment="Center" FontSize="20"  Background="Black" Foreground="White" Height="35" HorizontalAlignment="Left" Margin="147,254,0,0" VerticalAlignment="Top" Width="53" Text="0" />  </Grid>  
3

This is a modification of another answer but with binding support

<UserControl x:Class="YourNamespace.Controls.NumericUpDown"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             xmlns:local="clr-namespace:NetReactorLM.Desktop.Controls"
             mc:Ignorable="d"
             d:DesignHeight="300" d:DesignWidth="300">
    <Grid  VerticalAlignment="Top">
        <TextBox x:Name="txtNum" x:FieldModifier="private" Text="0" TextChanged="txtNum_TextChanged" Margin="3,2,13,3" />
        <Button x:Name="cmdUp" x:FieldModifier="private" FontSize="10" Padding="0,-4,0,0" Content="▲" Width="10" Click="cmdUp_Click" Margin="33,2,1,13" />
        <Button x:Name="cmdDown" x:FieldModifier="private" FontSize="10" Padding="0,-4,0,0" Content="▼" Width="10" Click="cmdDown_Click" Margin="33,12,1,3" />
    </Grid>
</UserControl>


using System.Windows;
using System.Windows.Controls;

namespace YourNamespace.Controls
{
    public partial class NumericUpDown : UserControl
    {

        public static readonly DependencyProperty ValueProperty = DependencyProperty.Register(
            "Value", typeof(int), typeof(NumericUpDown), new PropertyMetadata(default(int)));

        public int Value
        {
            get { return (int) GetValue(ValueProperty); }
            set
            {
                SetValue(ValueProperty, value); 
                txtNum.Text = value.ToString();
            }
        }
        
        public NumericUpDown()
        {
            InitializeComponent();
            txtNum.Text = Value.ToString();
        }

        private void cmdUp_Click(object sender, RoutedEventArgs e)
        {
            Value++;
        }

        private void cmdDown_Click(object sender, RoutedEventArgs e)
        {
            Value--;
        }

        private void txtNum_TextChanged(object sender, TextChangedEventArgs e)
        {
            if (txtNum == null)
            {
                return;
            }

            if (!int.TryParse(txtNum.Text, out var val))
            {
                Value = val;
                txtNum.Text = val.ToString();
            }
        }
    }
}
  • `if (txtNum == null)` why checking this? if the textbox is null then how its `TextChanged` can fire? – Hossein Ebrahimi Jun 01 '21 at 20:06
  • @HosseinEbrahimi, I use ReactiveUI, so events can be fired from anywhere, even if the entire pipeline was null, so I persisted this check because without it I get null reference exception, also static analyser can not understand the relation between changed event and the object itself – Al Banna Techno logy Jun 02 '21 at 20:04
2

Just a pragmatic to do sample:

-Right click your Project (under Solution), select "Manage nuget Packages..."

-In Menu click Browse Tab search for "wpftoolkit", select "Extended.Wpf.Toolkit"

-Install it!

-Right click in your User Control Toolbox, select "Add Tab.." and name it "WPF Toolkit"

-Right click on the new "WPF Toolkit" Tab, select "Choose items..."

-In Menu click "Browse..." Button, look for nugets DLL folder, select all "...\packages\Extended.Wpf.Toolkit.3.5.0\lib\net40\*.dll"

Ignore Warnings about some DLLs may not containing user controls!

Ready :)

thewhiteambit
  • 1,365
  • 16
  • 31
  • 2
    Extended.Wpf.Toolkit is now only for "non-commercial" use. See the license here: https://github.com/xceedsoftware/wpftoolkit/blob/master/license.md. – Palle Due Sep 29 '20 at 11:57
  • Afaik the Extended.Wpf.Toolkit still has a problem with x64 visual studio layout editor that remains far to long. So be aware of that and a new license before using it. – thewhiteambit Nov 15 '20 at 17:01
  • The usage of VisualStudio 2022 (what is first x64 VS) can reduce the pain caused by Extended.Wpf.Toolkit for x64 builds, since the error does not pop to the surface anymore. Anyhow, this is a workaround and the bug should really be fixed in the source. – thewhiteambit Jan 05 '22 at 05:57
2

Go to NugetPackage manager of you project-> Browse and search for mahApps.Metro -> install package into you project. You will see Reference added: MahApps.Metro. Then in you XAML code add:

"xmlns:mah="http://metro.mahapps.com/winfx/xaml/controls"

Where you want to use your object add:

<mah:NumericUpDown x:Name="NumericUpDown" ... /> 

Enjoy the full extensibility of the object (Bindings, triggers and so on...).

Andre
  • 21
  • 1
2

You can use "NumericUpDown".

<mah:NumericUpDown MinWidth="70" Height="35" Minimum="0" Maximum="10000" Interval="1" />

Here is the link https://mahapps.com/docs/controls/numericupdown

luqman ahmad
  • 171
  • 1
  • 7
1

Free version of Extended.Wpf.Toolkit doesn't support .net core and .net

You can use HandyControl, it has a NumericUpDown control and works good in Wpf and .net 5.

https://github.com/HandyOrg/HandyControl

obnews
  • 602
  • 5
  • 13