wpf 自定义控件 NumericUpDown

CustomNumericUpDown.xaml

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    xmlns:local="clr-namespace:.View.NumericUpDown">
    <BooleanToVisibilityConverter x:Key="b2v"/>
    <Style TargetType="local:NumericUpDown">
        <Setter Property="VerticalContentAlignment" Value="Center" />
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="local:NumericUpDown">  
                        <Border Name="Bd" CornerRadius="2" SnapsToDevicePixels="True" Padding="0" BorderBrush="#FFDCDEE0" BorderThickness="1" Margin="0,0,0,0">
                            <Grid HorizontalAlignment="Stretch">
                                <local:NumberBox x:Name="TEXT" Margin="3,0,0,0" Focusable="True" HorizontalAlignment="Stretch" VerticalAlignment="Center" BorderThickness="0" FormatString="{TemplateBinding FormatString}" FontSize="14" Canvas.Left="1"
                                         TextAlignment="{TemplateBinding TextAlignment}"  BorderBrush="#FFDCDEE0" Value="{TemplateBinding Value}" >                                    
                                </local:NumberBox>
                            <UniformGrid Rows="2" Columns="1" VerticalAlignment="Stretch" HorizontalAlignment="Right"  Canvas.Right="0" Canvas.Bottom="0">                                
                                <RepeatButton x:Name="BTNUP" BorderThickness="1,0,0,0"  Focusable="False" Interval="{TemplateBinding ButtonRepeatInterval}" 
                                          IsEnabled="{TemplateBinding CanIncrement}" Background="White" Height="Auto" Width="25" BorderBrush="#FFDCDEE0" VerticalContentAlignment="Center">
                                        <Viewbox VerticalAlignment="Center" HorizontalAlignment="Center">
                                            <Path Stroke="{TemplateBinding Foreground}" Width="12" Height="12" StrokeThickness="0.5" Data="M2,7.333 L6,3.333 L10,7.333" VerticalAlignment="Center" ></Path>
                                        </Viewbox>
                                    </RepeatButton>
                                <RepeatButton x:Name="BTNDOWN" BorderThickness="1,1,0,0"  Focusable="False" Interval="{TemplateBinding ButtonRepeatInterval}" 
                                          IsEnabled="{TemplateBinding CanDecrement}" Background="White"  Height="Auto" Width="25" BorderBrush="#FFDCDEE0" VerticalContentAlignment="Center">
                                        <Viewbox VerticalAlignment="Center" HorizontalAlignment="Center">
                                            <Path Stroke="{TemplateBinding Foreground}" Width="12" Height="12" StrokeThickness="0.5" Data="M2,4 L6,8 L10,4" 
                                          VerticalAlignment="Center" HorizontalAlignment="Center"></Path>
                                        </Viewbox>
                                    </RepeatButton>
                                </UniformGrid>
                            </Grid>
                           
                        </Border>
                    
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</ResourceDictionary>

Dictionary1.xaml

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    xmlns:local="clr-namespace:.View.NumericUpDown">
    <BooleanToVisibilityConverter x:Key="b2v"/>
    <Style TargetType="local:NumericUpDown">
        <Setter Property="VerticalContentAlignment" Value="Center" />
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="local:NumericUpDown">
                    <DockPanel>
                        <UniformGrid Rows="2" Columns="1" DockPanel.Dock="Right" VerticalAlignment="Stretch">
                            <RepeatButton x:Name="BTNUP" BorderThickness="1" Padding="0" Focusable="False" Interval="{TemplateBinding ButtonRepeatInterval}" 
                                          IsEnabled="{TemplateBinding CanIncrement}" Background="White" Height="12" Width="25" BorderBrush="#FFDCDEE0" VerticalContentAlignment="Center">
                                <Viewbox VerticalAlignment="Center" HorizontalAlignment="Center">
                                    <Path Stroke="{TemplateBinding Foreground}" Width="12" Height="8" StrokeThickness="0.5" Data="M2,5.333 L6,1.333 L10,5.333" VerticalAlignment="Center" ></Path>
                                </Viewbox>
                            </RepeatButton>
                            <RepeatButton x:Name="BTNDOWN" BorderThickness="1" Padding="0" Focusable="False" Interval="{TemplateBinding ButtonRepeatInterval}" 
                                          IsEnabled="{TemplateBinding CanDecrement}" Background="White"  Height="12" Width="25" BorderBrush="#FFDCDEE0" VerticalContentAlignment="Center">
                                <Viewbox VerticalAlignment="Center" HorizontalAlignment="Center">
                                    <Path Stroke="{TemplateBinding Foreground}" Width="12" Height="8" StrokeThickness="0.5" Data="M2,0.666 L6,4.666 L10,0.666" 
                                          VerticalAlignment="Center" HorizontalAlignment="Center"></Path>
                                </Viewbox>
                            </RepeatButton>
                        </UniformGrid>
                        <Border Name="Bd" CornerRadius="4" SnapsToDevicePixels="True" BorderBrush="#FFDCDEE0" BorderThickness="1,1,0,1">
                            <local:NumberBox x:Name="TEXT" Focusable="True" FormatString="{TemplateBinding FormatString}" FontSize="14"
                                         TextAlignment="{TemplateBinding TextAlignment}" VerticalContentAlignment="Center" BorderBrush="#FFDCDEE0" >
                            </local:NumberBox>
                        </Border>
                    </DockPanel>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</ResourceDictionary>

