基于WPF实现数字框控件

 更新时间:2023年08月10日 09:21:30   作者:WPF开发者  
这篇文章主要介绍了如何利用WPF实现数字框控件,文中的示例代码讲解详细,对我们学习或工作有一定的帮助,需要的小伙伴可以参考一下

WPF 实现数字框控件

框架使用.NET4 至 .NET6

Visual Studio 2022;

实现代码

1)新增 NumericBox.cs 代码如下:

OnPreviewKeyDown 方法:

  • 处理 Enter 键:调用 DealInputText 方法处理输入文本,选择所有文本,并标记事件为已处理。
  • 处理向上箭头键:调用 ContinueChangeValue 方法增加 NumericBox 值,并标记事件为已处理。
  • 处理向下箭头键:调用 ContinueChangeValue 方法减少 NumericBox 值,并标记事件为已处理。

OnPreviewKeyUp 方法:

处理释放的向上箭头键或向下箭头键:调用 ResetInternal 方法重置 NumericBox 内部状态。

尝试将输入的文本转换为 double 类型的数值。

如果转换成功:

检查转换后的值与当前值是否非常接近,如果是,则不做任何操作。

否则,根据转换后的值和最大值、最小值进行比较:

如果大于最大值,则将值设置为最大值。

如果小于最小值,则将值设置为最小值。

否则,将值设置为转换后的值。

如果转换失败,则将当前值设置回文本框。

