NumericUpDown 控件看起来像是一个文本框与一对用户可单击以调整值的箭头的组合。该控件显示并设置固定的数值选择列表中的单个数值。用户可以通过单击向上和向下、按向上和向下键或在控件的文本框部件中键入一个数字来增大和减小数字。单击向上键时,值向最大值方向移动;单击向下键时,值向最小值方向移动。
我这里提供的是在网上找的别人自己写好的NumericUpDown 控件,然后我进行了样式修改,修改之后是长按向上键,值会不停的增大,直至最大值,同理,长按向下键,值会不停的减小,直至最小值(就是把以前的Button换成了RepeatButton)。单击功能仍和以前一样。
代码奉上:
首先这个是自定义控件:
public class NumericUpDown : Control { static NumericUpDown() { InitializeCommands(); // Listen to MouseLeftButtonDown event to determine if NumericUpDown should move focus to itself EventManager.RegisterClassHandler(typeof(NumericUpDown), Mouse.MouseDownEvent, new MouseButtonEventHandler(NumericUpDown.OnMouseLeftButtonDown), true); DefaultStyleKeyProperty.OverrideMetadata(typeof(NumericUpDown), new FrameworkPropertyMetadata(typeof(NumericUpDown))); } public NumericUpDown() : base() { updateValueString(); } #region Properties #region Value public decimal Value { get { return (decimal)GetValue(ValueProperty); } set { SetValue(ValueProperty, value); } } /// <summary> /// Identifies the Value dependency property. /// </summary> public static readonly DependencyProperty ValueProperty = DependencyProperty.Register( "Value", typeof(decimal), typeof(NumericUpDown), new FrameworkPropertyMetadata(DefaultValue, new PropertyChangedCallback(OnValueChanged), new CoerceValueCallback(CoerceValue) ) ); private static void OnValueChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args) { NumericUpDown control = (NumericUpDown)obj; decimal oldValue = (decimal)args.OldValue; decimal newValue = (decimal)args.NewValue; #region Fire Automation events NumericUpDownAutomationPeer peer = UIElementAutomationPeer.FromElement(control) as NumericUpDownAutomationPeer; if (peer != null) { peer.RaiseValueChangedEvent(oldValue, newValue); } #endregion RoutedPropertyChangedEventArgs<decimal> e = new RoutedPropertyChangedEventArgs<decimal>( oldValue, newValue, ValueChangedEvent); control.OnValueChanged(e); control.updateValueString(); } /// <summary> /// Raises the ValueChanged event. /// </summary> /// <param name="args">Arguments associated with the ValueChanged event.</param> protected virtual void OnValueChanged(RoutedPropertyChangedEventArgs<decimal> args) { RaiseEvent(args); } private static object CoerceValue(DependencyObject element, object value) { decimal newValue = (decimal)value; NumericUpDown control = (NumericUpDown)element; newValue = Math.Max(control.Minimum, Math.Min(control.Maximum, newValue)); newValue = Decimal.Round(newValue, control.DecimalPlaces); return newValue; } #endregion #region Minimum public decimal Minimum { get { return (decimal)GetValue(MinimumProperty); } set { SetValue(MinimumProperty, value); } } public static readonly DependencyProperty MinimumProperty = DependencyProperty.Register( "Minimum", typeof(decimal), typeof(NumericUpDown), new FrameworkPropertyMetadata(DefaultMinValue, new PropertyChangedCallback(OnMinimumChanged), new CoerceValueCallback(CoerceMinimum) ) ); private static void OnMinimumChanged(DependencyObject element, DependencyPropertyChangedEventArgs args) { element.CoerceValue(MaximumProperty); element.CoerceValue(ValueProperty); } private static object CoerceMinimum(DependencyObject element, object value) { decimal minimum = (decimal)value; NumericUpDown control = (NumericUpDown)element; return Decimal.Round(minimum, control.DecimalPlaces); } #endregion #region Maximum public decimal Maximum { get { return (decimal)GetValue(MaximumProperty); } set { SetValue(MaximumProperty, value); } } public static readonly DependencyProperty MaximumProperty = DependencyProperty.Register( "Maximum", typeof(decimal), typeof(NumericUpDown), new FrameworkPropertyMetadata(DefaultMaxValue, new PropertyChangedCallback(OnMaximumChanged), new CoerceValueCallback(CoerceMaximum) ) ); private static void OnMaximumChanged(DependencyObject element, DependencyPropertyChangedEventArgs args) { element.CoerceValue(ValueProperty); } private static object CoerceMaximum(DependencyObject element, object value) { NumericUpDown control = (NumericUpDown)element; decimal newMaximum = (decimal)value; return Decimal.Round(Math.Max(newMaximum, control.Minimum), control.DecimalPlaces); } #endregion #region Change public decimal Change { get { return (decimal)GetValue(ChangeProperty); } set { SetValue(ChangeProperty, value); } } public static readonly DependencyProperty ChangeProperty = DependencyProperty.Register( "Change", typeof(decimal), typeof(NumericUpDown), new FrameworkPropertyMetadata(DefaultChange, new PropertyChangedCallback(OnChangeChanged), new CoerceValueCallback(CoerceChange)), new ValidateValueCallback(ValidateChange) ); private static bool ValidateChange(object value) { decimal change = (decimal)value; return change > 0; } private static void OnChangeChanged(DependencyObject element, DependencyPropertyChangedEventArgs args) { } private static object CoerceChange(DependencyObject element, object value) { decimal newChange = (decimal)value; NumericUpDown control = (NumericUpDown)element; decimal coercedNewChange = Decimal.Round(newChange, control.DecimalPlaces); //If Change is .1 and DecimalPlaces is changed from 1 to 0, we want Change to go to 1, not 0. //Put another way, Change should always be rounded to DecimalPlaces, but never smaller than the //previous Change if (coercedNewChange < newChange) { coercedNewChange = smallestForDecimalPlaces(control.DecimalPlaces); } return coercedNewChange; } private static decimal smallestForDecimalPlaces(int decimalPlaces) { if (decimalPlaces < 0) { throw new ArgumentException("decimalPlaces"); } decimal d = 1; for (int i = 0; i < decimalPlaces; i++) { d /= 10; } return d; } #endregion #region DecimalPlaces public int DecimalPlaces { get { return (int)GetValue(DecimalPlacesProperty); } set { SetValue(DecimalPlacesProperty, value); } } public static readonly DependencyProperty DecimalPlacesProperty = DependencyProperty.Register( "DecimalPlaces", typeof(int), typeof(NumericUpDown), new FrameworkPropertyMetadata(DefaultDecimalPlaces, new PropertyChangedCallback(OnDecimalPlacesChanged) ), new ValidateValueCallback(ValidateDecimalPlaces) ); private static void OnDecimalPlacesChanged(DependencyObject element, DependencyPropertyChangedEventArgs args) { NumericUpDown control = (NumericUpDown)element; control.CoerceValue(ChangeProperty); control.CoerceValue(MinimumProperty); control.CoerceValue(MaximumProperty); control.CoerceValue(ValueProperty); control.updateValueString(); } private static bool ValidateDecimalPlaces(object value) { int decimalPlaces = (int)value; return decimalPlaces >= 0; } #endregion #region ValueString //public string ValueString //{ // get // { // return (string)GetValue(ValueStringProperty); // } //} //private static readonly DependencyPropertyKey ValueStringPropertyKey = // DependencyProperty.RegisterAttachedReadOnly("ValueString", typeof(string), typeof(NumericUpDown), new PropertyMetadata()); //public static readonly DependencyProperty ValueStringProperty = ValueStringPropertyKey.DependencyProperty; public string ValueString { get { return (string)GetValue(ValueStringProperty); } set { SetValue(ValueStringProperty, value); } } /// <summary> /// Identifies the Value dependency property. /// </summary> public static readonly DependencyProperty ValueStringProperty = DependencyProperty.Register("ValueString", typeof(string), typeof(NumericUpDown), new FrameworkPropertyMetadata()); private void updateValueString() { m_NumberFormatInfo.NumberDecimalDigits = this.DecimalPlaces; string newValueString = this.Value.ToString("f", m_NumberFormatInfo); //this.SetValue(ValueStringPropertyKey, newValueString); ValueString = newValueString; } private NumberFormatInfo m_NumberFormatInfo = new NumberFormatInfo(); #endregion #endregion #region Events /// <summary> /// Identifies the ValueChanged routed event. /// </summary> public static readonly RoutedEvent ValueChangedEvent = EventManager.RegisterRoutedEvent( "ValueChanged", RoutingStrategy.Bubble, typeof(RoutedPropertyChangedEventHandler<decimal>), typeof(NumericUpDown)); /// <summary> /// Occurs when the Value property changes. /// </summary> public event RoutedPropertyChangedEventHandler<decimal> ValueChanged { add { AddHandler(ValueChangedEvent, value); } remove { RemoveHandler(ValueChangedEvent, value); } } #endregion #region Commands public static RoutedCommand IncreaseCommand { get { return m_IncreaseCommand; } } public static RoutedCommand DecreaseCommand { get { return m_DecreaseCommand; } } private static void InitializeCommands() { m_IncreaseCommand = new RoutedCommand("IncreaseCommand", typeof(NumericUpDown)); CommandManager.RegisterClassCommandBinding(typeof(NumericUpDown), new CommandBinding(m_IncreaseCommand, OnIncreaseCommand)); CommandManager.RegisterClassInputBinding(typeof(NumericUpDown), new InputBinding(m_IncreaseCommand, new KeyGesture(Key.Up))); m_DecreaseCommand = new RoutedCommand("DecreaseCommand", typeof(NumericUpDown)); CommandManager.RegisterClassCommandBinding(typeof(NumericUpDown), new CommandBinding(m_DecreaseCommand, OnDecreaseCommand)); CommandManager.RegisterClassInputBinding(typeof(NumericUpDown), new InputBinding(m_DecreaseCommand, new KeyGesture(Key.Down))); } private static void OnIncreaseCommand(object sender, ExecutedRoutedEventArgs e) { NumericUpDown control = sender as NumericUpDown; if (control != null) { control.OnIncrease(); } } private static void OnDecreaseCommand(object sender, ExecutedRoutedEventArgs e) { NumericUpDown control = sender as NumericUpDown; if (control != null) { control.OnDecrease(); } } protected virtual void OnIncrease() { this.Value += Change; } protected virtual void OnDecrease() { this.Value -= Change; } private static RoutedCommand m_IncreaseCommand; private static RoutedCommand m_DecreaseCommand; #endregion #region Automation protected override AutomationPeer OnCreateAutomationPeer() { return new NumericUpDownAutomationPeer(this); } #endregion /// <summary> /// This is a class handler for MouseLeftButtonDown event. /// The purpose of this handle is to move input focus to NumericUpDown when user pressed /// mouse left button on any part of slider that is not focusable. /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private static void OnMouseLeftButtonDown(object sender, MouseButtonEventArgs e) { NumericUpDown control = (NumericUpDown)sender; // When someone click on a part in the NumericUpDown and it's not focusable // NumericUpDown needs to take the focus in order to process keyboard correctly if (!control.IsKeyboardFocusWithin) { e.Handled = control.Focus() || e.Handled; } } private const decimal DefaultMinValue = 0, DefaultValue = DefaultMinValue, DefaultMaxValue = 100, DefaultChange = 1; private const int DefaultDecimalPlaces = 0; } public class NumericUpDownAutomationPeer : FrameworkElementAutomationPeer, IRangeValueProvider { public NumericUpDownAutomationPeer(NumericUpDown control) : base(control) { } protected override string GetClassNameCore() { return "NumericUpDown"; } protected override AutomationControlType GetAutomationControlTypeCore() { return AutomationControlType.Spinner; } public override object GetPattern(PatternInterface patternInterface) { if (patternInterface == PatternInterface.RangeValue) { return this; } return base.GetPattern(patternInterface); } internal void RaiseValueChangedEvent(decimal oldValue, decimal newValue) { base.RaisePropertyChangedEvent(RangeValuePatternIdentifiers.ValueProperty, (double)oldValue, (double)newValue); } #region IRangeValueProvider Members bool IRangeValueProvider.IsReadOnly { get { return !IsEnabled(); } } double IRangeValueProvider.LargeChange { get { return (double)MyOwner.Change; } } double IRangeValueProvider.Maximum { get { return (double)MyOwner.Maximum; } } double IRangeValueProvider.Minimum { get { return (double)MyOwner.Minimum; } } void IRangeValueProvider.SetValue(double value) { if (!IsEnabled()) { throw new ElementNotEnabledException(); } decimal val = (decimal)value; if (val < MyOwner.Minimum || val > MyOwner.Maximum) { throw new ArgumentOutOfRangeException("value"); } MyOwner.Value = val; } double IRangeValueProvider.SmallChange { get { return (double)MyOwner.Change; } } double IRangeValueProvider.Value { get { return (double)MyOwner.Value; } } #endregion private NumericUpDown MyOwner { get { return (NumericUpDown)base.Owner; } } }
然后建一个窗体:
<Window x:Class="WpfTest.NumericUpDownStyle"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:styles="clr-namespace:WpfTest"
Title="NumericUpDownStyle"
Height="300"
Width="300">
<Window.Resources>
<ControlTemplate x:Key="addBtnTemplate"
TargetType="{x:Type RepeatButton}">
<Border Height="19"
Width="39"
BorderThickness="0">
<Border.Background>
<LinearGradientBrush EndPoint="0.5,1"
StartPoint="0.5,0"
Opacity="1">
<GradientStop Color="#FFb9bbbc"
Offset="0" />
<GradientStop Color="#FF909294"
Offset="0.973" />
</LinearGradientBrush>
</Border.Background>
<ContentPresenter HorizontalAlignment="Center"
Content="{TemplateBinding Button.Content}"
VerticalAlignment="Center"></ContentPresenter>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsPressed"
Value="True">
<Setter Property="RenderTransform">
<Setter.Value>
<TranslateTransform X=".5"
Y=".3"></TranslateTransform>
</Setter.Value>
</Setter>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
<Style TargetType="{x:Type styles:NumericUpDown}">
<Setter Property="BorderBrush"
Value="#81ADD0" />
<Setter Property="BorderThickness"
Value="1" />
<Setter Property="Padding"
Value="0" />
<Setter Property="MinWidth"
Value="180" />
<Setter Property="Height"
Value="40" />
<Setter Property="HorizontalAlignment"
Value="Center" />
<Setter Property="VerticalAlignment"
Value="Center" />
<Setter Property="FocusVisualStyle"
Value="{x:Null}" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type styles:NumericUpDown}">
<Border BorderBrush="#999999"
Width="128"
Height="39"
BorderThickness="1"
Padding="{TemplateBinding Padding}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition></ColumnDefinition>
</Grid.ColumnDefinitions>
<TextBox Text="{Binding
RelativeSource=
{
RelativeSource
Mode=FindAncestor,
AncestorType={x:Type styles:NumericUpDown}
},
Path=Value,
Mode=TwoWay
}"
BorderThickness="0"
IsEnabled="True"
FontSize="24"
TextAlignment="Right"
VerticalAlignment="Center"
HorizontalAlignment="Left"
Grid.Column="0"
Width="70" />
<Border BorderThickness="1,0,0,0" Width="39"
HorizontalAlignment="Right"
BorderBrush="#999999">
<StackPanel Width="39">
<RepeatButton Command="styles:NumericUpDown.IncreaseCommand"
Grid.Row="0"
HorizontalAlignment="Right"
Width="39"
Height="19"
Margin="0,0,0,0"
Template="{StaticResource addBtnTemplate}">
<RepeatButton.Content>
<Path Height="6"
Width="12"
Stretch="Fill"
Opacity="1"
Data="M 666.5,597 C666.5,597 678.5,597 678.5,597 678.5,597 672.5,591 672.5,591 672.5,591 666.5,597 666.5,597 z"
Fill="#ffffff" />
</RepeatButton.Content>
</RepeatButton>
<RepeatButton Command="styles:NumericUpDown.DecreaseCommand"
Margin="0,0,0,0"
Grid.Row="1"
Width="39"
HorizontalAlignment="Right"
Height="19"
Template="{StaticResource addBtnTemplate}">
<RepeatButton.Content>
<Path Height="6"
Width="12"
Stretch="Fill"
Opacity="1"
Data="M 666.5,609 C666.5,609 678.5,609 678.5,609 678.5,609 672.5,615 672.5,615 672.5,615 666.5,609 666.5,609 z"
Fill="#ffffff" />
</RepeatButton.Content>
</RepeatButton>
</StackPanel>
</Border>
</Grid>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsEnabled"
Value="false">
<Setter Property="Opacity"
Value="0.2" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<Grid>
<styles:NumericUpDown x:Name="nUpDown_hege"
Minimum="0"
Maximum="100"
Grid.Column="1"></styles:NumericUpDown>
</Grid>
</Window>
ok,大功告成。。