NumberBox.cs

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

namespace LabInOnePC.View.NumericUpDown
{
    public class NumberBox : TextBox
    {
        static NumberBox()
        {
            //NumberBox基本不需要额外的样式,用Textbox的就够了
            //DefaultStyleKeyProperty.OverrideMetadata(typeof(NumberBox), new FrameworkPropertyMetadata(typeof(NumberBox)));
        }
        public NumberBox()
        {
            InputMethod.SetIsInputMethodEnabled(this, false);//禁用文本框的输入法 
            this.VerticalContentAlignment = VerticalAlignment.Center;
            this.Text = "0";
            this.MaxLength = 3;
        }

        protected override void OnLostFocus(RoutedEventArgs e)
        {
            base.OnLostFocus(e);
            if (double.TryParse(this.Text, out double value))
            {//如果是有效的数字则替换当前值
                updateValue(value);
            }
            else
            {//失去焦点时如果不是有效的数字则用当前值替换用户的输入
                updateText(this.Value.ToString(this.FormatString));
            }
        }

        private bool isUpdateValue = false, isUpdateText = false;

        protected override void OnTextInput(TextCompositionEventArgs e)
        {
            var txt = e.Text;
            if (txt != null)
            {
                foreach (var item in txt)
                {
                    if (!(char.IsDigit(item) || item == '.' || item == '-'))
                    {
                        e.Handled = true;
                        break;
                    }
                    else
                    {
                        if (!AllowDecimals && item == '.')
                        {//不允许输入小数
                            e.Handled = true;
                            break;
                        }
                        if (item == '.' || item == '-')
                        {//小数点和负号只能出现一次
                            if (contains(item))
                            {
                                e.Handled = true;
                                break;
                            }
                        }
                    }

                }
            }
            else
            {
                e.Handled = true;
            }

            base.OnTextInput(e);
        }

        /// <summary>
        /// 当前文本中是否包含指定的字符
        /// </summary>
        /// <param name="c"></param>
        /// <returns></returns>
        private bool contains(char c)
        {
            if (this.Text == null)
            {
                return false;
            }
            foreach (var item in this.Text)
            {
                if (item == c)
                {
                    return true;
                }
            }
            return false;
        }


        /// <summary>
        /// 静默更新值
        /// </summary>
        /// <param name="value"></param>
        protected void updateValue(double value)
        {
            isUpdateValue = true;
            try
            {
                this.Value = value;
            }
            catch (Exception)
            {
                throw;
            }
            finally
            {
                isUpdateValue = false;
            }
        }
        /// <summary>
        /// 静默更新文本
        /// </summary>
        /// <param name="text"></param>
        protected void updateText(string text)
        {
            isUpdateText = true;
            try
            {
                this.Text = text;
            }
            catch (Exception)
            {
                throw;
            }
            finally
            {
                isUpdateText = false;
            }
        }

        /// <summary>
        /// 当前值
        /// </summary>
        public double Value
        {
            get { return (double)GetValue(ValueProperty); }
            set { SetValue(ValueProperty, value); }
        }

        public static readonly DependencyProperty ValueProperty =
            DependencyProperty.Register("Value", typeof(double), typeof(NumberBox), new PropertyMetadata(0d));


        /// <summary>
        /// 数值的格式化字符串,默认最多保留小数点后6位。
        /// </summary>
        public string FormatString
        {
            get { return (string)GetValue(FormatStringProperty); }
            set { SetValue(FormatStringProperty, value); }
        }

        public static readonly DependencyProperty FormatStringProperty =
            DependencyProperty.Register("FormatString", typeof(string), typeof(NumberBox), new PropertyMetadata("0.######"));


