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

一、前言

成都创新互联主营曹妃甸网站建设的网络公司,主营网站建设方案,重庆APP软件开发,曹妃甸h5小程序定制开发搭建,曹妃甸网站营销推广欢迎曹妃甸等地区企业咨询

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

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

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

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

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

二、构成

Grid+TextBox*4+TextBlock*3

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

界面代码如下:


 
  
   
    
    
   
  
  
 
 
  
   
   
   
   
   
   
   
  

  
  
   
    
     
      
     
    
   
  
  

  
  
   
    
     
      
     
    
   
  
  

  
  
   
    
     
      
     
    
   
  
  

  
  
   
    
     
      
     
    
   
  
 

三、验证输入格式

界面中为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地址输入控件

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

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

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对创新互联的支持。


网站标题:WPF自定义实现IP地址输入控件
链接URL:http://azwzsj.com/article/iiggde.html