WPF IP地址输入控件的实现
原文:WPF IP地址输入控件的实现

一、前言

WPF没有内置IP地址输入控件,因此我们需要通过自己定义实现。

我们先看一下IP地址输入控件有什么特性:

  • 输满三个数字焦点会往右移
  • 键盘←→可以空光标移动
  • 任意位置可复制整段IP地址,且支持x.x.x.x格式的粘贴赋值
  • 删除字符会自动向左移动焦点

知道以上特性,我们就可以开始动手了。

二、构成

Grid+TextBox*4+TextBlock*3

通过这几个控件的组合,我们完成IP地址输入控件的功能。

界面代码如下:

  1 <UserControl
  2     x:Class="IpAddressControl.IpAddressControl"
  3     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  4     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  5     xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
  6     xmlns:local="clr-namespace:IpAddressControl"
  7     xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
  8     Margin="10,0"
  9     d:DesignHeight="50"
 10     d:DesignWidth="800"
 11     mc:Ignorable="d" Background="White">
 12     <UserControl.Resources>
 13         <ControlTemplate x:Key="validationTemplate">
 14             <DockPanel>
 15                 <TextBlock
 16                     Margin="1,2"
 17                     DockPanel.Dock="Right"
 18                     FontSize="{DynamicResource ResourceKey=Heading4}"
 19                     FontWeight="Bold"
 20                     Foreground="Red"
 21                     Text="" />
 22                 <AdornedElementPlaceholder />
 23             </DockPanel>
 24         </ControlTemplate>
 25         <Style x:Key="CustomTextBoxTextStyle" TargetType="TextBox">
 26             <Setter Property="MaxLength" Value="3" />
 27             <Setter Property="HorizontalAlignment" Value="Stretch" />
 28             <Setter Property="VerticalAlignment" Value="Center" />
 29             <Style.Triggers>
 30                 <Trigger Property="Validation.HasError" Value="True">
 31                     <Trigger.Setters>
 32                         <Setter Property="ToolTip" Value="{Binding RelativeSource={RelativeSource Self}, Path=(Validation.Errors)[0].ErrorContent}" />
 33                         <Setter Property="BorderBrush" Value="Red" />
 34                         <Setter Property="Background" Value="Red" />
 35                     </Trigger.Setters>
 36                 </Trigger>
 37             </Style.Triggers>
 38         </Style>
 39     </UserControl.Resources>
 40     <Grid>
 41         <Grid.ColumnDefinitions>
 42             <ColumnDefinition MinWidth="30" />
 43             <ColumnDefinition Width="10" />
 44             <ColumnDefinition MinWidth="30" />
 45             <ColumnDefinition Width="10" />
 46             <ColumnDefinition MinWidth="30" />
 47             <ColumnDefinition Width="10" />
 48             <ColumnDefinition MinWidth="30" />
 49         </Grid.ColumnDefinitions>
 50
 51         <!--  Part 1  -->
 52         <TextBox
 53             Grid.Column="0"
 54             BorderThickness="0"
 55             HorizontalAlignment="Stretch"
 56             VerticalAlignment="Stretch"
 57             VerticalContentAlignment="Center"
 58             HorizontalContentAlignment="Center"
 59             x:Name="part1"
 60             PreviewKeyDown="Part1_PreviewKeyDown"
 61             local:FocusChangeExtension.IsFocused="{Binding IsPart1Focused, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, NotifyOnSourceUpdated=True}"
 62             Style="{StaticResource CustomTextBoxTextStyle}"
 63             Validation.ErrorTemplate="{StaticResource validationTemplate}">
 64             <TextBox.Text>
 65                 <Binding Path="Part1" UpdateSourceTrigger="PropertyChanged">
 66                     <Binding.ValidationRules>
 67                         <local:IPRangeValidationRule Max="255" Min="0" />
 68                     </Binding.ValidationRules>
 69                 </Binding>
 70             </TextBox.Text>
 71         </TextBox>
 72         <TextBlock
 73             Grid.Column="1"
 74             HorizontalAlignment="Center"
 75             FontSize="15"
 76             Text="."
 77             VerticalAlignment="Center"
 78       />
 79
 80         <!--  Part 2  -->
 81         <TextBox
 82             Grid.Column="2"
 83             x:Name="part2"
 84             BorderThickness="0"
 85             VerticalAlignment="Stretch"
 86             VerticalContentAlignment="Center"
 87             HorizontalContentAlignment="Center"
 88             PreviewKeyDown="Part2_KeyDown"
 89             local:FocusChangeExtension.IsFocused="{Binding IsPart2Focused}"
 90             Style="{StaticResource CustomTextBoxTextStyle}"
 91             Validation.ErrorTemplate="{StaticResource validationTemplate}">
 92             <TextBox.Text>
 93                 <Binding Path="Part2" UpdateSourceTrigger="PropertyChanged">
 94                     <Binding.ValidationRules>
 95                         <local:IPRangeValidationRule Max="255" Min="0" />
 96                     </Binding.ValidationRules>
 97                 </Binding>
 98             </TextBox.Text>
 99         </TextBox>