        /// <summary>
        /// 是否允许输入小数
        /// </summary>
        public bool AllowDecimals
        {
            get { return (bool)GetValue(AllowDecimalsProperty); }
            set { SetValue(AllowDecimalsProperty, value); }
        }

        public static readonly DependencyProperty AllowDecimalsProperty =
            DependencyProperty.Register("AllowDecimals", typeof(bool), typeof(NumberBox), new PropertyMetadata(true));

        protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e)
        {
            base.OnPropertyChanged(e);
            if (e.Property == ValueProperty)
            {
                this.OnValueChanged();
            }
            else if (e.Property == FormatStringProperty)
            {
                updateText(this.Value.ToString(this.FormatString));
            }
        }
        /// <summary>
        /// 在当前值改变后触发
        /// </summary>
        public event EventHandler ValueChanged;

        protected virtual void OnValueChanged()
        {
            if (!isUpdateValue)
            {
                updateText(this.Value.ToString(this.FormatString));
            }
            this.ValueChanged?.Invoke(this, EventArgs.Empty);
        }
        protected override void OnTextChanged(TextChangedEventArgs e)
        {
            if (!isUpdateText)
            {
                if (double.TryParse(this.Text, out double value))
                {
                    updateValue(AllowDecimals ? value : Convert.ToInt32(value));//不允许小数则将值强行取整
                }
            }
            base.OnTextChanged(e);
        }
       
    }
}
 

NumericUpDown.cs

using System;
using System.Windows;
using System.Windows.Controls.Primitives;
using System.Windows.Input;
using System.Windows.Media;

namespace .View.NumericUpDown
{
    /// <summary>
    /// 具有调节按钮的数值框
    /// </summary>
    [TemplatePart(Name = "BTNUP", Type = typeof(ButtonBase))]
    [TemplatePart(Name = "BTNDOWN", Type = typeof(ButtonBase))]
    [TemplatePart(Name = "TEXT", Type = typeof(NumberBox))]
    public class NumericUpDown : System.Windows.Controls.Primitives.RangeBase
    {

