Where is the WPF Numeric UpDown control?
Asked Answered
B

16

156

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

Buccaneer answered 8/5, 2009 at 18:57 Comment(8)
Thanks a lot. I saw this one as well. Kevin has a pretty amazing collection.Buccaneer
The Extented WPF Toolkit has one: NumericUpDown !alt textDiphosgene
For reference the control source code for Kevin's is here.Propinquity
This single comment is the biggest driver of traffic to my blog for all time. Even to this day. Hilarious.Psychobiology
possible duplicate of Good NumericUpDown equivalent in WPF?Balkan
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, howeverFishery
@Fishery 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.Childlike
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?Salto
H
68

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" />

Herv answered 18/2, 2013 at 16:1 Comment(7)
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.Childlike
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'.Holarctic
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)Gettogether
Xceed updated their announcement to say that [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."Potbelly
Xceed are charging money for the PLUS version (Which supports .net core 3)Beret
Xceed have changed their licencing since V4 now must be <10 end users!Eraser
The name "IntegerUpDown" does not exist in the namespace "http://schemas.xceed.com/wpf/xaml/toolkit".Cockiness
P
55

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();
}
Pizza answered 2/5, 2010 at 5:32 Comment(5)
the textChanged event does not exist for a textbox in WPF .net 4Godbey
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. Note that the solution in Sonorx's answer does not have these issues.Childlike
NumValue should be a dependency property to allow binding, also other properties like MinValue and MaxValue would be nice.Smashing
This solution is lame. It's a custom control with no support for wpf data binding, routed events and commands.Hel
Very WPF-ish :)Paintbox
P
24

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;

    }

}
Partial answered 16/3, 2011 at 5:59 Comment(5)
Is the font "Marlett" guaranteed to exist on the user's system?Abruption
@Abruption It is by default in Windows since 3.11, AFAIR.Farriery
This is a good solution and seems to work with UWP XAML too. Just had a bit of issue with the PreviewKeyDown / PreviewKeyUP methods.Muth
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)Pleach
You might want to use these Unicode characters instead: ▲ ▼Dallapiccola
J
13

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}}"
Jive answered 4/9, 2020 at 2:58 Comment(6)
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.Paintbox
@AlexandruDicu I've add some BLACK MAGIC make it support input restrictions.Jive
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.Chimp
@Chimp 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.Jive
@Chimp and if you want it work with mouse wheel, you can add a MouseBinding in InputBindings and read this: #2271842Jive
@Mr.Squirrel.Downy Thanks for advice, I use this solution on my experimental project right now, and I'll beware of that.Chimp
J
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;
        }
    }

}
Jolin answered 29/4, 2017 at 21:1 Comment(1)
The best answer here. Proper use of dependency properties.Smashing
D
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;
        }
    }

}
Doubleness answered 31/8, 2012 at 10:27 Comment(2)
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)'.Mckay
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?Jongjongleur
F
8

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

Fatimafatimah answered 17/2, 2015 at 22:52 Comment(3)
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!Wool
Great job with the library. Very useful. Thanks.Idolatrize
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.Isotonic
A
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();
}
Anhydrous answered 22/9, 2012 at 19:20 Comment(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://mcmap.net/q/152865/-flip-scrollbar-wpf - just rotate the scrollbarLevelheaded
H
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 =)

Heartily answered 16/1, 2019 at 5:17 Comment(1)
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.Meaningless
I
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

Irrelevance answered 21/2, 2019 at 19:39 Comment(0)
E
4

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();
            }
        }
    }
}
Encumber answered 30/4, 2021 at 22:0 Comment(3)
if (txtNum == null) why checking this? if the textbox is null then how its TextChanged can fire?Snide
@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 itselfEncumber
You should add PropertychangedCallback in Register method - otherwise when first time loaded a numeric control always shows zero (not a value I wanted). public static readonly DependencyProperty ValueProperty = DependencyProperty.Register( "Value", typeof(int), typeof(NumericUpDown) , new PropertyMetadata(default(int) , new PropertyChangedCallback(ValueProperty_PropertyChanged)) ); Then inside ValueProperty_PropertyChanged call: ((NumericUpDown)obj).txtNum.Text = e.NewValue.ToString();Coons
F
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>  
Fairlie answered 27/9, 2015 at 4:34 Comment(0)
S
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 :)

Symmetrize answered 16/6, 2019 at 22:42 Comment(3)
Extended.Wpf.Toolkit is now only for "non-commercial" use. See the license here: github.com/xceedsoftware/wpftoolkit/blob/master/license.md.Raynold
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.Symmetrize
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.Symmetrize
H
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...).

Hermilahermina answered 2/6, 2020 at 11:8 Comment(0)
O
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

Oina answered 12/1, 2022 at 14:4 Comment(0)
N
1

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

Nigrify answered 2/2, 2022 at 15:53 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.