WPF如何自定义实现IP地址输入控件

这篇文章将为大家详细讲解有关WPF如何自定义实现IP地址输入控件,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。

创新互联公司服务项目包括陇川网站建设、陇川网站制作、陇川网页制作以及陇川网络营销策划等。多年来,我们专注于互联网行业,利用自身积累的技术优势、行业经验、深度合作伙伴关系等,向广大中小型企业、政府机构等提供互联网行业的解决方案,陇川网站推广取得了明显的社会效益与经济效益。目前,我们服务的客户以成都为中心已经辐射到陇川省份的部分城市,未来相信会继续扩大服务区域并继续获得客户的支持与信任!

一、前言

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

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

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

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

二、构成

Grid+TextBox*4+TextBlock*3

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

界面代码如下:

<UserControl x:Class="IpAddressControl.IpAddressControl" 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:local="clr-namespace:IpAddressControl" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" Margin="10,0" d:DesignHeight="50" d:DesignWidth="800" mc:Ignorable="d" Background="White"> <UserControl.Resources>  <ControlTemplate x:Key="validationTemplate">   <DockPanel>    <TextBlock     Margin="1,2"     DockPanel.Dock="Right"     FontSize="{DynamicResource ResourceKey=Heading4}"     FontWeight="Bold"     Foreground="Red"     Text="" />    <AdornedElementPlaceholder />   </DockPanel>  </ControlTemplate>  <Style x:Key="CustomTextBoxTextStyle" TargetType="TextBox">   <Setter Property="MaxLength" Value="3" />   <Setter Property="HorizontalAlignment" Value="Stretch" />   <Setter Property="VerticalAlignment" Value="Center" />   <Style.Triggers>    <Trigger Property="Validation.HasError" Value="True">     <Trigger.Setters>      <Setter Property="ToolTip" Value="{Binding RelativeSource={RelativeSource Self}, Path=(Validation.Errors)[0].ErrorContent}" />      <Setter Property="BorderBrush" Value="Red" />      <Setter Property="Background" Value="Red" />     </Trigger.Setters>    </Trigger>   </Style.Triggers>  </Style> </UserControl.Resources> <Grid>  <Grid.ColumnDefinitions>   <ColumnDefinition MinWidth="30" />   <ColumnDefinition Width="10" />   <ColumnDefinition MinWidth="30" />   <ColumnDefinition Width="10" />   <ColumnDefinition MinWidth="30" />   <ColumnDefinition Width="10" />   <ColumnDefinition MinWidth="30" />  </Grid.ColumnDefinitions>  <!-- Part 1 -->  <TextBox   Grid.Column="0"   BorderThickness="0"   HorizontalAlignment="Stretch"   VerticalAlignment="Stretch"   VerticalContentAlignment="Center"   HorizontalContentAlignment="Center"   x:Name="part1"   PreviewKeyDown="Part1_PreviewKeyDown"   local:FocusChangeExtension.IsFocused="{Binding IsPart1Focused, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, NotifyOnSourceUpdated=True}"   Style="{StaticResource CustomTextBoxTextStyle}"   Validation.ErrorTemplate="{StaticResource validationTemplate}">   <TextBox.Text>    <Binding Path="Part1" UpdateSourceTrigger="PropertyChanged">     <Binding.ValidationRules>      <local:IPRangeValidationRule Max="255" Min="0" />     </Binding.ValidationRules>    </Binding>   </TextBox.Text>  </TextBox>  <TextBlock   Grid.Column="1"   HorizontalAlignment="Center"   FontSize="15"   Text="."   VerticalAlignment="Center"  />  <!-- Part 2 -->  <TextBox   Grid.Column="2"   x:Name="part2"   BorderThickness="0"   VerticalAlignment="Stretch"   VerticalContentAlignment="Center"   HorizontalContentAlignment="Center"   PreviewKeyDown="Part2_KeyDown"   local:FocusChangeExtension.IsFocused="{Binding IsPart2Focused}"   Style="{StaticResource CustomTextBoxTextStyle}"   Validation.ErrorTemplate="{StaticResource validationTemplate}">   <TextBox.Text>    <Binding Path="Part2" UpdateSourceTrigger="PropertyChanged">     <Binding.ValidationRules>      <local:IPRangeValidationRule Max="255" Min="0" />     </Binding.ValidationRules>    </Binding>   </TextBox.Text>  </TextBox>  <TextBlock   Grid.Column="3"   HorizontalAlignment="Center"   FontSize="15"   Text="."   VerticalAlignment="Center"/>  <!-- Part 3 -->  <TextBox   Grid.Column="4"   x:Name="part3"   BorderThickness="0"   VerticalAlignment="Stretch"   VerticalContentAlignment="Center"   HorizontalContentAlignment="Center"   PreviewKeyDown="Part3_KeyDown"   local:FocusChangeExtension.IsFocused="{Binding IsPart3Focused}"   Style="{StaticResource CustomTextBoxTextStyle}"   Validation.ErrorTemplate="{StaticResource validationTemplate}">   <TextBox.Text>    <Binding Path="Part3" UpdateSourceTrigger="PropertyChanged">     <Binding.ValidationRules>      <local:IPRangeValidationRule Max="255" Min="0" />     </Binding.ValidationRules>    </Binding>   </TextBox.Text>  </TextBox>  <TextBlock   Grid.Column="5"   HorizontalAlignment="Center"   FontSize="15"   Text="."   VerticalAlignment="Center"/>  <!-- Part 4 -->  <TextBox   Grid.Column="6"   x:Name="part4"   BorderThickness="0"   VerticalAlignment="Stretch"   VerticalContentAlignment="Center"   HorizontalContentAlignment="Center"   PreviewKeyDown="Part4_KeyDown"   local:FocusChangeExtension.IsFocused="{Binding IsPart4Focused}"   Style="{StaticResource CustomTextBoxTextStyle}"   Validation.ErrorTemplate="{StaticResource validationTemplate}">   <TextBox.Text>    <Binding Path="Part4" UpdateSourceTrigger="PropertyChanged">     <Binding.ValidationRules>      <local:IPRangeValidationRule Max="255" Min="0" />     </Binding.ValidationRules>    </Binding>   </TextBox.Text>  </TextBox> </Grid></UserControl>

