WPF数字输入框和IP地址输入框
数字输入框
简介
在业务中,我们经常需要限制用户的输入,比如限制输入长度,限制只能输入数字等等。限制输入长度WPF内置的TextBox已经帮我们解决了,但是限制输入数字却并未在WPF中内置解决方案。使用第三方的控件又要多增加一个引用,于是决定自己写一个。
在写的过程中发现需要考虑的问题比较多,比如限制输入法、部分限制输入小数点和负号、限制输入字母和其它符号、粘贴时做特殊处理等等。值得一提的是,将文本绑定到Double型且将UpdateSourceTrigger设为PropertyChanged时,出现了界面上包含小数点,但是通过Text获取的文本却并不包含小数点的情况,猜测原因是因为绑定更新和自动类型转换出现的问题。
数字输入框提供了设置最小值(可用)、最大值(可用)、精度(小数点后位数)的功能。不合法的按键将会被过滤掉,输入的值小于最小值或者大于最大值时,其输入都将视为无效。
代码
代码较简单,且有注释,不再多说。
namespace YiYan127.WPF.Controls
{
using System;
using System.Globalization;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input; /// <summary>
/// 输入数值的文本框
/// </summary>
public class NumbericTextBox : TextBox
{
#region Fields #region DependencyProperty /// <summary>
/// 最大值的依赖属性
/// </summary>
public static readonly DependencyProperty MaxValueProperty = DependencyProperty.Register(
"MaxValue",
typeof(double),
typeof(NumbericTextBox),
new PropertyMetadata(double.MaxValue)); /// <summary>
/// 最小值的依赖属性
/// </summary>
public static readonly DependencyProperty MinValueProperty = DependencyProperty.Register(
"MinValue",
typeof(double),
typeof(NumbericTextBox),
new PropertyMetadata(double.MinValue)); /// <summary>
/// 精度的依赖属性
/// </summary>
public static readonly DependencyProperty PrecisionProperty = DependencyProperty.Register(
"Precision",
typeof(ushort),
typeof(NumbericTextBox),
new PropertyMetadata((ushort)2)); #endregion DependencyProperty /// <summary>
/// 先前合法的文本
/// </summary>
private string lastLegalText; /// <summary>
/// 是否为粘贴
/// </summary>
private bool isPaste; public event EventHandler<TextChangedEventArgs> PreviewTextChanged; #endregion Fields #region Constructor /// <summary>
/// 构造函数
/// </summary>
public NumbericTextBox()
{
this.PreviewTextInput += this.NumbericTextBoxPreviewTextInput;
this.TextChanged += this.NumbericTextBoxTextChanged;
this.PreviewKeyDown += this.NumbericTextBox_PreviewKeyDown;
this.LostFocus += this.NumbericTextBoxLostFocus;
InputMethod.SetIsInputMethodEnabled(this, false); this.Loaded += this.NumbericTextBoxLoaded;
} #endregion Constructor #region Properties /// <summary>
/// 最大值,可取
/// </summary>
public double MaxValue
{
get { return (double)this.GetValue(MaxValueProperty); }
set { this.SetValue(MaxValueProperty, value); }
} /// <summary>
/// 最小值,可取
/// </summary>
public double MinValue
{
get { return (double)this.GetValue(MinValueProperty); }
set { this.SetValue(MinValueProperty, value); }
} /// <summary>
/// 精度,即精确到小数点后的位数
/// </summary>
public ushort Precision
{
get { return (ushort)this.GetValue(PrecisionProperty); }
set { this.SetValue(PrecisionProperty, value); }
} #endregion Properties protected virtual void OnPreviewTextChanged(TextChangedEventArgs e)
{
if (this.PreviewTextChanged != null)
{
this.PreviewTextChanged(this, e);
}
} #region Private Methods /// <summary>
/// 处理粘贴的情况
/// </summary>
protected virtual void HandlePaste()
{
this.isPaste = false; // 处理符号的标志
bool handledSybmol = false; // 处理小数点的标志
bool handledDot = false; // 当前位对应的基数
double baseNumber = 1; // 转换后的数字
double number = 0; // 上一次合法的数字
double lastNumber = 0; // 小数点后的位数
double precision = 0;
foreach (var c in this.Text)
{
if (!handledSybmol && (c == '-'))
{
baseNumber = -1;
handledSybmol = true;
} if ((c >= '0') && (c <= '9'))
{
int digit = c - '0';
if (!handledDot)
{
number = (number * baseNumber) + digit;
baseNumber = 10;
}
else
{
baseNumber = baseNumber / 10;
number += digit * baseNumber;
} // 正负号必须位于最前面
handledSybmol = true;
} if (c == '.')
{
// 精度已经够了
if (precision + 1 > this.Precision)
{
break;
} handledDot = true; // 此时正负号不能起作用
handledSybmol = true;
baseNumber = 0.1;
precision++;
} if ((number < this.MinValue) || (number > this.MaxValue))
{
this.Text = lastNumber.ToString(CultureInfo.InvariantCulture);
this.SelectionStart = this.Text.Length;
return;
} lastNumber = number;
} this.Text = number.ToString(CultureInfo.InvariantCulture);
this.SelectionStart = this.Text.Length;
} #endregion Private Methods #region Overrides of TextBoxBase #endregion #region Events Handling private void NumbericTextBoxLoaded(object sender, RoutedEventArgs e)
{
if (this.MinValue > this.MaxValue)
{
this.MinValue = this.MaxValue;
} if (string.IsNullOrEmpty(this.Text))
{
double val = (this.MaxValue + this.MinValue) / 2;
val = Math.Round(val, this.Precision); this.Text = val.ToString(CultureInfo.InvariantCulture);
} this.isPaste = true;
} /// <summary>
/// The numberic text box preview text input.
/// </summary>
/// <param name="sender"> The sender.</param>
/// <param name="e"> The e.</param>
private void NumbericTextBoxPreviewTextInput(object sender, TextCompositionEventArgs e)
{
// 如果是粘贴不会引发该事件
this.isPaste = false; short val; // 输入非数字
if (!short.TryParse(e.Text, out val))
{
// 小于0时,可输入负号
if ((this.MinValue < 0) && (e.Text == "-"))
{
int minusPos = this.Text.IndexOf('-'); // 未输入负号且负号在第一位
if ((minusPos == -1) && (0 == this.SelectionStart))
{
return;
}
} // 精度大于0时,可输入小数点
if ((this.Precision > 0) && (e.Text == "."))
{
// 解决UpdateSourceTrigger为PropertyChanged时输入小数点文本与界面不一致的问题
if (this.SelectionStart > this.Text.Length)
{
e.Handled = true;
return;
} // 小数点位置
int dotPos = this.Text.IndexOf('.'); // 未存在小数点可输入
if (dotPos == -1)
{
return;
} // 已存在小数点但处于选中状态,也可输入小数点
if ((this.SelectionStart >= dotPos) && (this.SelectionLength > 0))
{
return;
}
} e.Handled = true;
}
else
{
int dotPos = this.Text.IndexOf('.');
int cursorIndex = this.SelectionStart; // 已经存在小数点,且小数点在光标后
if ((dotPos != -1) && (dotPos < cursorIndex))
{
// 不允许输入超过精度的数
if (((this.Text.Length - dotPos) > this.Precision) && (this.SelectionLength == 0))
{
e.Handled = true;
}
}
}
} /// <summary>
/// The numberic text box text changed.
/// </summary>
/// <param name="sender"> The sender.</param>
/// <param name="e"> The e.</param>
private void NumbericTextBoxTextChanged(object sender, TextChangedEventArgs e)
{
if (this.lastLegalText == this.Text)
{
return;
} this.OnPreviewTextChanged(e); // 允许为空
if (string.IsNullOrEmpty(this.Text))
{
return;
} // 粘贴而来的文本
if (this.isPaste)
{
this.HandlePaste();
this.lastLegalText = this.Text; return;
} double val;
if (double.TryParse(this.Text, out val))
{
// 保存光标位置
int selectIndex = this.SelectionStart;
if ((val > this.MaxValue) || (val < this.MinValue))
{
this.Text = this.lastLegalText;
this.SelectionStart = selectIndex;
return;
} this.lastLegalText = this.Text;
} this.isPaste = true;
} /// <summary>
/// The numberic text box_ preview key down.
/// </summary>
/// <param name="sender"> The sender.</param>
/// <param name="e"> The e.</param>
private void NumbericTextBox_PreviewKeyDown(object sender, KeyEventArgs e)
{
// 过滤空格
if (e.Key == Key.Space)
{
e.Handled = true;
}
} /// <summary>
/// The numberic text box_ lost focus.
/// </summary>
/// <param name="sender"> The sender.</param>
/// <param name="e"> The e.</param>
private void NumbericTextBoxLostFocus(object sender, RoutedEventArgs e)
{
if (string.IsNullOrEmpty(this.Text))
{
this.Text = this.lastLegalText;
}
} #endregion Events Handling
}
}
IP地址输入框
IP地址输入框使用了上面的数字输入框,按点时可跳转到IP地址下一部分输入,支持IP地址的粘贴。代码较简单,且有注释,不再多说。
XAML
<UserControl x:Class="YiYan127.WPF.Controls.IpAddressControl" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="clr-namespace:YiYan127.WPF.Controls">
<UniformGrid Columns="4" TextBoxBase.GotFocus="TextBox_OnGotFocus">
<DockPanel Margin="5,2">
<TextBlock VerticalAlignment="Center" DockPanel.Dock="Right" Text="." />
<controls:NumbericTextBox x:Name="IPPart1" MaxValue="255" MinValue="0"
Precision="0" />
</DockPanel>
<DockPanel Margin="0,2,5,2">
<TextBlock VerticalAlignment="Center" DockPanel.Dock="Right" Text="." />
<controls:NumbericTextBox x:Name="IPPart2" MaxValue="255" MinValue="0"
Precision="0" />
</DockPanel>
<DockPanel Margin="0,2,5,2">
<TextBlock VerticalAlignment="Center" DockPanel.Dock="Right" Text="." />
<controls:NumbericTextBox x:Name="IPPart3" MaxValue="255" MinValue="0"
Precision="0" />
</DockPanel>
<controls:NumbericTextBox x:Name="IPPart4" Margin="0,2,5,2" MaxValue="255"
MinValue="0" Precision="0" />
</UniformGrid>
</UserControl>
后台代码
namespace YiYan127.WPF.Controls
{
using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Text.RegularExpressions;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input; /// <summary>
/// IP地址输入框
/// </summary>
public partial class IpAddressControl
{
#region Fields /// <summary>
/// IP地址的依赖属性
/// </summary>
public static readonly DependencyProperty IPProperty = DependencyProperty.Register(
"IP",
typeof(string),
typeof(IpAddressControl),
new FrameworkPropertyMetadata(DefaultIP, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, IPChangedCallback)); /// <summary>
/// IP地址的正则表达式
/// </summary>
public static readonly Regex IpRegex = new
Regex(@"^((2[0-4]\d|25[0-5]|(1\d{2})|([1-9]?[0-9]))\.){3}(2[0-4]\d|25[0-4]|(1\d{2})|([1-9][0-9])|([1-9]))$"); /// <summary>
/// 默认IP地址
/// </summary>
private const string DefaultIP = "127.0.0.1"; private static readonly Regex PartIprRegex = new Regex(@"^(\.?(2[0-4]\d|25[0-5]|(1\d{2})|([1-9]?[0-9]))\.?)+$"); /// <summary>
/// 输入框的集合
/// </summary>
private readonly List<NumbericTextBox> numbericTextBoxs = new List<NumbericTextBox>(); /// <summary>
/// 当前活动的输入框
/// </summary>
private NumbericTextBox currentNumbericTextBox; #endregion Fields #region Constructors public IpAddressControl()
{
InitializeComponent();
this.numbericTextBoxs.Add(this.IPPart1);
this.numbericTextBoxs.Add(this.IPPart2);
this.numbericTextBoxs.Add(this.IPPart3);
this.numbericTextBoxs.Add(this.IPPart4);
this.KeyUp += this.IpAddressControlKeyUp; this.UpdateParts(this); foreach (var numbericTextBox in this.numbericTextBoxs)
{
numbericTextBox.PreviewTextChanged += this.NumbericTextBox_OnPreviewTextChanged;
} foreach (var numbericTextBox in this.numbericTextBoxs)
{
numbericTextBox.TextChanged += this.TextBoxBase_OnTextChanged;
}
} #endregion Constructors #region Properties public string IP
{
get
{
return (string)GetValue(IPProperty);
} set
{
SetValue(IPProperty, value);
}
} #endregion Properties #region Private Methods /// <summary>
/// IP值改变的响应
/// </summary>
/// <param name="dependencyObject"></param>
/// <param name="dependencyPropertyChangedEventArgs"></param>
private static void IPChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
{
if (dependencyPropertyChangedEventArgs.NewValue == null)
{
throw new Exception("IP can not be null");
} var control = dependencyObject as IpAddressControl;
if (control != null)
{
control.UpdateParts(control);
}
} private void UpdateParts(IpAddressControl control)
{
string[] parts = control.IP.Split(new[] { '.' });
control.IPPart1.Text = parts[0];
control.IPPart2.Text = parts[1];
control.IPPart3.Text = parts[2];
control.IPPart4.Text = parts[3];
} #endregion Private Methods #region Event Handling /// <summary>
/// 按键松开的响应
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void IpAddressControlKeyUp(object sender, KeyEventArgs e)
{
if (e.Key == Key.OemPeriod || e.Key == Key.Decimal)
{
if (this.currentNumbericTextBox != null)
{
int index = this.numbericTextBoxs.IndexOf(this.currentNumbericTextBox);
int next = (index + 1) % this.numbericTextBoxs.Count;
this.numbericTextBoxs[next].Focus();
this.numbericTextBoxs[next].SelectionStart = this.numbericTextBoxs[next].Text.Length;
}
}
} /// <summary>
/// 获得焦点的响应
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void TextBox_OnGotFocus(object sender, RoutedEventArgs e)
{
this.currentNumbericTextBox = e.OriginalSource as NumbericTextBox;
} private void NumbericTextBox_OnPreviewTextChanged(object sender, TextChangedEventArgs e)
{
var numbericTextBox = sender as NumbericTextBox;
Contract.Assert(numbericTextBox != null); if (PartIprRegex.IsMatch(numbericTextBox.Text))
{
var ips = numbericTextBox.Text.Split('.'); if (ips.Length == 1)
{
return;
} int index = this.numbericTextBoxs.IndexOf(numbericTextBox);
int pointer2Ips = 0;
for (int i = index; i < this.numbericTextBoxs.Count; i++)
{
while (pointer2Ips < ips.Length && string.IsNullOrEmpty(ips[pointer2Ips]))
{
pointer2Ips++;
} if (pointer2Ips >= ips.Length)
{
return;
} this.numbericTextBoxs[i].Text = ips[pointer2Ips];
pointer2Ips++;
}
}
} private void TextBoxBase_OnTextChanged(object sender, TextChangedEventArgs e)
{
var ip = string.Format(
"{0}.{1}.{2}.{3}",
this.IPPart1.Text,
this.IPPart2.Text,
this.IPPart3.Text,
this.IPPart4.Text);
if (IpRegex.IsMatch(ip))
{
this.IP = ip;
}
} #endregion Event Handling
}
}
WPF数字输入框和IP地址输入框的更多相关文章
- [Qt] IP地址输入框实现
封装了一个ip地址的输入框.网络上下载了份代码,找不到哪里的了.经过修改之后,尽力让它的行为和windows的IP地址输入框的行为看起来像些.代码如下: //ipaddredit.h #ifndef ...
- IP地址输入框
<style> div.IPDiv{background:#ffffff;width:120;font-size:9pt;text-align:center;border:2 ridge ...
- JavaScript实现IP地址的输入框方式
最近遇到一些这样的需求:实现一种IP地址的输入方式,就是输入3个字符或自动跳到下一个输入框内,删除的时候,一个输入框没有了字符,自动跳回上一个输入框.看到这里,相信大家都有一些想法了,没错,这种方法就 ...
- Qt编写自定义控件68-IP地址输入框
一.前言 这个IP地址输入框控件,估计写烂了,网上随便一搜索,保证一大堆,估计也是因为这个控件太容易了,非常适合新手练手,一般的思路都是用4个qlineedit控件拼起来,然后每个输入框设置正则表达式 ...
- WPF IP地址输入控件的实现
一.前言 WPF没有内置IP地址输入控件,因此我们需要通过自己定义实现. 我们先看一下IP地址输入控件有什么特性: 输满三个数字焦点会往右移 键盘←→可以空光标移动 任意位置可复制整段IP地址,且支持 ...
- (C#)IP地址与数字地址相互转换
站长网IP查询地址:http://tool.chinaz.com/ip/ 和ip地址转换为数字的工具地址:http://www.msxindl.com/tools/ip/ip_num.asp 可以看到 ...
- php实现IP地址和数字相互转换
echo $a=ip2long ("202.97.224.68");//地址转换成数字 系统自带的函数 注:这里面有一个要注意的地方,大概由于PHP无法正确判断转换完的数字类型,出 ...
- IP地址与数字地址相互转换
/// <summary> /// IP地址转换成数字 /// </summary> /// <param name="addr">IP地址&l ...
- 【Web探索之旅】第三部分第二课:IP地址和域名
内容简介 1.第三部分第二课:IP地址和域名 2.第三部分第三课预告:协议 第三部分第二课:IP地址和域名 上一课我们说了在Web之中,全球各地有无数台机器,有些充当客户机,有些作为服务器. 那么这些 ...
随机推荐
- Linux守护进程的编程实现(转)
http://blog.csdn.net/zg_hover/article/details/2553321 http://blog.csdn.net/kongdefei5000/article/det ...
- jQuery UI resizble、draggable的div包含iframe导致缩放和拖拽的不平滑解决方法
前言 不仅仅是jQuery UI resizble的div包含iframe会导致缩放的不平滑,draggable也会出现包含iframe会导致拖放的不平滑,但是因为jQuery UI有为draggab ...
- MySQL数据库的基本数据类型
整数类型 数值型数据类型主要用来存储数字,包含的类型有: TINYINT.SMALLINT.MEDIUMINT. INT(INTEGER). BIGINT. 下面通过一个例子来查看各种类型的所占有的数 ...
- UDF2
问题 根据给定的gps点point(x,y)和北京的shape数据,关联出 AOI ID IO 输入 gps点表 create table gps ( x double, //经度 y double ...
- Android项目实战(十六):QQ空间实现(一)—— 展示说说中的评论内容并有相应点击事件
大家都玩QQ空间客户端,对于每一个说说,我们都可以评论,那么,对于某一条评论: 白雪公主 回复 小矮人 : 你们好啊~ 我们来分析一下: .QQ空间允许我们 点击 回复人和被回复人的名字就可以进入对于 ...
- Android项目实战(十四):TextView显示html样式的文字
项目需求: TextView显示一段文字,格式为:(消息个数,不确定)条消息 这段文字中名字和数字的长度是不确定的,还要求名字和数字各自有各自的颜色. 一开始我想的是用(转) SpannableStr ...
- Swift开发第六篇——操作运算符也可以重载& func 的参数修饰
本篇分为两部分: 1.Swift 中重载操作运算符的使用 2.Swfit 中 func 的参数修饰 1.Swift 中重载操作运算符的使用 与别的语言不同,Swift 支持运算符的重载,运算符指的是“ ...
- iOS开发中的一些细节BUG的解决
这篇博客里我将不定期更新自己遇到的一些细节上的BUG,并提供解决方法,遇到同样问题的童鞋们可以参考交流一下. 1.关于tableView的tableHeaderView 请注意,我这里说的是table ...
- iOS开发笔记9:NSUserDefaults存储自定义实体对象
NSUserDefaults常常用来本地存储一些简单的数据,例如用户信息等等,NSUserDefaults支持floats.doubles.integers.Booleans.URLs.NSData. ...
- JQuery怎么实现页面刷新后保留鼠标点击样式的方法
今天抽空研究了下鼠标点击添加样式的方法.为了防止忘记,写篇文章算是备份吧. $('ul.main-menu li a').each(function(){ if($($(this))[0].h ...