100         <TextBlock
101             Grid.Column="3"
102             HorizontalAlignment="Center"
103             FontSize="15"
104             Text="."
105             VerticalAlignment="Center"/>
106
107         <!--  Part 3  -->
108         <TextBox
109             Grid.Column="4"
110             x:Name="part3"
111             BorderThickness="0"
112             VerticalAlignment="Stretch"
113             VerticalContentAlignment="Center"
114             HorizontalContentAlignment="Center"
115             PreviewKeyDown="Part3_KeyDown"
116             local:FocusChangeExtension.IsFocused="{Binding IsPart3Focused}"
117             Style="{StaticResource CustomTextBoxTextStyle}"
118             Validation.ErrorTemplate="{StaticResource validationTemplate}">
119             <TextBox.Text>
120                 <Binding Path="Part3" UpdateSourceTrigger="PropertyChanged">
121                     <Binding.ValidationRules>
122                         <local:IPRangeValidationRule Max="255" Min="0" />
123                     </Binding.ValidationRules>
124                 </Binding>
125             </TextBox.Text>
126         </TextBox>
127         <TextBlock
128             Grid.Column="5"
129             HorizontalAlignment="Center"
130             FontSize="15"
131             Text="."
132             VerticalAlignment="Center"/>
133
134         <!--  Part 4  -->
135         <TextBox
136             Grid.Column="6"
137             x:Name="part4"
138             BorderThickness="0"
139             VerticalAlignment="Stretch"
140             VerticalContentAlignment="Center"
141             HorizontalContentAlignment="Center"
142             PreviewKeyDown="Part4_KeyDown"
143             local:FocusChangeExtension.IsFocused="{Binding IsPart4Focused}"
144             Style="{StaticResource CustomTextBoxTextStyle}"
145             Validation.ErrorTemplate="{StaticResource validationTemplate}">
146             <TextBox.Text>
147                 <Binding Path="Part4" UpdateSourceTrigger="PropertyChanged">
148                     <Binding.ValidationRules>
149                         <local:IPRangeValidationRule Max="255" Min="0" />
150                     </Binding.ValidationRules>
151                 </Binding>
152             </TextBox.Text>
153         </TextBox>
154     </Grid>
155 </UserControl>

查看代码

三、验证输入格式

界面中为TextBox添加了CustomTextBoxTextStyle及validationTemplate样式,当输入格式不正确时,控件就会应用该样式。

通过自定义规则IPRangeValidationRule来验证输入的内容格式是否要求。

自定义规则代码如下:

 1 public class IPRangeValidationRule : ValidationRule
 2 {
 3     private int _min;
 4     private int _max;
 5
 6     public int Min
 7     {
 8         get { return _min; }
 9         set { _min = value; }
10     }
11
12     public int Max
13     {
14         get { return _max; }
15         set { _max = value; }
16     }
17
18     public override ValidationResult Validate(object value, CultureInfo cultureInfo)
19     {
20         int val = 0;
21         var strVal = (string)value;
22         try
23         {
24             if (strVal.Length > 0)
25             {
26                 if (strVal.EndsWith("."))
27                 {
28                     return CheckRanges(strVal.Replace(".", ""));
29                 }
30
31                 // Allow dot character to move to next box
32                 return CheckRanges(strVal);
33             }
34         }
35         catch (Exception e)
36         {
37             return new ValidationResult(false, "Illegal characters or " + e.Message);
38         }
39
40         if ((val < Min) || (val > Max))
41         {
42             return new ValidationResult(false,
43               "Please enter the value in the range: " + Min + " - " + Max + ".");
44         }
45         else
46         {
47             return ValidationResult.ValidResult;
48         }
49     }
50
51     private ValidationResult CheckRanges(string strVal)
52     {
53         if (int.TryParse(strVal, out var res))
54         {
55             if ((res < Min) || (res > Max))
56             {
57                 return new ValidationResult(false,
58                   "Please enter the value in the range: " + Min + " - " + Max + ".");
59             }
60             else
61             {
62                 return ValidationResult.ValidResult;
63             }
64         }
65         else
66         {
67             return new ValidationResult(false, "Illegal characters entered");
68         }
69     }
70 }

