Introduction
The article just deals with creating a simple numeric up down control for WPF. The control supports data binding, value changes with up and down keys along with the up and down buttons.
Prerequisites
- .NET Framework 3.5 or higher
- Windows XP, 2003, Vista, 2008
Creating the Control
The control consists of a text box and two buttons which can increment or decrement the value in the control. For creating the control, I am using VS 2008 SP1. Follow the steps provided below to create a numeric up down control.
Adding the Control
- Open VS 2008, go to File->New Project , choose a language and create a Windows Library Project.
- Add a new user control to the project by choosing user control from the WPF category.
- Name it
NumericUpDownControl
.
- Add a grid inside a border. The main control will be placed inside the border just to have an outline.
- In the grid, add the
textbox
along with the two button controls for up and down actions.
The XAML for the controls looks like:
<Grid Margin="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="20" />
</Grid.ColumnDefinitions>
<TextBox Name="TextBoxValue"
TextAlignment="Right"
Grid.Column="0"/>
<StackPanel Orientation="Vertical"
Grid.Column="1"
VerticalAlignment="Center">
<Button x:Name="Increase">
<Image Source="Images/up.png" Height="3" Width="5" />
</Button>
<Button x:Name="Decrease" >
<Image Source="Images/down.png" Height="3" Width="5" />
</Button>
</StackPanel>
</Grid>
Adding Properties and Basic Events
This creates the basic look of the control. Let’s add some event handlers to the control. First of all, since it’s a numeric up down control, it’s necessary to validate for the text input and allow only numeric input.
For validating the input, I am using regex
class on the ‘PreviewTextInput
’. The event is fired when the textbox
gets the text, perfect time to check and validate the input. Let’s define the regular expression and then add the event handler code. It’s good to have the Regex defined in the constructor because it’s used in the other events as well.
private void value_PreviewTextInput(object sender, TextCompositionEventArgs e)
{
var tb = (TextBox)sender;
var text = tb.Text.Insert(tb.CaretIndex, e.Text);
e.Handled = !_numMatch.IsMatch(text);
}
When PreviewTextInput
is fired, the event contains the char
that’s just entered by the user and it’s still not the part of the Text
property of the Textbox
, so let’s just insert the newly entered character and see if it fits our criteria.
But before adding more events, let’s add some basic properties to the control like, Maximum
, Minimum
. It’s a good practice to add the properties as a dependency property so that we can utilize the binding features.
Note: We can add a dependency property by using the snippet ‘propdp’.
public int Maximum
{
get { return (int)GetValue(MaximumProperty); }
set { SetValue(MaximumProperty, value); }
}
public static readonly DependencyProperty MaximumProperty =
DependencyProperty.Register("Maximum", typeof(int),
typeof(NumericBox), new UIPropertyMetadata(100));
public int Minimum
{
get { return (int)GetValue(MinimumProperty); }
set { SetValue(MinimumProperty, value); }
}
public static readonly DependencyProperty MinimumProperty =
DependencyProperty.Register("Minimum", typeof(int),
typeof(NumericBox), new UIPropertyMetadata(0));
Now let’s add the Value
property which represents the value of the numeric up down control. We’ll be adding it just like the previous properties with just one additional param, i.e., the PropertyChangedCallback
, so that we may know when the value is changed and we can set the Textbox
’s Text
property to the current value.
The code for the Value
property looks like:
public int Value
{
get
{
return (int)GetValue(ValueProperty);
}
set
{
TextBoxValue.Text = value.ToString();
SetValue(ValueProperty, value);
}
}
public static readonly DependencyProperty ValueProperty =
DependencyProperty.Register("Value", typeof(int), typeof(NumericBox),
new PropertyMetadata(0, new PropertyChangedCallback(OnSomeValuePropertyChanged)));
private static void OnSomeValuePropertyChanged(
DependencyObject target, DependencyPropertyChangedEventArgs e)
{
NumericBox numericBox = target as NumericBox;
numericBox.TextBoxValue.Text = e.NewValue.ToString();
}
Now once the properties are in place, let's just add the rest of the button
and TextBox
events:
Value Changed Event: Let’s add a TextChanged
event to the textbox
and add the following code in the event handler.
private void value_TextChanged(object sender, TextChangedEventArgs e)
{
var tb = (TextBox)sender;
if (!_numMatch.IsMatch(tb.Text)) ResetText(tb);
Value = Convert.ToInt32(tb.Text);
if (Value < Minimum) Value = Minimum;
if (Value > Maximum) Value = Maximum;
RaiseEvent(new RoutedEventArgs(ValueChangedEvent));
}
First the input is validated, then min
and max
values are validated. After that, a ValueChangedEvent
is fired. The users can register the event on the source control to get notified when the value in the control is changed.
private static readonly RoutedEvent ValueChangedEvent =
EventManager.RegisterRoutedEvent("ValueChanged", RoutingStrategy.Bubble,
typeof(RoutedEventHandler), typeof(NumericBox));
public event RoutedEventHandler ValueChanged
{
add { AddHandler(ValueChangedEvent, value); }
remove { RemoveHandler(ValueChangedEvent, value); }
}
For increment and decrement buttons, all we have to do is to increment or decrement the ‘Value
’ property and raise the respective event so that the user on the parent control can register the event for changes.
private void Increase_Click(object sender, RoutedEventArgs e)
{
if (Value < Maximum)
{
Value++;
RaiseEvent(new RoutedEventArgs(IncreaseClickedEvent));
}}
private static readonly RoutedEvent IncreaseClickedEvent =
EventManager.RegisterRoutedEvent("IncreaseClicked", RoutingStrategy.Bubble,
typeof(RoutedEventHandler), typeof(NumericBox));
public event RoutedEventHandler IncreaseClicked
{
add { AddHandler(IncreaseClickedEvent, value); }
remove { RemoveHandler(IncreaseClickedEvent, value); }
}
Final Touches
The basic control looks ready to roll. Just one thing is missing, i.e. support of Up And Down Keys which is quite needed. So let’s just add that support to the control as well.
Add a PreviewKeyDown
Event to the TextBox
and the code for the handler looks like:
private void value_PreviewKeyDown(object sender, KeyEventArgs e)
{
if (e.IsDown && e.Key == Key.Up && Value < Maximum)
{
Value++;
RaiseEvent(new RoutedEventArgs(IncreaseClickedEvent));
}
else if (e.IsDown && e.Key == Key.Down && Value > Minimum)
{
Value--;
RaiseEvent(new RoutedEventArgs(DecreaseClickedEvent));
}
}
Using the Code
The control is pretty straight forward to use. The demo project attached shows a sample usage where the control is binded to an object. You can also set the value from the codebehind or XAML file.
- The control supports
Maximum
and Minimum
values along with the value
property which returns the current value.
- All the three properties mentioned above are dependency properties and hence can be bounded to the object. This is really helpful when you are working with the MVVM or similar pattern where the View layer is kept clean of the UI code.
- A sample XAML usage is shown below:
<nb:NumericBox Value="{Binding Age,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
Minimum="5" Maximum="100" Margin="5"/>
C# Code
p = new Person { Age = 25 };
this.DataContext = p;
Conclusion
This is a very simple implementation of a numeric up down control. You can feel free to use it anywhere you want. I may have overlooked something or some issues but if you find any of those, please feel free to post them here so the issues can be fixed.
History
- 25 Dec 2010 - Article published