        static NumericUpDown()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(NumericUpDown), new FrameworkPropertyMetadata(typeof(NumericUpDown)));
        }
        public NumericUpDown()
        {

        }
        private RepeatButton btnup, btndown;
        private NumberBox textbox;

        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();

            if (this.textbox != null)
            {
                this.textbox.ValueChanged -= Textbox_ValueChanged;
                this.textbox.LostFocus -= Textbox_LostFocus;
            }
            if (this.btnup != null)
            {
                this.btnup.Click -= Btnup_Click;
            }
            if (this.btndown != null)
            {
                this.btndown.Click -= Btndown_Click;
            }

            this.btnup = (RepeatButton)this.GetTemplateChild("BTNUP");
            this.btndown = (RepeatButton)this.GetTemplateChild("BTNDOWN");
            this.textbox = (NumberBox)this.GetTemplateChild("TEXT");
            
            if (this.textbox != null)
            {
                this.textbox.Value = this.Value;
                CheckAllowIncrementOrDecrement();
                this.textbox.ValueChanged += Textbox_ValueChanged;
                this.textbox.LostFocus += Textbox_LostFocus;
            }
            if (this.btnup != null)
            {
                this.btnup.Click += Btnup_Click;
            }
            if (this.btndown != null)
            {
                this.btndown.Click += Btndown_Click;
            }
        }

        #region 实现

        protected override void OnPreviewMouseWheel(MouseWheelEventArgs e)
        {
            base.OnPreviewMouseWheel(e);
            if (e.Delta > 0)
            {
                this.OnIncrement();
            }
            else if (e.Delta < 0)
            {
                this.OnDecrement();
            }
        }
        protected override void OnPreviewKeyDown(KeyEventArgs e)
        {
            base.OnPreviewKeyDown(e);
            if (e.Key == Key.Up)
            {
                UpdateAndCheckValue(this.Value + this.SmallChange);
            }
            else if (e.Key == Key.Down)
            {
                UpdateAndCheckValue(this.Value - this.SmallChange);
            }
        }

        private void Textbox_LostFocus(object sender, RoutedEventArgs e)
        {//数值框失去焦点时将当前值写道数值框
            if (this.textbox.Value != this.Value)
            {
                this.textbox.Value = this.Value;
            }
        }
        private void Textbox_ValueChanged(object sender, EventArgs e)
        {//数值框的值变化时使用数值框的值替换当前值
            if (this.textbox.Value > this.Minimum && this.textbox.Value < this.Maximum)
            {
                this.Value = this.textbox.Value;
            }
        }

        protected override void OnGotFocus(RoutedEventArgs e)
        {
            base.OnGotFocus(e);
            this.textbox?.Focus();
        }

        private void Btndown_Click(object sender, RoutedEventArgs e)
        {
            this.OnDecrement();
        }

        private void Btnup_Click(object sender, RoutedEventArgs e)
        {
            this.OnIncrement();
        }
        /// <summary>
        /// 检查是否允许递增/递减
        /// </summary>
        protected void CheckAllowIncrementOrDecrement()
        {
            CanIncrement = (Value < Maximum);
            CanDecrement = (Value > Minimum);
        }

        protected virtual void OnIncrement()
        {
            var value = this.Value + this.LargeChange;
            this.UpdateAndCheckValue(value);
        }

        protected virtual void OnDecrement()
        {
            var value = this.Value - this.LargeChange;
            this.UpdateAndCheckValue(value);
        }

        /// <summary>
        /// 更新并且检查当前值,保证值不会超出<see cref="Minimum"/>和<see cref="Maximum"/>限定的范围。
        /// </summary>
        /// <param name="value"></param>
        protected virtual void UpdateAndCheckValue(double value)
        {
            if (value > this.Maximum)
            {
                this.Value = Maximum;
            }
            else if (value < this.Minimum)
            {
                this.Value = Minimum;
            }
            else
            {
                this.Value = value;
            }
        }

        protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e)
        {
            base.OnPropertyChanged(e);
            if (e.Property == ValueProperty)
            {
                CheckAllowIncrementOrDecrement();               
                if (this.textbox != null)
                {
                    this.textbox.Value = this.Value;                              
                }
            }
        }
        #endregion

        #region 属性

        /// <summary>
        /// 数字的格式化字符串,默认最多保留小数点后6位。
        /// </summary>
        public string FormatString
        {
            get { return (string)GetValue(FormatStringProperty); }
            set { SetValue(FormatStringProperty, value); }
        }

        public static readonly DependencyProperty FormatStringProperty =
            DependencyProperty.Register("FormatString", typeof(string), typeof(NumericUpDown), new PropertyMetadata("0.######"));

        /// <summary>
        /// 文字对齐方式
        /// </summary>
        public TextAlignment TextAlignment
        {
            get { return (TextAlignment)GetValue(TextAlignmentProperty); }
            set { SetValue(TextAlignmentProperty, value); }
        }

        public static readonly DependencyProperty TextAlignmentProperty =
            DependencyProperty.Register("TextAlignment", typeof(TextAlignment), typeof(NumericUpDown), new PropertyMetadata(TextAlignment.Left));


        /// <summary>
        /// 按钮重复触发Click事件的间隔时间,单位为毫秒,默认200毫秒。
        /// </summary>
        public int ButtonRepeatInterval
        {
            get { return (int)GetValue(ButtonRepeatIntervalProperty); }
            set { SetValue(ButtonRepeatIntervalProperty, value); }
        }

        public static readonly DependencyProperty ButtonRepeatIntervalProperty =
            DependencyProperty.Register("ButtonRepeatInterval", typeof(int), typeof(NumericUpDown), new PropertyMetadata(200));


        /// <summary>
        /// 允许递增
        /// </summary>
        protected internal bool CanIncrement
        {
            get { return (bool)GetValue(CanIncrementProperty); }
            set { SetValue(CanIncrementProperty, value); }
        }

        protected internal static readonly DependencyProperty CanIncrementProperty =
            DependencyProperty.Register("CanIncrement", typeof(bool), typeof(NumericUpDown), new PropertyMetadata(false));


        /// <summary>
        /// 允许递减
        /// </summary>
        protected internal bool CanDecrement
        {
            get { return (bool)GetValue(CanDecrementProperty); }
            set { SetValue(CanDecrementProperty, value); }
        }

        protected internal static readonly DependencyProperty CanDecrementProperty =
            DependencyProperty.Register("CanDecrement", typeof(bool), typeof(NumericUpDown), new PropertyMetadata(false));

       

        #endregion       
    }
}
 

App.xaml


                <ResourceDictionary Source="/View/NumericUpDown/CustomNumericUpDown.xaml"/>

调用
                                        <local:NumericUpDown Maximum="1000" Minimum="0" Height="24" Width="68" Margin="108,89,174,56" x:Name="nudDay"></local:NumericUpDown>

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值