查看代码

四、控制焦点变化

在界面代码中我通过local:FocusChangeExtension.IsFocused附加属性实现绑定属性控制焦点的变化。

附加属性的代码如下:

 1 public static class FocusChangeExtension
 2 {
 3     public static bool GetIsFocused(DependencyObject obj)
 4     {
 5         return (bool)obj.GetValue(IsFocusedProperty);
 6     }
 7
 8     public static void SetIsFocused(DependencyObject obj, bool value)
 9     {
10         obj.SetValue(IsFocusedProperty, value);
11     }
12
13     public static readonly DependencyProperty IsFocusedProperty =
14         DependencyProperty.RegisterAttached(
15             "IsFocused", typeof(bool), typeof(FocusChangeExtension),
16             new UIPropertyMetadata(false, OnIsFocusedPropertyChanged));
17
18     private static void OnIsFocusedPropertyChanged(
19         DependencyObject d,
20         DependencyPropertyChangedEventArgs e)
21     {
22         var control = (UIElement)d;
23         if ((bool)e.NewValue)
24         {
25             control.Focus();
26         }
27     }
28 }

查看代码

五、VM+后台代码混合实现焦点控制及内容复制粘贴

1、后台代码主要实现复制粘贴内容,另外←→移动光标也需要后台代码控制。通过PreviewKeyDown事件捕获键盘左移右移,复制,删除等事件,做出相应处理:

 1 private void Part2_KeyDown(object sender, System.Windows.Input.KeyEventArgs e)
 2 {
 3     if (e.Key == Key.Back && part2.Text == "")
 4     {
 5         part1.Focus();
 6     }
 7     if (e.Key == Key.Right && part2.CaretIndex == part2.Text.Length)
 8     {
 9         part3.Focus();
10         e.Handled = true;
11     }
12     if (e.Key == Key.Left && part2.CaretIndex == 0)
13     {
14         part1.Focus();
15         e.Handled = true;
16     }
17
18     if (e.KeyboardDevice.Modifiers.HasFlag(ModifierKeys.Control) && e.Key == Key.C)
19     {
20         if (part2.SelectionLength == 0)
21         {
22             var vm = this.DataContext as IpAddressViewModel;
23             Clipboard.SetText(vm.AddressText);
24         }
25     }
26 }

部分代码

通过DataObject.AddPastingHandler(part1, TextBox_Pasting)添加粘贴事件。使控件赋值。

2、通过ViewModel方式实现属性绑定通知,来控制焦点变化及内容赋值。

ViewModel类要实现绑定通知需要实现INotifyPropertyChanged接口中的方法。