Precision 可输入多少小数位。

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows.Controls.Primitives;
using System.Windows.Input;
using System.Windows;
using System.Xml.Linq;
using WPFDevelopers.Utilities;
using System.Windows.Controls;
using WPFDevelopers.Datas;
namespace WPFDevelopers.Controls
{
    [DefaultEvent("ValueChanged"), DefaultProperty("Value")]
    [TemplatePart(Name = TextBoxTemplateName, Type = typeof(TextBox))]
    [TemplatePart(Name = NumericUpTemplateName, Type = typeof(RepeatButton))]
    [TemplatePart(Name = NumericDownTemplateName, Type = typeof(RepeatButton))]
    public class NumericBox : Control
    {
        private static readonly Type _typeofSelf = typeof(NumericBox);
        private const double DefaultInterval = 1d;
        private const int DefaultDelay = 500;
        private const string TextBoxTemplateName = "PART_TextBox";
        private const string NumericUpTemplateName = "PART_NumericUp";
        private const string NumericDownTemplateName = "PART_NumericDown";
        private static RoutedCommand _increaseCommand = null;
        private static RoutedCommand _decreaseCommand = null;
        private TextBox _valueTextBox;
        private RepeatButton _repeatUp;
        private RepeatButton _repeatDown;
        private double _internalLargeChange = DefaultInterval;
        private double _intervalValueSinceReset = 0;
        private double? _lastOldValue = null;
        private bool _isManual;
        private bool _isBusy;
        static NumericBox()
        {
            InitializeCommands();
            DefaultStyleKeyProperty.OverrideMetadata(_typeofSelf, new FrameworkPropertyMetadata(_typeofSelf));
        }
        #region Command
        private static void InitializeCommands()
        {
            _increaseCommand = new RoutedCommand("Increase", _typeofSelf);
            _decreaseCommand = new RoutedCommand("Decrease", _typeofSelf);
            CommandManager.RegisterClassCommandBinding(_typeofSelf, new CommandBinding(_increaseCommand, OnIncreaseCommand, OnCanIncreaseCommand));
            CommandManager.RegisterClassCommandBinding(_typeofSelf, new CommandBinding(_decreaseCommand, OnDecreaseCommand, OnCanDecreaseCommand));
        }
        public static RoutedCommand IncreaseCommand
        {
            get { return _increaseCommand; }
        }
        public static RoutedCommand DecreaseCommand
        {
            get { return _decreaseCommand; }
        }
        private static void OnIncreaseCommand(object sender, RoutedEventArgs e)
        {
            var numericBox = sender as NumericBox;
            numericBox.ContinueChangeValue(true);
        }
        private static void OnCanIncreaseCommand(object sender, CanExecuteRoutedEventArgs e)
        {
            var numericBox = sender as NumericBox;
            e.CanExecute = (!numericBox.IsReadOnly && numericBox.IsEnabled && DoubleUtil.LessThan(numericBox.Value, numericBox.Maximum));
        }
        private static void OnDecreaseCommand(object sender, RoutedEventArgs e)
        {
            var numericBox = sender as NumericBox;
            numericBox.ContinueChangeValue(false);
        }
        private static void OnCanDecreaseCommand(object sender, CanExecuteRoutedEventArgs e)
        {
            var numericBox = sender as NumericBox;
            e.CanExecute = (!numericBox.IsReadOnly && numericBox.IsEnabled && DoubleUtil.GreaterThan(numericBox.Value, numericBox.Minimum));
        }
        #endregion
        #region RouteEvent
        public static readonly RoutedEvent ValueChangedEvent = EventManager.RegisterRoutedEvent("ValueChanged", RoutingStrategy.Bubble, typeof(RoutedPropertyChangedEventHandler<double>), _typeofSelf);
        public event RoutedPropertyChangedEventHandler<double> ValueChanged
        {
            add { AddHandler(ValueChangedEvent, value); }
            remove { RemoveHandler(ValueChangedEvent, value); }
        }
        #endregion
        #region Properties
        public static readonly DependencyProperty DisabledValueChangedWhileBusyProperty = DependencyProperty.Register("DisabledValueChangedWhileBusy", typeof(bool), _typeofSelf,
           new PropertyMetadata(false));
        [Category("Common")]
        [DefaultValue(true)]
        public bool DisabledValueChangedWhileBusy
        {
            get { return (bool)GetValue(DisabledValueChangedWhileBusyProperty); }
            set { SetValue(DisabledValueChangedWhileBusyProperty, value); }
        }
        public static readonly DependencyProperty IntervalProperty = DependencyProperty.Register("Interval", typeof(double), _typeofSelf,
           new FrameworkPropertyMetadata(DefaultInterval, IntervalChanged, CoerceInterval));
        [Category("Behavior")]
        [DefaultValue(DefaultInterval)]
        public double Interval
        {
            get { return (double)GetValue(IntervalProperty); }
            set { SetValue(IntervalProperty, value); }
        }
        private static object CoerceInterval(DependencyObject d, object value)
        {
            var interval = (double)value;
            return DoubleUtil.IsNaN(interval) ? 0 : Math.Max(interval, 0);
        }
        private static void IntervalChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var numericBox = (NumericBox)d;
            numericBox.ResetInternal();
        }
        public static readonly DependencyProperty SpeedupProperty = DependencyProperty.Register("Speedup", typeof(bool), _typeofSelf,
           new PropertyMetadata(true));
        [Category("Common")]
        [DefaultValue(true)]
        public bool Speedup
        {
            get { return (bool)GetValue(SpeedupProperty); }
            set { SetValue(SpeedupProperty, value); }
        }
        public static readonly DependencyProperty DelayProperty = DependencyProperty.Register("Delay", typeof(int), _typeofSelf,
            new PropertyMetadata(DefaultDelay, null, CoerceDelay));
        [DefaultValue(DefaultDelay)]
        [Category("Behavior")]
        public int Delay
        {
            get { return (int)GetValue(DelayProperty); }
            set { SetValue(DelayProperty, value); }
        }
        private static object CoerceDelay(DependencyObject d, object value)
        {
            var delay = (int)value;
            return Math.Max(delay, 0);
        }
        public static readonly DependencyProperty UpDownButtonsWidthProperty = DependencyProperty.Register("UpDownButtonsWidth", typeof(double), _typeofSelf,
           new PropertyMetadata(20d));
        [Category("Appearance")]
        [DefaultValue(20d)]
        public double UpDownButtonsWidth
        {
            get { return (double)GetValue(UpDownButtonsWidthProperty); }
            set { SetValue(UpDownButtonsWidthProperty, value); }
        }
        public static readonly DependencyProperty TextAlignmentProperty = TextBox.TextAlignmentProperty.AddOwner(_typeofSelf);
        [Category("Common")]
        public TextAlignment TextAlignment
        {
            get { return (TextAlignment)GetValue(TextAlignmentProperty); }
            set { SetValue(TextAlignmentProperty, value); }
        }
        public static readonly DependencyProperty IsReadOnlyProperty = TextBoxBase.IsReadOnlyProperty.AddOwner(_typeofSelf,
            new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.Inherits, IsReadOnlyPropertyChangedCallback));
        [Category("Appearance")]
        public bool IsReadOnly
        {
            get { return (bool)GetValue(IsReadOnlyProperty); }
            set { SetValue(IsReadOnlyProperty, value); }
        }
        private static void IsReadOnlyPropertyChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            if (e.OldValue == e.NewValue || e.NewValue == null)
                return;
            ((NumericBox)d).ToggleReadOnlyMode((bool)e.NewValue);
        }
        public static readonly DependencyProperty PrecisionProperty = DependencyProperty.Register("Precision", typeof(int?), _typeofSelf,
            new PropertyMetadata(null, OnPrecisionChanged, CoercePrecision));
        [Category("Common")]
        public int? Precision
        {
            get { return (int?)GetValue(PrecisionProperty); }
            set { SetValue(PrecisionProperty, value); }
        }
        private static object CoercePrecision(DependencyObject d, object value)
        {
            var precision = (int?)value;
            return (precision.HasValue && precision.Value < 0) ? 0 : precision;
        }
        private static void OnPrecisionChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var numericBox = (NumericBox)d;
            var newPrecision = (int?)e.NewValue;
            var roundValue = numericBox.CorrectPrecision(newPrecision, numericBox.Value);
            if (DoubleUtil.AreClose(numericBox.Value, roundValue))
                numericBox.InternalSetText(roundValue);
            else
                numericBox.Value = roundValue;
        }
        public static readonly DependencyProperty MinimumProperty = DependencyProperty.Register("Minimum", typeof(double), _typeofSelf,
            new PropertyMetadata(double.MinValue, OnMinimumChanged));
        [Category("Common")]
        public double Minimum
        {
            get { return (double)GetValue(MinimumProperty); }
            set { SetValue(MinimumProperty, value); }
        }
        private static void OnMinimumChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var numericBox = (NumericBox)d;
            numericBox.CoerceValue(MaximumProperty, numericBox.Maximum);
            numericBox.CoerceValue(ValueProperty, numericBox.Value);
        }
        public static readonly DependencyProperty MaximumProperty = DependencyProperty.Register("Maximum", typeof(double), _typeofSelf,
            new PropertyMetadata(double.MaxValue, OnMaximumChanged, CoerceMaximum));
        [Category("Common")]
        public double Maximum
        {
            get { return (double)GetValue(MaximumProperty); }
            set { SetValue(MaximumProperty, value); }
        }
        private static object CoerceMaximum(DependencyObject d, object value)
        {
            var minimum = ((NumericBox)d).Minimum;
            var val = (double)value;
            return DoubleUtil.LessThan(val, minimum) ? minimum : val;
        }
        private static void OnMaximumChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var numericBox = (NumericBox)d;
            numericBox.CoerceValue(ValueProperty, numericBox.Value);
        }
        public static readonly DependencyProperty ValueProperty = DependencyProperty.Register("Value", typeof(double), _typeofSelf,
            new FrameworkPropertyMetadata(0d, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnValueChanged, CoerceValue));
        [Category("Common")]
        public double Value
        {
            get { return (double)GetValue(ValueProperty); }
            set { SetValue(ValueProperty, value); }
        }
        private static object CoerceValue(DependencyObject d, object value)
        {
            var numericBox = (NumericBox)d;
            var val = (double)value;
            if (DoubleUtil.LessThan(val, numericBox.Minimum))
                return numericBox.Minimum;
            if (DoubleUtil.GreaterThan(val, numericBox.Maximum))
                return numericBox.Maximum;
            return numericBox.CorrectPrecision(numericBox.Precision, val);
        }
        private static void OnValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var numericBox = (NumericBox)d;
            numericBox.OnValueChanged((double)e.OldValue, (double)e.NewValue);
        }
        #endregion
        #region Virtual
        protected virtual void OnValueChanged(double oldValue, double newValue)
        {
            InternalSetText(newValue);
            InvalidateRequerySuggested(newValue);
            if ((!_isBusy || !DisabledValueChangedWhileBusy) && !DoubleUtil.AreClose(oldValue, newValue))
            {
                RaiseEvent(new TextBoxValueChangedEventArgs<double>(oldValue, newValue, _isManual, _isBusy, ValueChangedEvent));
            }
            _isManual = false;
        }
        #endregion
        #region Override
        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();
            UnsubscribeEvents();
            _valueTextBox = GetTemplateChild(TextBoxTemplateName) as TextBox;
            _repeatUp = GetTemplateChild(NumericUpTemplateName) as RepeatButton;
            _repeatDown = GetTemplateChild(NumericDownTemplateName) as RepeatButton;
            if (_valueTextBox == null || _repeatUp == null || _repeatDown == null)
            {
                throw new NullReferenceException(string.Format("You have missed to specify {0}, {1} or {2} in your template", NumericUpTemplateName, NumericDownTemplateName, TextBoxTemplateName));
            }
            _repeatUp.PreviewMouseUp += OnRepeatButtonPreviewMouseUp;
            _repeatDown.PreviewMouseUp += OnRepeatButtonPreviewMouseUp;
            ToggleReadOnlyMode(IsReadOnly);
            OnValueChanged(Value, Value);
        }
        protected override void OnGotFocus(RoutedEventArgs e)
        {
            base.OnGotFocus(e);
            if (Focusable && !IsReadOnly)
            {
                Focused();
                SelectAll();
            }
        }
        protected override void OnPreviewMouseWheel(MouseWheelEventArgs e)
        {
            base.OnPreviewMouseWheel(e);
            if (e.Delta != 0 && (IsFocused || _valueTextBox.IsFocused))
                ContinueChangeValue(e.Delta >= 0, false);
        }
        protected override void OnPreviewKeyDown(KeyEventArgs e)
        {
            base.OnPreviewKeyDown(e);
            switch (e.Key)
            {
                case Key.Enter:
                    DealInputText(_valueTextBox.Text);
                    SelectAll();
                    e.Handled = true;
                    break;
                case Key.Up:
                    ContinueChangeValue(true);
                    e.Handled = true;
                    break;
                case Key.Down:
                    ContinueChangeValue(false);
                    e.Handled = true;
                    break;
            }
        }
        protected override void OnPreviewKeyUp(KeyEventArgs e)
        {
            base.OnPreviewKeyUp(e);
            switch (e.Key)
            {
                case Key.Down:
                case Key.Up:
                    ResetInternal();
                    break;
            }
        }
        #endregion
        #region Event 
        private void OnRepeatButtonPreviewMouseUp(object sender, MouseButtonEventArgs e)
        {
            ResetInternal();
        }
        private void OnPreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            if (Focusable && !IsReadOnly && !_valueTextBox.IsKeyboardFocusWithin)
            {
                e.Handled = true;
                Focused();
                SelectAll();
            }
        }
        private void OnTextBoxLostFocus(object sender, RoutedEventArgs e)
        {
            var tb = (TextBox)sender;
            DealInputText(tb.Text);
        }
        private void OnValueTextBoxPaste(object sender, DataObjectPastingEventArgs e)
        {
            var textBox = (TextBox)sender;
            var textPresent = textBox.Text;
            if (!e.SourceDataObject.GetDataPresent(DataFormats.Text, true))
                return;
            var text = e.SourceDataObject.GetData(DataFormats.Text) as string;
            var newText = string.Concat(textPresent.Substring(0, textBox.SelectionStart), text, textPresent.Substring(textBox.SelectionStart + textBox.SelectionLength));
            double number;
            if (!double.TryParse(newText, out number))
                e.CancelCommand();
        }
        #endregion
        #region Private
        private void UnsubscribeEvents()
        {
            if (_valueTextBox != null)
            {
                _valueTextBox.LostFocus -= OnTextBoxLostFocus;
                _valueTextBox.PreviewMouseLeftButtonDown -= OnPreviewMouseLeftButtonDown;
                DataObject.RemovePastingHandler(_valueTextBox, OnValueTextBoxPaste);
            }
            if (_repeatUp != null)
                _repeatUp.PreviewMouseUp -= OnRepeatButtonPreviewMouseUp;
            if (_repeatDown != null)
                _repeatDown.PreviewMouseUp -= OnRepeatButtonPreviewMouseUp;
        }
        private void Focused()
        {
            _valueTextBox?.Focus();
        }
        private void SelectAll()
        {
            _valueTextBox?.SelectAll();
        }
        private void DealInputText(string inputText)
        {
            double convertedValue;
            if (double.TryParse(inputText, out convertedValue))
            {
                if (DoubleUtil.AreClose(Value, convertedValue))
                {
                    InternalSetText(Value);
                    return;
                }
                _isManual = true;
                if (convertedValue > Maximum)
                {
                    if (DoubleUtil.AreClose(Value, Maximum))
                        OnValueChanged(Value, Value);
                    else
                        Value = Maximum;
                }
                else if (convertedValue < Minimum)
                {
                    if (DoubleUtil.AreClose(Value, Minimum))
                        OnValueChanged(Value, Value);
                    else
                        Value = Minimum;
                }
                else
                    Value = convertedValue;
            }
            else
                InternalSetText(Value);
        }
        private void MoveFocus()
        {
            var request = new TraversalRequest((Keyboard.Modifiers & ModifierKeys.Shift) == ModifierKeys.Shift ? FocusNavigationDirection.Previous : FocusNavigationDirection.Next);
            var elementWithFocus = Keyboard.FocusedElement as UIElement;
            elementWithFocus?.MoveFocus(request);
        }
        private void ToggleReadOnlyMode(bool isReadOnly)
        {
            if (_valueTextBox == null)
                return;
            if (isReadOnly)
            {
                _valueTextBox.LostFocus -= OnTextBoxLostFocus;
                _valueTextBox.PreviewMouseLeftButtonDown -= OnPreviewMouseLeftButtonDown;
                DataObject.RemovePastingHandler(_valueTextBox, OnValueTextBoxPaste);
            }
            else
            {
                _valueTextBox.LostFocus += OnTextBoxLostFocus;
                _valueTextBox.PreviewMouseLeftButtonDown += OnPreviewMouseLeftButtonDown;
                DataObject.AddPastingHandler(_valueTextBox, OnValueTextBoxPaste);
            }
        }
        private void InternalSetText(double newValue)
        {
            var text = newValue.ToString(GetPrecisionFormat());
            if (_valueTextBox != null && !Equals(text, _valueTextBox.Text))
                _valueTextBox.Text = text;
        }
        private string GetPrecisionFormat()
        {
            return Precision.HasValue == false
                ? "g"
                : (Precision.Value == 0
                    ? "#0"
                    : ("#0.0" + string.Join("", Enumerable.Repeat("#", Precision.Value - 1))));
        }
        private void CoerceValue(DependencyProperty dp, object localValue)
        {
            SetCurrentValue(dp, localValue);
            CoerceValue(dp);
        }
        private double CorrectPrecision(int? precision, double originValue)
        {
            return Math.Round(originValue, precision ?? 0, MidpointRounding.AwayFromZero);
        }
        private void ContinueChangeValue(bool isIncrease, bool isContinue = true)
        {
            if (IsReadOnly || !IsEnabled)
                return;
            if (isIncrease && DoubleUtil.LessThan(Value, Maximum))
            {
                if (!_isBusy && isContinue)
                {
                    _isBusy = true;
                    if (DisabledValueChangedWhileBusy)
                        _lastOldValue = Value;
                }
                _isManual = true;
                Value = (double)CoerceValue(this, Value + CalculateInterval(isContinue));
            }
            if (!isIncrease && DoubleUtil.GreaterThan(Value, Minimum))
            {
                if (!_isBusy && isContinue)
                {
                    _isBusy = true;
                    if (DisabledValueChangedWhileBusy)
                        _lastOldValue = Value;
                }
                _isManual = true;
                Value = (double)CoerceValue(this, Value - CalculateInterval(isContinue));
            }
        }
        private double CalculateInterval(bool isContinue = true)
        {
            if (!Speedup || !isContinue)
                return Interval;
            if (DoubleUtil.GreaterThan((_intervalValueSinceReset += _internalLargeChange), _internalLargeChange * 100))
                _internalLargeChange *= 10;
            return _internalLargeChange;
        }
        private void ResetInternal()
        {
            _internalLargeChange = Interval;
            _intervalValueSinceReset = 0;
            _isBusy = false;
            if (_lastOldValue.HasValue)
            {
                _isManual = true;
                OnValueChanged(_lastOldValue.Value, Value);
                _lastOldValue = null;
            }
        }
        private void InvalidateRequerySuggested(double value)
        {
            if (_repeatUp == null || _repeatDown == null)
                return;
            if (DoubleUtil.AreClose(value, Maximum) && _repeatUp.IsEnabled
               || DoubleUtil.AreClose(value, Minimum) && _repeatDown.IsEnabled)
                CommandManager.InvalidateRequerySuggested();
            else
            {
                if (!_repeatUp.IsEnabled || !_repeatDown.IsEnabled)
                    CommandManager.InvalidateRequerySuggested();
            }
        }
        #endregion
    }
}

