WPF较完备的自定义NumericDownUp控件

     主要功能:

         1)、左右RepeatedButton;

         2)、支持最小,最大值以及输入的检测,回滚;

         3)、支持每次减少/增加的值幅度大小;

         4)、支持前缀Label,长度,可见性。

第1步、构造控件的具体样式

    完整XAML代码如下:

<UserControl x:Class="WpfUserControlApp11.MyNumUpDown"
             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:WpfUserControlApp11"
             mc:Ignorable="d" 
             d:DesignHeight="36" d:DesignWidth="100">

	<Grid>
		<Grid.ColumnDefinitions>
			<ColumnDefinition Width="auto" MinWidth="0"/>
			<ColumnDefinition Width="6*" MinWidth="20"/>
			<ColumnDefinition Width="9*" MinWidth="36"/>
			<ColumnDefinition Width="6*" MinWidth="20"/>
		</Grid.ColumnDefinitions>

		<Label 
			Name="myNumLabelName" 
			Grid.Row="0" 
			Grid.Column="0" 
			Margin="0" 
			VerticalAlignment="Center" 
			HorizontalAlignment="Left" 
			Foreground="Black" 
			FontSize="12" />
		<RepeatButton Grid.Column="1" x:Name="btnSub" VerticalContentAlignment="Center" HorizontalContentAlignment="Center" Content=" - " Margin="0 0 0 0" Click="btnSubClick" FontSize="20" 
			ClickMode="Press"
			Delay="500"
			Interval="80">
			<RepeatButton.Style>
				<Style TargetType="RepeatButton">
					<Setter Property="Background" Value="#DDDDDD"/>
					<Setter Property="Template">
						<Setter.Value>
							<ControlTemplate TargetType="{x:Type RepeatButton}">
								<Border BorderBrush="{TemplateBinding Control.BorderBrush}" Background="{TemplateBinding Background}" BorderThickness="0.5 0.5 0 0.5" CornerRadius="6 0 0 6">
									<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
								</Border>
							</ControlTemplate>
						</Setter.Value>
					</Setter>
					<Style.Triggers>
						<Trigger Property="IsMouseOver" Value="True">
							<Setter Property="Background" Value="#87CEFA"/>
						</Trigger>
						<Trigger Property="IsPressed" Value="True">
							<Setter Property="Background" Value="#30BCFA"/>
							<Setter Property="Foreground" Value="#FFFFFF"/>
						</Trigger>
						<Trigger Property="IsEnabled" Value="False">
							<Setter Property="Background" Value="WhiteSmoke"/>
						</Trigger>
					</Style.Triggers>
				</Style>
			</RepeatButton.Style>
		</RepeatButton>
		<TextBox 
			Grid.Column="2" 
			x:Name="tbContent" 		
			Margin="0 0 0 0" 
			FontSize="12" 
			HorizontalContentAlignment="Center" 
			VerticalContentAlignment="Center" 
			TextChanged="tbContentTextChanged" 
			GotFocus="tbContentGotFocus"
			LostFocus="tbCntLostFocus"
			Text="0">
			<TextBox.Style>
				<Style TargetType="TextBox">
					<Setter Property="Background" Value="#FFFFFF"/>
					<Style.Triggers>
						<Trigger Property="IsMouseOver" Value="True">
							<Setter Property="Background" Value="#F0F8FF"/>
						</Trigger>
						<Trigger Property="IsEnabled" Value="False">
							<Setter Property="Background" Value="#FFFFFF"/>
						</Trigger>
					</Style.Triggers>
				</Style>
			</TextBox.Style>
		</TextBox>
		<RepeatButton Grid.Column="3" x:Name="btnAdd" VerticalContentAlignment="Center" HorizontalContentAlignment="Center" Content=" + " Margin="0 0 0 0" Click="btnAddClick" FontSize="20"
			ClickMode="Press"
			Delay="500"
			Interval="80">
			<RepeatButton.Style>
				<Style TargetType="RepeatButton">
					<Setter Property="Background" Value="#DDDDDD"/>
					<Setter Property="Template">
						<Setter.Value>
							<ControlTemplate TargetType="{x:Type RepeatButton}">
								<Border BorderBrush="{TemplateBinding Control.BorderBrush}" Background="{TemplateBinding Background}" BorderThickness="0 0.5 0.5 0.5" CornerRadius="0 6 6 0">
									<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
								</Border>
							</ControlTemplate>
						</Setter.Value>
					</Setter>
					<Style.Triggers>
						<Trigger Property="IsMouseOver" Value="True">
							<Setter Property="Background" Value="#87CEFA"/>
						</Trigger>
						<Trigger Property="IsPressed" Value="True">
							<Setter Property="Background" Value="#30BCFA"/>
							<Setter Property="Foreground" Value="#FFFFFF"/>
						</Trigger>
						<Trigger Property="IsEnabled" Value="False">
							<Setter Property="Background" Value="WhiteSmoke"/>
						</Trigger>
					</Style.Triggers>
				</Style>
			</RepeatButton.Style>
		</RepeatButton>
	</Grid>