我们新建一个IpAddressViewModel类继承INotifyPropertyChanged,代码如下:

  1 public class IpAddressViewModel : INotifyPropertyChanged
  2 {
  3     public event EventHandler AddressChanged;
  4
  5     public string AddressText
  6     {
  7         get { return $"{Part1??"0"}.{Part2??"0"}.{Part3??"0"}.{Part4??"0"}"; }
  8     }
  9
 10     private bool isPart1Focused;
 11
 12     public bool IsPart1Focused
 13     {
 14         get { return isPart1Focused; }
 15         set { isPart1Focused = value; OnPropertyChanged(); }
 16     }
 17
 18     private string part1;
 19
 20     public string Part1
 21     {
 22         get { return part1; }
 23         set
 24         {
 25             part1 = value;
 26             SetFocus(true, false, false, false);
 27
 28             var moveNext = CanMoveNext(ref part1);
 29
 30             OnPropertyChanged();
 31             OnPropertyChanged(nameof(AddressText));
 32             AddressChanged?.Invoke(this, EventArgs.Empty);
 33
 34             if (moveNext)
 35             {
 36                 SetFocus(false, true, false, false);
 37             }
 38         }
 39     }
 40
 41     private bool isPart2Focused;
 42
 43     public bool IsPart2Focused
 44     {
 45         get { return isPart2Focused; }
 46         set { isPart2Focused = value; OnPropertyChanged(); }
 47     }
 48
 49
 50     private string part2;
 51
 52     public string Part2
 53     {
 54         get { return part2; }
 55         set
 56         {
 57             part2 = value;
 58             SetFocus(false, true, false, false);
 59
 60             var moveNext = CanMoveNext(ref part2);
 61
 62             OnPropertyChanged();
 63             OnPropertyChanged(nameof(AddressText));
 64             AddressChanged?.Invoke(this, EventArgs.Empty);
 65
 66             if (moveNext)
 67             {
 68                 SetFocus(false, false, true, false);
 69             }
 70         }
 71     }
 72
 73     private bool isPart3Focused;
 74
 75     public bool IsPart3Focused
 76     {
 77         get { return isPart3Focused; }
 78         set { isPart3Focused = value; OnPropertyChanged(); }
 79     }
 80
 81     private string part3;
 82
 83     public string Part3
 84     {
 85         get { return part3; }
 86         set
 87         {
 88             part3 = value;
 89             SetFocus(false, false, true, false);
 90             var moveNext = CanMoveNext(ref part3);
 91
 92             OnPropertyChanged();
 93             OnPropertyChanged(nameof(AddressText));
 94             AddressChanged?.Invoke(this, EventArgs.Empty);
 95
 96             if (moveNext)
 97             {
 98                 SetFocus(false, false, false, true);
 99             }
100         }
101     }
102
103     private bool isPart4Focused;
104
105     public bool IsPart4Focused
106     {
107         get { return isPart4Focused; }
108         set { isPart4Focused = value; OnPropertyChanged(); }
109     }
110
111     private string part4;
112
113     public string Part4
114     {
115         get { return part4; }
116         set
117         {
118             part4 = value;
119             SetFocus(false, false, false, true);
120             var moveNext = CanMoveNext(ref part4);
121
122             OnPropertyChanged();
123             OnPropertyChanged(nameof(AddressText));
124             AddressChanged?.Invoke(this, EventArgs.Empty);
125
126         }
127     }
128
129     public void SetAddress(string address)
130     {
131         if (string.IsNullOrWhiteSpace(address))
132             return;
133
134         var parts = address.Split('.');
135
136         if (int.TryParse(parts[0], out var num0))
137         {
138             Part1 = num0.ToString();
139         }
140
141         if (int.TryParse(parts[1], out var num1))
142         {
143             Part2 = parts[1];
144         }
145
146         if (int.TryParse(parts[2], out var num2))
147         {
148             Part3 = parts[2];
149         }
150
151         if (int.TryParse(parts[3], out var num3))
152         {
153             Part4 = parts[3];
154         }
155
156     }
157
158     private bool CanMoveNext(ref string part)
159     {
160         bool moveNext = false;
161
162         if (!string.IsNullOrWhiteSpace(part))
163         {
164             if (part.Length >= 3)
165             {
166                 moveNext = true;
167             }
168
169             if (part.EndsWith("."))
170             {
171                 moveNext = true;
172                 part = part.Replace(".", "");
173             }
174         }
175
176         return moveNext;
177     }
178
179     private void SetFocus(bool part1, bool part2, bool part3, bool part4)
180     {
181         IsPart1Focused = part1;
182         IsPart2Focused = part2;
183         IsPart3Focused = part3;
184         IsPart4Focused = part4;
185     }
186
187     public event PropertyChangedEventHandler PropertyChanged;
188
189
190     protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
191     {
192         PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
193     }
194 }

查看代码

到这里基本就完成了,生成控件然后到MainWindow中引用该控件

六、最终效果

————————————————————

代码地址:https://github.com/cmfGit/IpAddressControl.git

posted on 2019-06-03 08:51 NET未来之路 阅读(...) 评论(...) 编辑 收藏

转载于:https://www.cnblogs.com/lonelyxmas/p/10965664.html