2)新增 NumericBox.xaml 代码如下:

  • 上下按钮部分使用了一个垂直的 Grid,其中包含三个行定义。第一行是上按钮,第二行是一个高度为 1 的占位行,第三行是下按钮。
  • 上按钮和下按钮都是 RepeatButton 类型,并分别命名为 PART_NumericUp 和 PART_NumericDown。它们分别与 controls:NumericBox.IncreaseCommand 和 controls:NumericBox.DecreaseCommand 命令关联。
  • 样式的触发器部分定义了一个触发条件,当 UpDownButtonsWidth 属性的值为 0 时,将隐藏上下按钮。
<ResourceDictionary
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:controls="clr-namespace:WPFDevelopers.Controls">
    <ResourceDictionary.MergedDictionaries>
        <ResourceDictionary Source="Basic/ControlBasic.xaml" />
    </ResourceDictionary.MergedDictionaries>
    <Style
        x:Key="WD.NumericBox"
        BasedOn="{StaticResource WD.ControlBasicStyle}"
        TargetType="{x:Type controls:NumericBox}">
        <Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Disabled" />
        <Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Disabled" />
        <Setter Property="BorderThickness" Value="1" />
        <Setter Property="BorderBrush" Value="{DynamicResource WD.BaseSolidColorBrush}" />
        <Setter Property="Background" Value="{DynamicResource WD.BackgroundSolidColorBrush}" />
        <Setter Property="HorizontalContentAlignment" Value="Stretch" />
        <Setter Property="VerticalContentAlignment" Value="Center" />
        <Setter Property="FocusVisualStyle" Value="{x:Null}" />
        <Setter Property="Padding" Value="{StaticResource WD.DefaultPadding}" />
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type controls:NumericBox}">
                    <controls:SmallPanel Width="{TemplateBinding Width}" Height="{TemplateBinding Height}">
                        <Border
                            x:Name="PART_Border"
                            Background="{TemplateBinding Background}"
                            BorderBrush="{TemplateBinding BorderBrush}"
                            BorderThickness="{TemplateBinding BorderThickness}"
                            CornerRadius="{Binding Path=(helpers:ElementHelper.CornerRadius), RelativeSource={RelativeSource TemplatedParent}}"
                            SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}">
                            <Grid>
                                <Grid.ColumnDefinitions>
                                    <ColumnDefinition />
                                    <ColumnDefinition Width="Auto" />
                                </Grid.ColumnDefinitions>
                                <TextBox
                                    x:Name="PART_TextBox"
                                    MinHeight="{TemplateBinding MinHeight}"
                                    Padding="{TemplateBinding Padding}"
                                    HorizontalAlignment="Stretch"
                                    VerticalAlignment="Center"
                                    HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
                                    VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
                                    Background="{x:Null}"
                                    BorderThickness="0"
                                    FocusVisualStyle="{x:Null}"
                                    Focusable="{TemplateBinding Focusable}"
                                    FontFamily="{TemplateBinding FontFamily}"
                                    FontSize="{TemplateBinding FontSize}"
                                    Foreground="{TemplateBinding Foreground}"
                                    HorizontalScrollBarVisibility="{TemplateBinding ScrollViewer.HorizontalScrollBarVisibility}"
                                    InputMethod.IsInputMethodEnabled="False"
                                    IsReadOnly="{TemplateBinding IsReadOnly}"
                                    IsTabStop="{TemplateBinding IsTabStop}"
                                    SelectionBrush="{DynamicResource WD.WindowBorderBrushSolidColorBrush}"
                                    SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
                                    Style="{x:Null}"
                                    TabIndex="{TemplateBinding TabIndex}"
                                    TextAlignment="{TemplateBinding TextAlignment}"
                                    VerticalScrollBarVisibility="{TemplateBinding ScrollViewer.VerticalScrollBarVisibility}" />
                                <Grid Grid.Column="1" Width="{TemplateBinding UpDownButtonsWidth}">
                                    <Grid.RowDefinitions>
                                        <RowDefinition />
                                        <RowDefinition Height="1" />
                                        <RowDefinition />
                                    </Grid.RowDefinitions>
                                    <RepeatButton
                                        x:Name="PART_NumericUp"
                                        Grid.Row="0"
                                        Margin="0,1,1,0"
                                        Command="{x:Static controls:NumericBox.IncreaseCommand}"
                                        Delay="{TemplateBinding Delay}"
                                        IsTabStop="False"
                                        Style="{StaticResource WD.DefaultRepeatButton}">
                                        <controls:PathIcon Kind="ChevronUp" Style="{StaticResource WD.MiniPathIcon}" />
                                    </RepeatButton>
                                    <RepeatButton
                                        x:Name="PART_NumericDown"
                                        Grid.Row="2"
                                        Margin="0,0,1,1"
                                        Command="{x:Static controls:NumericBox.DecreaseCommand}"
                                        Delay="{TemplateBinding Delay}"
                                        IsTabStop="False">
                                        <controls:PathIcon Kind="ChevronDown" Style="{StaticResource WD.MiniPathIcon}" />
                                    </RepeatButton>
                                </Grid>
                            </Grid>
                        </Border>
                    </controls:SmallPanel>
                    <ControlTemplate.Triggers>
                        <Trigger Property="UpDownButtonsWidth" Value="0">
                            <Setter TargetName="PART_NumericDown" Property="Visibility" Value="Collapsed" />
                            <Setter TargetName="PART_NumericUp" Property="Visibility" Value="Collapsed" />
                        </Trigger>
                        <Trigger Property="IsReadOnly" Value="True">
                            <Setter Property="Focusable" Value="False" />
                            <Setter TargetName="PART_NumericDown" Property="IsEnabled" Value="False" />
                            <Setter TargetName="PART_NumericUp" Property="IsEnabled" Value="False" />
                        </Trigger>
                        <Trigger Property="IsMouseOver" Value="True">
                            <Setter Property="BorderBrush" Value="{DynamicResource WD.PrimaryNormalSolidColorBrush}" />
                        </Trigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
    <Style BasedOn="{StaticResource WD.NumericBox}" TargetType="{x:Type controls:NumericBox}" />