</UserControl>

 第2步、控件的业务逻辑CS代码

    完整的C#代码如下:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace WpfUserControlApp11
{
	/// <summary>
	/// MyNumUpDown.xaml 的交互逻辑
	/// </summary>
	public partial class MyNumUpDown : UserControl
	{
		/// <summary>
		/// 当前值
		/// </summary>
		private decimal AvaliableVal = 0;

		/// <summary>
		/// Sync Resource
		/// </summary>
		private static object lockObj = new object();
		public MyNumUpDown()
		{
			InitializeComponent();
		}

		/// <summary>
		/// label 显示内容
		/// </summary>
		public string LabeContent
		{
			get { return GetValue(LabeContentProperty).ToString(); }
			set { SetValue(LabeContentProperty, value); }
		}

		/// <summary>
		/// 最小值
		/// </summary>
		public decimal MinValue
		{
			get { return Convert.ToDecimal(GetValue(MinValueProperty)); }
			set { SetValue(MinValueProperty, value); }
		}

		/// <summary>
		/// 最大值
		/// </summary>
		public decimal MaxValue
		{
			get { return Convert.ToDecimal(GetValue(MaxValueProperty)); }
			set
			{
				SetValue(MaxValueProperty, value);
			}
		}

		/// <summary>
		/// 默认值
		/// </summary>
		public decimal DefaultValue
		{
			get { return Convert.ToDecimal(GetValue(DefaultValueProperty)); }
			set 
			{ 
				SetValue(DefaultValueProperty, value); 
				NumericValue = DefaultValue; 
			}
		}

		/// <summary>
		/// 是否整数
		/// </summary>
		public bool IsInteger
		{
			get { return Convert.ToBoolean(GetValue(IsIntegerProperty)); }
			set { SetValue(IsIntegerProperty, value); }
		}

		/// <summary>
		/// 步长
		/// </summary>
		public decimal OffsetValue
		{
			get { return Convert.ToDecimal(GetValue(OffsetValueProperty)); }
			set { SetValue(OffsetValueProperty, value); }
		}

		/// <summary>
		/// 输入框值
		/// </summary>
		public decimal NumericValue
		{
			get { return Convert.ToDecimal(GetValue(NumericValueProperty)); }
			set
			{
				SetValue(NumericValueProperty, value);
			}
		}

		/// <summary>
		/// 左侧的Label的宽度
		/// </summary>
		public double LabelWidth
		{
			get { return Convert.ToDouble(GetValue(LabelWidthValueProperty)); }
			set { SetValue(LabelWidthValueProperty, value); }
		}

		/// <summary>
		/// 标签的可见性
		/// </summary>
		public string LabelVisibility
		{
			get { return Convert.ToString(GetValue(LabelVisibilityValueProperty)); }
			set { SetValue(LabelVisibilityValueProperty, value); }
		}

		public static readonly DependencyProperty LabeContentProperty =
			 DependencyProperty.Register("LabeContent",
				 typeof(string),
				 typeof(MyNumUpDown),
				 new PropertyMetadata("", new PropertyChangedCallback(LabelContentCallback))
		 );

		private static void LabelContentCallback(DependencyObject obj, DependencyPropertyChangedEventArgs e)
		{
			MyNumUpDown control = obj as MyNumUpDown;
			control.myNumLabelName.Content = e.NewValue.ToString();
		}

		public static readonly DependencyProperty MinValueProperty =
			DependencyProperty.Register("MinValue",
				typeof(decimal),
				typeof(MyNumUpDown),
				new PropertyMetadata(-1m, null)
		 );

		public static readonly DependencyProperty MaxValueProperty =
			DependencyProperty.Register("MaxValue",
				typeof(decimal),
				typeof(MyNumUpDown),
				new PropertyMetadata(-1m, new PropertyChangedCallback(MaxValueChangedCallback))
		 );

		public static readonly DependencyProperty DefaultValueProperty =
			DependencyProperty.Register("DefaultValue",
				typeof(decimal),
				typeof(MyNumUpDown)
		 );

		public static readonly DependencyProperty IsIntegerProperty =
			DependencyProperty.Register("IsInteger",
				typeof(bool),
				typeof(MyNumUpDown), new PropertyMetadata(false)
		 );

		public static readonly DependencyProperty OffsetValueProperty =
			 DependencyProperty.Register("OffsetValue",
				typeof(decimal),
				typeof(MyNumUpDown), new PropertyMetadata(1m)
		 );

		public static readonly DependencyProperty NumericValueProperty =
			DependencyProperty.Register("NumericValue",
			   typeof(decimal),
			   typeof(MyNumUpDown),
			   new PropertyMetadata(new PropertyChangedCallback(NumericValueChangedCallback))
		);

		public static readonly DependencyProperty LabelWidthValueProperty =
			DependencyProperty.Register("LabelWidth",
			   typeof(double),
			   typeof(MyNumUpDown),
			   new PropertyMetadata(0.0, new PropertyChangedCallback(LabelWidthCallback))
		 );

		public static readonly DependencyProperty LabelVisibilityValueProperty =
			DependencyProperty.Register("LabelVisibility",
			   typeof(string),
			   typeof(MyNumUpDown),
			   new PropertyMetadata("Visible", new PropertyChangedCallback(LabelVisibilityCallback))
		 );

		private static void LabelWidthCallback(DependencyObject obj, DependencyPropertyChangedEventArgs e)
		{
			MyNumUpDown control = obj as MyNumUpDown;
			control.myNumLabelName.Width = double.Parse(string.IsNullOrEmpty(e.NewValue.ToString()) ? "0" : e.NewValue.ToString());
		}

		private static void LabelVisibilityCallback(DependencyObject obj, DependencyPropertyChangedEventArgs e)
		{
			MyNumUpDown control = obj as MyNumUpDown;
			string visi = e.NewValue.ToString();
			if (visi.Equals(Visibility.Visible.ToString()))
				control.myNumLabelName.Visibility = Visibility.Visible;
			else if (visi.Equals(Visibility.Hidden.ToString()))
				control.myNumLabelName.Visibility = Visibility.Hidden;
			else
				control.myNumLabelName.Visibility = Visibility.Collapsed;
		}

		private static void MaxValueChangedCallback(DependencyObject obj, DependencyPropertyChangedEventArgs e)
		{
			MyNumUpDown control = obj as MyNumUpDown;
			control.tbContent.MaxLength = e.NewValue.ToString().Length;
		}

		private static void NumericValueChangedCallback(DependencyObject obj, DependencyPropertyChangedEventArgs e)
		{
			MyNumUpDown control = obj as MyNumUpDown;
			control.AvaliableVal = Convert.ToDecimal(e.NewValue);
			control.tbContent.Text = e.NewValue.ToString();
			control.tbContent.SelectionStart = control.tbContent.Text.Length;
		}

		/// <summary>
		/// 增加按钮
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void btnAddClick(object sender, RoutedEventArgs e)
		{
			decimal offsetValue = OffsetValue;
			AvaliableVal = Convert.ToDecimal(tbContent.Text);
			decimal newValue;

			if (AvaliableVal > MaxValue)
			{
				NumericValue = MaxValue;
				return;
			}
			if (AvaliableVal + offsetValue > MaxValue)
			{
				NumericValue = MaxValue;
				return;
			}
			newValue = AvaliableVal + offsetValue;
			if (newValue < MinValue)
			{
				NumericValue = MinValue;
				return;
			}
			if (IsInteger)
				newValue = decimal.Parse(newValue.ToString("0"));
			else
				newValue = decimal.Parse(newValue.ToString("0.######"));
			tbContent.Text = newValue.ToString();
			NumericValue = newValue;
		}

		/// <summary>
		/// 减少按钮
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void btnSubClick(object sender, RoutedEventArgs e)
		{
			AvaliableVal = Convert.ToDecimal(tbContent.Text);
			if (AvaliableVal < MinValue)
			{
				NumericValue = MinValue;
				return;
			}
			if (AvaliableVal - OffsetValue < MinValue)
			{
				NumericValue = MinValue;
				return;
			}
			if (AvaliableVal > MaxValue)
			{
				NumericValue = MaxValue;
				return;
			}
			decimal newValue;
			newValue = AvaliableVal - OffsetValue;
			if (IsInteger)
				newValue = decimal.Parse(newValue.ToString("0"));
			else
				newValue = decimal.Parse(newValue.ToString("0.######"));
			tbContent.Text = newValue.ToString();
			NumericValue = newValue;
		}

		/// <summary>
		/// 获取焦点
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void tbContentGotFocus(object sender, RoutedEventArgs e)
		{
			TextBox tb = sender as System.Windows.Controls.TextBox;
			this.AvaliableVal = Convert.ToDecimal(tb.Text);
		}

		/// <summary>
		/// 是否数字
		/// </summary>
		/// <param name="input"></param>
		/// <returns></returns>
		private bool IsNumber(string input)
		{
			string pattern = "^-?\\d+$|^(-?\\d+)(\\.\\d+)?$";
			Regex regex = new Regex(pattern);
			return regex.IsMatch(input);
		}

		/// <summary>
		/// 是否整数
		/// </summary>
		/// <param name="strNumber"></param>
		/// <returns></returns>
		private bool IsWholeNumber(string strNumber)
		{
			Regex g = new Regex(@"^[0-9]\d*$");
			return g.IsMatch(strNumber);
		}

		/// <summary>
		/// 数字Text变动Event
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void tbContentTextChanged(object sender, TextChangedEventArgs e)
		{
			lock (lockObj)
			{
				TextBox tb = sender as TextBox;
				if (tb != null)
				{
					bool isNumeric = IsNumber(tb.Text);
					if (isNumeric)
					{
						var vv = Convert.ToDecimal(tb.Text);
						if (vv > MaxValue && MaxValue != -1)
						{
							//此句为了解决实际应用时数据刷新的问题
							NumericValue = MaxValue - 1;
							try
							{
								System.Threading.Thread.Sleep(0);
							}
							catch { }
							NumericValue = MaxValue;
							return;
						}
						NumericValue = vv;
					}
					else
					{
						//NumericValue = null;
					}
				}
			}
		}

		/// <summary>
		/// Lost焦点事件处理
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void tbCntLostFocus(object sender, RoutedEventArgs e)
		{
			lock (lockObj)
			{
				TextBox tb = sender as TextBox;
				bool isNumeric = IsNumber(tb.Text);
				if (isNumeric && IsInteger)
				{
					isNumeric = IsWholeNumber(tb.Text);
				}
				decimal oldValue = this.AvaliableVal;
				if (isNumeric)
				{
					decimal ctrlValue = Convert.ToDecimal(tb.Text);
					if (ctrlValue < MinValue)
					{
						if (oldValue <= MaxValue && oldValue >= MinValue)
							NumericValue = oldValue;
						else
							NumericValue = MinValue;
						AvaliableVal = NumericValue;
					}
					else if (ctrlValue > MaxValue)
					{
						if (oldValue <= MaxValue && oldValue >= MinValue)
							NumericValue = oldValue;
						else
							NumericValue = MaxValue;
						AvaliableVal = NumericValue;
					}
					else
					{
						AvaliableVal = ctrlValue;
						NumericValue = AvaliableVal;
					}
				}
				else
				{
					if (oldValue > MaxValue || oldValue < MinValue)
						oldValue = DefaultValue;
					tb.Text = oldValue.ToString();
					NumericValue = oldValue;
				}
			}
		}
	}
}