WPF IP地址输入控件的实现相关推荐

  1. qt显示rgba8888 如何改 frame_Qt开源作品17-IP地址输入控件

    一.前言 这个IP地址输入框控件,估计写烂了,网上随便一搜索,保证一大堆,估计也是因为这个控件太容易了,非常适合新手练手,一般的思路都是用4个qlineedit控件拼起来,然后每个输入框设置正则表达式 ...

  2. 正则表达式——WPF输入控件TextBox 限定输入特定字符

    正则表达式--WPF输入控件TextBox 限定输入特定字符 原文:正则表达式--WPF输入控件TextBox 限定输入特定字符 概念: 正则表达式是对字符串操作的一种逻辑公式, 就是用事先定义好的一 ...

  3. [html] 怎样去除iOS和Android中的输入URL地址的控件条呢?

    [html] 怎样去除iOS和Android中的输入URL地址的控件条呢? setTimeout(scrollTo,0,0,0); 个人简介 我是歌谣,欢迎和大家一起交流前后端知识.放弃很容易, 但坚 ...

  4. PyQt5 基本语法(四):输入控件

    文章目录 2. 输入控件(一) 2.1 纯键盘 2.1.1 QLineEdit 2.1.1.1 描述 2.1.1.2 控件创建 2.1.1.3 输出模式 2.1.1.4 提示字符串 2.1.1.5 清 ...

  5. WPF编程,将控件所呈现的内容保存成图像的一种方法。

    WPF编程,将控件所呈现的内容保存成图像的一种方法. 原文:WPF编程,将控件所呈现的内容保存成图像的一种方法. 版权声明:我不生产代码,我只是代码的搬运工. https://blog.csdn.ne ...

  6. 创建输入控件(input控件、文本框、密码框、单项选择、多项选择、重置与提交按钮的设置)

    创建输入控件 input控件的相关概念 input控件的属性 input控件的类型 文本框的设置 密码框的设置 单项选择的设置 多项选择的设置 重置与提交按钮的设置 综合运用 相关概念选择题及参考答案 ...

  7. Kettle输入控件

    Kettle输入控件 输入:就是用来抽取数据或生成数据. 是ETL操作的Extract 1CSV文件输入 2 : 文本文件输入 提取日志信息的数据是开发常见的操作,日志信息基本都是文本类型. 3Exc ...

  8. AvalonEdit-基于WPF的代码显示控件

    AvalonEdit是基于WPF的代码显示控件,项目地址:https://github.com/icsharpcode/AvalonEdit,支持C#,javascript,C++,XML,HTML, ...

  9. 在WPF中使用WinForm控件方法

    在WPF中使用WinForm控件方法 原文:在WPF中使用WinForm控件方法 1.      首先添加对如下两个dll文件的引用:WindowsFormsIntegration.dll,Syste ...

最新文章

  1. Elasticsearch对垒8大竞品技术,孰优孰劣?
  2. 御泥坊搜索引擎优化网络诊断_掌握以下这些网络营销搜索引擎优化技巧,你的排名想不靠前都难!...
  3. js判断是否是ie浏览器且给出ie版本
  4. 如何跟踪log4j漏洞原理及发现绕WAF的tips
  5. Hibernate执行原理总结
  6. [Python图像处理] 三十一.图像点运算处理两万字详细总结(灰度化处理、阈值化处理)
  7. Zoom的Web客户端与WebRTC有何不同?
  8. Qt打开文件对话框同时选中多个文件或单个文件
  9. python自助电影售票机_Spring Cloud版——电影售票系统六使用 Spring Cloud Config 统一管理微服务配置...
  10. get函数和getline函数
  11. Python 常用官方文档整理(中文版)
  12. [转载]《Linux Kernel Development》读书笔记 - 蔚蓝海岸 - C++博客
  13. vi vim 字符串替换
  14. 关于信号发生器的功能和参数介绍(二)
  15. Monorepo 項目管理方案:lerna + yarn workspace / pnpm
  16. java浮点类型数据运算并保留小数点后几位工具类
  17. ios 区分iphone ipod ipad的方法及获取设备名称。
  18. 《科比传》留下深刻印象的文字
  19. css3 特效 加1加2,Bootstrap轮播加上css3动画,炫酷到底!
  20. Dreamweaver 安装 激活

热门文章

  1. 计算机网络知识点总结(第六版,谢希仁)
  2. 添加事件的兼容性写法
  3. linux 防火墙配置与REJECT导致没有生效问题
  4. wordpress--SEO们的福音
  5. .net工程师至少要懂的东西
  6. Android 4.4 Kitkat 使能有线网络 Ethernet
  7. 第9章:Bootstrap Token方式增加Node
  8. 6.2 gzip:压缩或解压文件
  9. java web 柱状图_使用JFreeChart实现基于Web的柱状图
  10. vue main.js中引入js_web前端的同学不容错过,大厂Vue最佳实践总结,提高竞争力...