IpTextBox.cs 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430
  1. using System;
  2. using System.Text.RegularExpressions;
  3. using System.Windows;
  4. using System.Windows.Controls;
  5. using System.Windows.Input;
  6. namespace CustomUI
  7. {
  8. public enum EnumIpBoxType
  9. {
  10. /// <summary>
  11. /// IP地址或者网关
  12. /// </summary>
  13. IpAddress,
  14. /// <summary>
  15. /// 子网掩码
  16. /// </summary>
  17. SubnetMask,
  18. }
  19. public class IpTextBox : Control
  20. {
  21. /// <summary>
  22. /// IP正则
  23. /// </summary>
  24. private const string ipRegex = @"^(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])$";
  25. private const string ZeroTo255Tip = "{0} 不是有效项。请指定一个介于 0 和 255 间的值";
  26. #region private fields
  27. private TextBox PART_BOX1;
  28. private TextBox PART_BOX2;
  29. private TextBox PART_BOX3;
  30. private TextBox PART_BOX4;
  31. #endregion
  32. #region DependencyProperty
  33. #region Type
  34. /// <summary>
  35. /// 获取或者设置文本框类型
  36. /// </summary>
  37. public EnumIpBoxType Type
  38. {
  39. get { return (EnumIpBoxType)GetValue(TypeProperty); }
  40. set { SetValue(TypeProperty, value); }
  41. }
  42. public static readonly DependencyProperty TypeProperty =
  43. DependencyProperty.Register("Type", typeof(EnumIpBoxType), typeof(IpTextBox), new PropertyMetadata(EnumIpBoxType.IpAddress));
  44. #endregion
  45. #region IsHasError
  46. /// <summary>
  47. /// 获取或者设置是否输入了不合法的数字
  48. /// </summary>
  49. public bool IsHasError
  50. {
  51. get { return (bool)GetValue(IsHasErrorProperty); }
  52. private set { SetValue(IsHasErrorProperty, value); }
  53. }
  54. public static readonly DependencyProperty IsHasErrorProperty =
  55. DependencyProperty.Register("IsHasError", typeof(bool), typeof(IpTextBox), new PropertyMetadata(false));
  56. #endregion
  57. #region ErrorContent
  58. /// <summary>
  59. /// 获取或者设置非法输入时的提示内容
  60. /// </summary>
  61. public string ErrorContent
  62. {
  63. get { return (string)GetValue(ErrorContentProperty); }
  64. private set { SetValue(ErrorContentProperty, value); }
  65. }
  66. public static readonly DependencyProperty ErrorContentProperty =
  67. DependencyProperty.Register("ErrorContent", typeof(string), typeof(IpTextBox), new PropertyMetadata(string.Empty));
  68. #endregion
  69. #region IsKeyboardFocused
  70. public new bool IsKeyboardFocused
  71. {
  72. get { return (bool)GetValue(IsKeyboardFocusedProperty); }
  73. private set { SetValue(IsKeyboardFocusedProperty, value); }
  74. }
  75. public new static readonly DependencyProperty IsKeyboardFocusedProperty =
  76. DependencyProperty.Register("IsKeyboardFocused", typeof(bool), typeof(IpTextBox), new PropertyMetadata(false));
  77. #endregion
  78. #region Text
  79. public string Text
  80. {
  81. get { return (string)GetValue(TextProperty); }
  82. set { SetValue(TextProperty, value); }
  83. }
  84. public static readonly DependencyProperty TextProperty =
  85. DependencyProperty.Register("Text", typeof(string), typeof(IpTextBox), new PropertyMetadata(string.Empty));
  86. #endregion
  87. #endregion
  88. #region Constructors
  89. static IpTextBox()
  90. {
  91. DefaultStyleKeyProperty.OverrideMetadata(typeof(IpTextBox), new FrameworkPropertyMetadata(typeof(IpTextBox)));
  92. }
  93. #endregion
  94. #region Override
  95. public override void OnApplyTemplate()
  96. {
  97. base.OnApplyTemplate();
  98. this.PART_BOX1 = this.GetTemplateChild("PART_BOX1") as TextBox;
  99. this.PART_BOX2 = this.GetTemplateChild("PART_BOX2") as TextBox;
  100. this.PART_BOX3 = this.GetTemplateChild("PART_BOX3") as TextBox;
  101. this.PART_BOX4 = this.GetTemplateChild("PART_BOX4") as TextBox;
  102. if(this.PART_BOX1 != null)
  103. {
  104. this.PART_BOX1.PreviewTextInput += PART_BOX1_PreviewTextInput;
  105. this.PART_BOX1.TextChanged += PART_BOX1_TextChanged;
  106. this.PART_BOX1.GotFocus += (o, e) => { this.IsKeyboardFocused = true; };
  107. this.PART_BOX1.LostFocus += (o, e) => { this.IsKeyboardFocused = false; };
  108. this.PART_BOX1.PreviewKeyDown += PART_BOX1_PreviewKeyDown;
  109. }
  110. if(this.PART_BOX2 != null)
  111. {
  112. this.PART_BOX2.PreviewTextInput += PART_BOX2_PreviewTextInput;
  113. this.PART_BOX2.TextChanged += PART_BOX2_TextChanged;
  114. this.PART_BOX2.GotFocus += (o, e) => { this.IsKeyboardFocused = true; };
  115. this.PART_BOX2.LostFocus += (o, e) => { this.IsKeyboardFocused = false; };
  116. this.PART_BOX2.PreviewKeyDown += PART_BOX1_PreviewKeyDown;
  117. }
  118. if(this.PART_BOX3 != null)
  119. {
  120. this.PART_BOX3.PreviewTextInput += PART_BOX3_PreviewTextInput;
  121. this.PART_BOX3.TextChanged += PART_BOX3_TextChanged;
  122. this.PART_BOX3.GotFocus += (o, e) => { this.IsKeyboardFocused = true; };
  123. this.PART_BOX3.LostFocus += (o, e) => { this.IsKeyboardFocused = false; };
  124. this.PART_BOX3.PreviewKeyDown += PART_BOX1_PreviewKeyDown;
  125. }
  126. if(this.PART_BOX4 != null)
  127. {
  128. this.PART_BOX4.PreviewTextInput += PART_BOX4_PreviewTextInput;
  129. this.PART_BOX4.GotFocus += (o, e) => { this.IsKeyboardFocused = true; };
  130. this.PART_BOX4.LostFocus += (o, e) => { this.IsKeyboardFocused = false; };
  131. this.PART_BOX4.PreviewKeyDown += PART_BOX1_PreviewKeyDown;
  132. }
  133. }
  134. void PART_BOX1_PreviewKeyDown(object sender, KeyEventArgs e)
  135. {
  136. if (e.KeyboardDevice.Modifiers.HasFlag(ModifierKeys.Control) && e.Key == Key.V)
  137. {
  138. IDataObject data = Clipboard.GetDataObject();
  139. if(data.GetDataPresent(DataFormats.Text))
  140. {
  141. string text = (string)data.GetData(DataFormats.UnicodeText);
  142. Regex regex = new Regex(ipRegex);
  143. if(regex.IsMatch(text))
  144. {
  145. string[] strs = text.Split(new char[] { '.' });
  146. //因为已经判断过是正确的IP地址,因此不用判断索引是否越界
  147. this.PART_BOX1.Text = strs[0];
  148. this.PART_BOX2.Text = strs[1];
  149. this.PART_BOX3.Text = strs[2];
  150. this.PART_BOX4.Text = strs[3];
  151. this.PART_BOX1.Focus();
  152. this.PART_BOX1.SelectionStart = 0;
  153. }
  154. else
  155. {
  156. this.IsHasError = true;
  157. this.ErrorContent = "您正在尝试将格式错误的 IP 地址粘贴到该字段";
  158. this.PART_BOX1.Text = string.Empty;
  159. this.PART_BOX2.Text = string.Empty;
  160. this.PART_BOX3.Text = string.Empty;
  161. this.PART_BOX4.Text = string.Empty;
  162. }
  163. }
  164. e.Handled = true;
  165. }
  166. }
  167. #endregion
  168. #region private function
  169. /// <summary>
  170. /// 检查输入的字符是否为数字
  171. /// </summary>
  172. /// <param name="input"></param>
  173. /// <returns></returns>
  174. private bool IsNumber(string input)
  175. {
  176. Regex regex = new Regex("^[0-9]*$");
  177. return regex.IsMatch(input);
  178. }
  179. /// <summary>
  180. /// 检查输入的数字是否在某个范围内
  181. /// </summary>
  182. /// <param name="number"></param>
  183. /// <param name="start"></param>
  184. /// <param name="end"></param>
  185. /// <returns></returns>
  186. private bool IsNumberRange(int number, int start, int end)
  187. {
  188. return number >= start && number <= end;
  189. }
  190. /// <summary>
  191. /// 检查输入的文本是否符合IP地址格式
  192. /// </summary>
  193. /// <param name="text1"></param>
  194. /// <param name="text2"></param>
  195. /// <returns></returns>
  196. private bool CheckNumberIsLegal(string text1, string text2)
  197. {
  198. if (!this.IsNumber(text2)) return true;
  199. int text = Convert.ToInt32(text1);
  200. return !this.IsNumberRange(text, 0, 255);
  201. }
  202. #endregion
  203. #region Event Implement Function
  204. void PART_BOX1_PreviewTextInput(object sender, System.Windows.Input.TextCompositionEventArgs e)
  205. {
  206. string input = this.PART_BOX1.Text + e.Text;
  207. if (!string.IsNullOrWhiteSpace(input) && ".".Equals(e.Text))
  208. {
  209. e.Handled = true;
  210. this.PART_BOX2.Focus();
  211. }
  212. else
  213. {
  214. //输入的不是数字,则直接返回
  215. if (!this.IsNumber(e.Text))
  216. {
  217. e.Handled = true;
  218. return;
  219. }
  220. if(!string.IsNullOrWhiteSpace(this.PART_BOX1.SelectedText))
  221. {
  222. input = this.PART_BOX1.Text.Remove(this.PART_BOX1.SelectionStart, this.PART_BOX1.SelectionLength) + e.Text;
  223. }
  224. int text = Convert.ToInt32(input);
  225. switch (this.Type)
  226. {
  227. case EnumIpBoxType.IpAddress:
  228. if (!this.IsNumberRange(text, 0, 223))
  229. {
  230. e.Handled = true;
  231. this.IsHasError = true;
  232. this.ErrorContent = string.Format("{0} 不是有效项。请指定一个介于 1 和 223 间的值", input);
  233. this.PART_BOX1.Text = "223";
  234. }
  235. else
  236. {
  237. IsHasError = false;
  238. }
  239. break;
  240. case EnumIpBoxType.SubnetMask:
  241. if (!this.IsNumberRange(text, 0, 255))
  242. {
  243. e.Handled = true;
  244. this.IsHasError = true;
  245. this.ErrorContent = string.Format(ZeroTo255Tip, input);
  246. this.PART_BOX1.Text = "255";
  247. }
  248. else
  249. {
  250. IsHasError = false;
  251. }
  252. break;
  253. }
  254. }
  255. }
  256. void PART_BOX2_PreviewTextInput(object sender, System.Windows.Input.TextCompositionEventArgs e)
  257. {
  258. string input = this.PART_BOX2.Text + e.Text;
  259. if (!string.IsNullOrWhiteSpace(this.PART_BOX2.SelectedText))
  260. {
  261. input = this.PART_BOX2.Text.Remove(this.PART_BOX2.SelectionStart, this.PART_BOX2.SelectionLength) + e.Text;
  262. }
  263. //如果输入了.且该文本框不为空,则自动跳到下一个输入框中
  264. if (!string.IsNullOrWhiteSpace(input) && ".".Equals(e.Text))
  265. {
  266. e.Handled = true;
  267. this.PART_BOX3.Focus();
  268. }
  269. else
  270. {
  271. e.Handled = this.CheckNumberIsLegal(input, e.Text);
  272. //如果输入不合法,则默认为255
  273. if (e.Handled)
  274. {
  275. this.PART_BOX2.Text = "255";
  276. this.PART_BOX2.SelectionStart = this.PART_BOX2.Text.Length + 1;
  277. this.IsHasError = true;
  278. this.ErrorContent = string.Format(ZeroTo255Tip, input);
  279. }
  280. else
  281. {
  282. this.IsHasError = false;
  283. }
  284. }
  285. }
  286. void PART_BOX3_PreviewTextInput(object sender, System.Windows.Input.TextCompositionEventArgs e)
  287. {
  288. string input = this.PART_BOX3.Text + e.Text;
  289. if (!string.IsNullOrWhiteSpace(this.PART_BOX3.SelectedText))
  290. {
  291. input = this.PART_BOX3.Text.Remove(this.PART_BOX3.SelectionStart, this.PART_BOX3.SelectionLength) + e.Text;
  292. }
  293. if (!string.IsNullOrWhiteSpace(input) && ".".Equals(e.Text))
  294. {
  295. e.Handled = true;
  296. this.PART_BOX4.Focus();
  297. }
  298. else
  299. {
  300. e.Handled = this.CheckNumberIsLegal(input, e.Text);
  301. if (e.Handled)
  302. {
  303. this.PART_BOX3.Text = "255";
  304. this.PART_BOX3.SelectionStart = this.PART_BOX3.Text.Length + 1;
  305. this.IsHasError = true;
  306. this.ErrorContent = string.Format(ZeroTo255Tip, input);
  307. }
  308. else
  309. {
  310. this.IsHasError = false;
  311. }
  312. }
  313. }
  314. void PART_BOX4_PreviewTextInput(object sender, System.Windows.Input.TextCompositionEventArgs e)
  315. {
  316. string input = this.PART_BOX4.Text + e.Text;
  317. if (!string.IsNullOrWhiteSpace(this.PART_BOX4.SelectedText))
  318. {
  319. input = this.PART_BOX4.Text.Remove(this.PART_BOX4.SelectionStart, this.PART_BOX4.SelectionLength) + e.Text;
  320. }
  321. e.Handled = this.CheckNumberIsLegal(input, e.Text);
  322. if (e.Handled)
  323. {
  324. this.PART_BOX4.Text = "255";
  325. this.PART_BOX4.SelectionStart = this.PART_BOX4.Text.Length + 1;
  326. this.IsHasError = true;
  327. this.ErrorContent = string.Format(ZeroTo255Tip, input);
  328. }
  329. else
  330. {
  331. this.IsHasError = false;
  332. }
  333. }
  334. void PART_BOX1_TextChanged(object sender, TextChangedEventArgs e)
  335. {
  336. if (this.PART_BOX1.Text.Length == 3)
  337. {
  338. int number = 1;
  339. if (Int32.TryParse(this.PART_BOX1.Text, out number))
  340. {
  341. switch (this.Type)
  342. {
  343. case EnumIpBoxType.IpAddress:
  344. if (number < 1)
  345. {
  346. this.PART_BOX1.Text = "1";
  347. }
  348. break;
  349. case EnumIpBoxType.SubnetMask:
  350. break;
  351. }
  352. }
  353. this.PART_BOX2.Focus();
  354. }
  355. }
  356. void PART_BOX2_TextChanged(object sender, TextChangedEventArgs e)
  357. {
  358. if (this.PART_BOX2.Text.Length == 3)
  359. {
  360. this.PART_BOX3.Focus();
  361. }
  362. }
  363. void PART_BOX3_TextChanged(object sender, TextChangedEventArgs e)
  364. {
  365. if (this.PART_BOX3.Text.Length == 3)
  366. {
  367. this.PART_BOX4.Focus();
  368. }
  369. }
  370. #endregion
  371. }
  372. }