</ResourceDictionary>

3)示例 代码如下:

            <wd:NumericBox
                Width="100"
                Maximum="100"
                Minimum="0" />
            <wd:NumericBox
                Width="100"
                Margin="10,0"
                Maximum="1000"
                Minimum="100"
                UpDownButtonsWidth="0" />
            <wd:NumericBox Width="100" Precision="3" />

效果图

以上就是基于WPF实现数字框控件的详细内容,更多关于WPF数字框控件的资料请关注脚本之家其它相关文章!

相关文章

  • 那些年,我还在学习C# 学习笔记

    那些年,我还在学习C# 学习笔记

    那些年学了ASP.NET后,才开始学习C#,说来也怪,怎么学了ASP.NET才来学习C#,其实没有什么的
    2012-03-03
  • 详谈C++引用&和指针在作为形参时的区别

    详谈C++引用&和指针在作为形参时的区别

    下面小编就为大家带来一篇详谈C++引用&和指针在作为形参时的区别。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-03-03
  • C#中静态方法和实例化方法的区别、使用

    C#中静态方法和实例化方法的区别、使用

    这篇文章主要介绍了C#中静态方法和实例化方法的区别、使用,文中讲解的非常细致,对大家的学习有所帮助,感兴趣的朋友可以了解下
    2020-06-06
  • C#使用AutoMapper实现类映射详解

    C#使用AutoMapper实现类映射详解

    AutoMapper是一个用于.NET中简化类之间的映射的扩展库,这篇文章主要介绍了C#如何使用AutoMapper实现类映射,感兴趣的小伙伴可以跟随小编一起学习一下
    2024-01-01
  • C#中深拷贝和浅拷贝的介绍与用法

    C#中深拷贝和浅拷贝的介绍与用法

    本文详细讲解了C#中深拷贝和浅拷贝的介绍与用法,文中通过示例代码介绍的非常详细。对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2022-03-03
  • C#中利用LINQ to XML与反射把任意类型的泛型集合转换成XML格式字符串的方法

    C#中利用LINQ to XML与反射把任意类型的泛型集合转换成XML格式字符串的方法

    本文主要介绍了C#中利用LINQ to XML与反射把任意类型的泛型集合转换成XML格式字符串的方法:利用反射,读取一个类型的所有属性,然后再把属性转换成XML元素的属性或者子元素。下面注释比较完整,需要的朋友可以看下
    2016-12-12
  • 用C#的params关键字实现方法形参个数可变示例

    用C#的params关键字实现方法形参个数可变示例

    params关键字以实现方法形参个数可变是C#语法的一大优点,下面是用C#中的params关键字实现方法形参个数可变
    2014-09-09
  • 深入理解C#之接口

    深入理解C#之接口

    这篇文章主要介绍了C#接口(Interface)用法,较为详细的分析了C#中接口的功能、实现及使用技巧,具有一定参考借鉴价值,需要的朋友可以参考下
    2021-07-07
  • C#算法之整数反转

    C#算法之整数反转

    这篇文章介绍了C#算法之整数反转,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2022-01-01
  • c# 动态构建LINQ查询表达式

    c# 动态构建LINQ查询表达式

    这篇文章主要介绍了c# 如何动态构建LINQ查询表达式,帮助大家更好的理解和学习c#,感兴趣的朋友可以了解下
    2020-11-11

最新评论