第3步、测试控件

MainWindow.xaml如下,无cs业务代码:

<Window x:Class="WpfUserControlApp11.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        mc:Ignorable="d"		
		xmlns:uc="clr-namespace:WpfUserControlApp11" 
		Title="MainWindow" Height="300" Width="600">
	
	<Grid Margin="10">
		<Grid.RowDefinitions>
			<RowDefinition Height="Auto" />
		</Grid.RowDefinitions>

		<StackPanel Grid.Row="0" Orientation="Horizontal">

			<uc:MyNumUpDown
				LabelVisibility="Visible"
				IsInteger="True"
				LabelWidth="70"
				IsEnabled="true"
				Width="250"
				Height="50" 
				x:Name="mNum3"
				NumericValue="108"
				OffsetValue="1" 
				MinValue="1" 
				MaxValue="9999" 
				VerticalAlignment="Center"
				LabeContent="产品价格:" 
				Margin="10"></uc:MyNumUpDown>
			
			<uc:MyNumUpDown
				LabelVisibility="Collapsed"
				IsInteger="True"
				LabelWidth="60"
				IsEnabled="false"
				Width="150"
				Height="50" 
				x:Name="mNum4"
				OffsetValue="1" 
				MinValue="0" 
				MaxValue="1000" 
				VerticalAlignment="Center"
				LabeContent="值:" 
				Margin="50,10,10,10"></uc:MyNumUpDown>			
			
		</StackPanel>
	</Grid>
</Window>

4、运行效果:

注:1)、以上代码添加到对应的VS工程代码中,即可正常运行出上述4的效果;

        2)、转载请注明出处

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
WPF工控软件自定义控件是指在基于.NET6框架下使用WPF MVVM进行UI设计时,开发者可以自定义控件来满足特定的功能需求。这些自定义控件可以根据项目的需求进行设计和修改,以提供更好的用户体验和功能支持。 如果你对WPF工控软件自定义控件感兴趣,我建议你可以下载并编译相关的示例代码,并尝试修改它们以适应你的项目需求。你可以在这个链接上找到相关界面的示例代码:https://blog.csdn.net/u010186391/article/details/125601226。通过实际操作和修改示例代码,你可以更好地理解和应用WPF自定义控件、样式、MVVM和异步线程等方面的知识。<span class="em">1</span><span class="em">2</span> #### 引用[.reference_title] - *1* [NET6+WPF+MVVM ](https://download.csdn.net/download/u010186391/85926327)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *2* [C# WPF 自定义控件 滑块控件 开关控件 ToggleButton Switcher](https://blog.csdn.net/shizu11zz/article/details/120459009)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值