三、验证输入格式

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

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

自定义规则代码如下:

public class IPRangeValidationRule : ValidationRule{ private int _min; private int _max; public int Min {  get { return _min; }  set { _min = value; } } public int Max {  get { return _max; }  set { _max = value; } } public override ValidationResult Validate(object value, CultureInfo cultureInfo) {  int val = 0;  var strVal = (string)value;  try  {   if (strVal.Length > 0)   {    if (strVal.EndsWith("."))    {     return CheckRanges(strVal.Replace(".", ""));    }    // Allow dot character to move to next box    return CheckRanges(strVal);   }  }  catch (Exception e)  {   return new ValidationResult(false, "Illegal characters or " + e.Message);  }  if ((val < Min) || (val > Max))  {   return new ValidationResult(false,    "Please enter the value in the range: " + Min + " - " + Max + ".");  }  else  {   return ValidationResult.ValidResult;  } } private ValidationResult CheckRanges(string strVal) {  if (int.TryParse(strVal, out var res))  {   if ((res < Min) || (res > Max))   {    return new ValidationResult(false,     "Please enter the value in the range: " + Min + " - " + Max + ".");   }   else   {    return ValidationResult.ValidResult;   }  }  else  {   return new ValidationResult(false, "Illegal characters entered");  } }}

四、控制焦点变化

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

附加属性的代码如下:

public static class FocusChangeExtension{ public static bool GetIsFocused(DependencyObject obj) {  return (bool)obj.GetValue(IsFocusedProperty); } public static void SetIsFocused(DependencyObject obj, bool value) {  obj.SetValue(IsFocusedProperty, value); } public static readonly DependencyProperty IsFocusedProperty =  DependencyProperty.RegisterAttached(   "IsFocused", typeof(bool), typeof(FocusChangeExtension),   new UIPropertyMetadata(false, OnIsFocusedPropertyChanged)); private static void OnIsFocusedPropertyChanged(  DependencyObject d,  DependencyPropertyChangedEventArgs e) {  var control = (UIElement)d;  if ((bool)e.NewValue)  {   control.Focus();  } }}

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

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

private void Part2_KeyDown(object sender, System.Windows.Input.KeyEventArgs e){ if (e.Key == Key.Back && part2.Text == "") {  part1.Focus(); } if (e.Key == Key.Right && part2.CaretIndex == part2.Text.Length) {  part3.Focus();  e.Handled = true; } if (e.Key == Key.Left && part2.CaretIndex == 0) {  part1.Focus();  e.Handled = true; } if (e.KeyboardDevice.Modifiers.HasFlag(ModifierKeys.Control) && e.Key == Key.C) {  if (part2.SelectionLength == 0)  {   var vm = this.DataContext as IpAddressViewModel;   Clipboard.SetText(vm.AddressText);  } }}

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

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

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

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

public class IpAddressViewModel : INotifyPropertyChanged{ public event EventHandler AddressChanged; public string AddressText {  get { return $"{Part1??"0"}.{Part2??"0"}.{Part3??"0"}.{Part4??"0"}"; } } private bool isPart1Focused; public bool IsPart1Focused {  get { return isPart1Focused; }  set { isPart1Focused = value; OnPropertyChanged(); } } private string part1; public string Part1 {  get { return part1; }  set  {   part1 = value;   SetFocus(true, false, false, false);   var moveNext = CanMoveNext(ref part1);   OnPropertyChanged();   OnPropertyChanged(nameof(AddressText));   AddressChanged?.Invoke(this, EventArgs.Empty);   if (moveNext)   {    SetFocus(false, true, false, false);   }  } }  private bool isPart2Focused; public bool IsPart2Focused {  get { return isPart2Focused; }  set { isPart2Focused = value; OnPropertyChanged(); } } private string part2; public string Part2 {  get { return part2; }  set  {   part2 = value;   SetFocus(false, true, false, false);   var moveNext = CanMoveNext(ref part2);   OnPropertyChanged();   OnPropertyChanged(nameof(AddressText));   AddressChanged?.Invoke(this, EventArgs.Empty);   if (moveNext)   {    SetFocus(false, false, true, false);   }  } } private bool isPart3Focused; public bool IsPart3Focused {  get { return isPart3Focused; }  set { isPart3Focused = value; OnPropertyChanged(); } } private string part3; public string Part3 {  get { return part3; }  set  {   part3 = value;   SetFocus(false, false, true, false);   var moveNext = CanMoveNext(ref part3);   OnPropertyChanged();   OnPropertyChanged(nameof(AddressText));   AddressChanged?.Invoke(this, EventArgs.Empty);   if (moveNext)   {    SetFocus(false, false, false, true);   }  } } private bool isPart4Focused; public bool IsPart4Focused {  get { return isPart4Focused; }  set { isPart4Focused = value; OnPropertyChanged(); } } private string part4; public string Part4 {  get { return part4; }  set  {   part4 = value;   SetFocus(false, false, false, true);   var moveNext = CanMoveNext(ref part4);   OnPropertyChanged();   OnPropertyChanged(nameof(AddressText));   AddressChanged?.Invoke(this, EventArgs.Empty);  } }  public void SetAddress(string address) {  if (string.IsNullOrWhiteSpace(address))   return;  var parts = address.Split('.');     if (int.TryParse(parts[0], out var num0))  {   Part1 = num0.ToString();  }  if (int.TryParse(parts[1], out var num1))  {   Part2 = parts[1];  }  if (int.TryParse(parts[2], out var num2))  {   Part3 = parts[2];  }  if (int.TryParse(parts[3], out var num3))  {   Part4 = parts[3];  } } private bool CanMoveNext(ref string part) {  bool moveNext = false;  if (!string.IsNullOrWhiteSpace(part))  {   if (part.Length >= 3)   {    moveNext = true;   }   if (part.EndsWith("."))   {    moveNext = true;    part = part.Replace(".", "");   }  }  return moveNext; } private void SetFocus(bool part1, bool part2, bool part3, bool part4) {  IsPart1Focused = part1;  IsPart2Focused = part2;  IsPart3Focused = part3;  IsPart4Focused = part4; } public event PropertyChangedEventHandler PropertyChanged; protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) {  PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); }}

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

关于“WPF如何自定义实现IP地址输入控件”这篇文章就分享到这里了,希望以上内容可以对大家有一定的帮助,使各位可以学到更多知识,如果觉得文章不错,请把它分享出去让更多的人看到。

文章题目:WPF如何自定义实现IP地址输入控件
URL链接:https://www.cdcxhl.com/article32/pcehpc.html

成都网站建设公司_创新互联,为您提供小程序开发电子商务网站收录关键词优化网站设计虚拟主机

广告

声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 创新互联

成都网站建设公司