控件名:IPEditBox
作 者:WPFDevelopersOrg - 驚鏵
原文链接[1]:https://github.com/WPFDevelopersOrg/WPFDevelopers
框架使用
.NET4 至 .NET6
;Visual Studio 2022
;


使用Grid来分为七列
TextBox控件PART_TextBox1位于第一列。
TextBox控件PART_TextBox2位于第三列。
TextBox控件PART_TextBox3位于第五列。
TextBox控件PART_TextBox4位于第七列。
1)新增 IPEditBox.cs
代码如下:
TextBox1_TextChanged方法:当TextBox1的文本发生变化时触发。如果TextBox1中的文本长度大于等于3,焦点将转移到TextBox2,并调用UpdateText方法更新Text。
TextBox2_TextChanged方法:当TextBox2的文本发生变化时触发。如果TextBox2中的文本长度大于等于3,焦点将转移到TextBox3,并调用UpdateText方法更新Text。
TextBox3_TextChanged方法:当TextBox3的文本发生变化时触发。如果TextBox3中的文本长度大于等于3,焦点将转移到TextBox4,并调用UpdateText方法更新Text。
TextBox4_TextChanged方法:当TextBox4的文本发生变化时触发。无论文本长度如何,并调用UpdateText方法更新Text。
监听TextBox1、2、3、4控件的PreviewKeyDown事件处理。
是否按下Ctrl+V键组合(粘贴快捷键),
如果是,则调用ClipboardHandle()方法,并将事件参数的Handled属性设置为true。
PasteTextIPTextBox方法负责处理粘贴的文本。如果粘贴的文本为空或只包含空白字符,则清除四个TextBox控件(_textBox1、_textBox2、_textBox3、_textBox4)的内容。否则,它通过句点('.')将文本分割,并将每个部分分配给_textboxBoxes数组中对应的TextBox控件。
监听TextBox1、2、3、4控件的Loaded事件处理。在TextBox加载完成时,通过CommandManager的AddPreviewExecutedHandler方法将TextBox的PreviewExecuted事件与TextBox_PreviewExecuted方法关联
如果执行的是复制(ApplicationCommands.Copy),则将_textBox1、_textBox2、_textBox3、_textBox4四个TextBox控件中的文本合并成一个IP地址字符串,并将该字符串设置为剪贴板的文本内容并更新Text。
如果是粘贴命令则将调用ClipboardHandle()方法。
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
namespace WPFDevelopers.Controls
{
[TemplatePart(Name = TextBox1TemplateName, Type = typeof(TextBox))]
[TemplatePart(Name = TextBox2TemplateName, Type = typeof(TextBox))]
[TemplatePart(Name = TextBox3TemplateName, Type = typeof(TextBox))]
[TemplatePart(Name = TextBox4TemplateName, Type = typeof(TextBox))]
public class IPEditBox : Control
{
private const string TextBox1TemplateName = "PART_TextBox1";
private const string TextBox2TemplateName = "PART_TextBox2";
private const string TextBox3TemplateName = "PART_TextBox3";
private const string TextBox4TemplateName = "PART_TextBox4";
public string Text
{
get { return (string)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register("Text", typeof(string), typeof(IPEditBox), new PropertyMetadata(string.Empty, OnTextChanged));
private static void OnTextChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var ctrl = d as IPEditBox;
if (e.NewValue is string text && !ctrl._isChangingText)
ctrl.PasteTextIPTextBox(text);
}
private TextBox _textBox1, _textBox2, _textBox3, _textBox4;
private bool _isChangingText = false;
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
_textBox1 = GetTemplateChild(TextBox1TemplateName) as TextBox;
if (_textBox1 != null)
{
_textBox1.TextChanged -= TextBox1_TextChanged;
_textBox1.TextChanged += TextBox1_TextChanged;
_textBox1.PreviewKeyDown -= TextBox_PreviewKeyDown;
_textBox1.PreviewKeyDown += TextBox_PreviewKeyDown;
_textBox1.Loaded -= TextBox_Loaded;
_textBox1.Loaded += TextBox_Loaded;
}
_textBox2 = GetTemplateChild(TextBox2TemplateName) as TextBox;
if (_textBox2 != null)
{
_textBox2.TextChanged -= TextBox2_TextChanged;
_textBox2.TextChanged += TextBox2_TextChanged;
_textBox2.PreviewKeyDown -= TextBox_PreviewKeyDown;
_textBox2.PreviewKeyDown += TextBox_PreviewKeyDown;
_textBox2.Loaded -= TextBox_Loaded; ;
_textBox2.Loaded += TextBox_Loaded;
}
_textBox3 = GetTemplateChild(TextBox3TemplateName) as TextBox;
if (_textBox3 != null)
{
_textBox3.TextChanged -= TextBox3_TextChanged;
_textBox3.TextChanged += TextBox3_TextChanged;
_textBox3.PreviewKeyDown -= TextBox_PreviewKeyDown;
_textBox3.PreviewKeyDown += TextBox_PreviewKeyDown;
_textBox3.Loaded -= TextBox_Loaded;
_textBox3.Loaded += TextBox_Loaded;
}
_textBox4 = GetTemplateChild(TextBox4TemplateName) as TextBox;
_textBox4.TextChanged -= TextBox4_TextChanged;
_textBox4.TextChanged += TextBox4_TextChanged;
_textBox4.PreviewKeyDown -= TextBox_PreviewKeyDown;
_textBox4.PreviewKeyDown += TextBox_PreviewKeyDown;
_textBox4.Loaded -= TextBox_Loaded;
_textBox4.Loaded += TextBox_Loaded;
}
private void TextBox1_TextChanged(object sender, TextChangedEventArgs e)
{
if (_textBox1.Text.ToString().Length >= 3) _textBox2.Focus();
UpdateText();
}
private void TextBox2_TextChanged(object sender, TextChangedEventArgs e)
{
if (_textBox2.Text.ToString().Length >= 3) _textBox3.Focus();
UpdateText();
}
private void TextBox3_TextChanged(object sender, TextChangedEventArgs e)
{
if (_textBox3.Text.ToString().Length >= 3) _textBox4.Focus();
UpdateText();
}
private void TextBox4_TextChanged(object sender, TextChangedEventArgs e)
{
UpdateText();
}
void TextBox_Loaded(object sender, RoutedEventArgs e)
{
CommandManager.AddPreviewExecutedHandler((sender as TextBox), TextBox_PreviewExecuted);
}
void TextBox_PreviewExecuted(object sender, ExecutedRoutedEventArgs e)
{
if (e.Command == ApplicationCommands.Paste)
{
ClipboardHandle();
UpdateText();
e.Handled = true;
}
else if (e.Command == ApplicationCommands.Copy)
{
var ip = $"{_textBox1.Text}.{_textBox2.Text}.{_textBox3.Text}.{_textBox4.Text}";
Clipboard.SetText(ip);
e.Handled = true;
}
}
void ClipboardHandle()
{
var data = Clipboard.GetDataObject();
if (data.GetDataPresent(DataFormats.Text))
{
var text = (string)data.GetData(DataFormats.UnicodeText);
PasteTextIPTextBox(text);
}
}
void TextBox_PreviewKeyDown(object sender, KeyEventArgs e)
{
if (e.KeyboardDevice.Modifiers.HasFlag(ModifierKeys.Control) && e.Key == Key.V)
{
ClipboardHandle();
_isChangingText = false;
e.Handled = true;
}
else if (e.Key == Key.Delete || e.Key == Key.Back)
{
_isChangingText = true;
}
else
_isChangingText = false;
}
void PasteTextIPTextBox(string text)
{
_textBox1.TextChanged -= TextBox1_TextChanged;
_textBox2.TextChanged -= TextBox2_TextChanged;
_textBox3.TextChanged -= TextBox3_TextChanged;
_textBox4.TextChanged -= TextBox4_TextChanged;
if (string.IsNullOrWhiteSpace(text))
{
_textBox1.Text = string.Empty;
_textBox2.Text = string.Empty;
_textBox3.Text = string.Empty;
_textBox4.Text = string.Empty;
}
else
{
var strs = text.Split('.');
var _textboxBoxes = new TextBox[] { _textBox1, _textBox2, _textBox3, _textBox4 };
for (short i = 0; i < _textboxBoxes.Length; i++)
{
var str = i < strs.Length ? strs[i] : string.Empty;
_textboxBoxes[i].Text = str;
}
}
_textBox1.TextChanged += TextBox1_TextChanged;
_textBox2.TextChanged += TextBox2_TextChanged;
_textBox3.TextChanged += TextBox3_TextChanged;
_textBox4.TextChanged += TextBox4_TextChanged;
}
void UpdateText()
{
var segments = new string[4]
{
_textBox1.Text.Trim(),
_textBox2.Text.Trim(),
_textBox3.Text.Trim(),
_textBox4.Text.Trim()
};
var allEmpty = segments.All(string.IsNullOrEmpty);
if (allEmpty)
{
SetValue(TextProperty, string.Empty);
return;
}
var noEmpty = segments.Where(s => !string.IsNullOrWhiteSpace(s));
if (noEmpty.Count() != 4) return;
var ip = string.Join(".", noEmpty);
if (ip != Text)
SetValue(TextProperty, ip);
}
}
}
2)新增 ColorPicker.xaml
代码如下:
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="clr-namespace:WPFDevelopers.Controls"
xmlns:helpers="clr-namespace:WPFDevelopers.Helpers">
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Basic/ControlBasic.xaml" />
</ResourceDictionary.MergedDictionaries>
<Style
x:Key="WD.IPEditBox"
BasedOn="{StaticResource WD.ControlBasicStyle}"
TargetType="{x:Type controls:IPEditBox}">
<Setter Property="BorderThickness" Value="1" />
<Setter Property="BorderBrush" Value="{DynamicResource WD.BaseSolidColorBrush}" />
<Setter Property="Background" Value="{DynamicResource WD.BackgroundSolidColorBrush}" />
<Setter Property="VerticalContentAlignment" Value="Center" />
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
<Setter Property="Padding" Value="{StaticResource WD.DefaultPadding}" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type controls:IPEditBox}">
<ControlTemplate.Resources>
<Style BasedOn="{StaticResource WD.DefaultTextBox}" TargetType="{x:Type TextBox}">
<Setter Property="BorderThickness" Value="0" />
<Setter Property="helpers:TextBoxHelper.AllowOnlyNumericInput" Value="True" />
<Setter Property="helpers:TextBoxHelper.MaxValue" Value="255" />
<Setter Property="helpers:TextBoxHelper.MinValue" Value="0" />
<Setter Property="VerticalContentAlignment" Value="Center" />
<Setter Property="HorizontalContentAlignment" Value="Center" />
</Style>
<Style TargetType="TextBlock">
<Setter Property="Text" Value="." />
<Setter Property="VerticalAlignment" Value="Center" />
</Style>
</ControlTemplate.Resources>
<Border
x:Name="Root"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="{Binding Path=(helpers:ElementHelper.CornerRadius), RelativeSource={RelativeSource TemplatedParent}}"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
UseLayoutRounding="{TemplateBinding UseLayoutRounding}">
<ScrollViewer HorizontalScrollBarVisibility="Auto">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition Width="Auto" />
<ColumnDefinition />
<ColumnDefinition Width="Auto" />
<ColumnDefinition />
<ColumnDefinition Width="Auto" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<TextBox x:Name="PART_TextBox1" />
<TextBlock Grid.Column="1" />
<TextBox x:Name="PART_TextBox2" Grid.Column="2" />
<TextBlock Grid.Column="3" />
<TextBox x:Name="PART_TextBox3" Grid.Column="4" />
<TextBlock Grid.Column="5" />
<TextBox x:Name="PART_TextBox4" Grid.Column="6" />
</Grid>
</ScrollViewer>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsKeyboardFocused" Value="True">
<Setter Property="BorderBrush" Value="{DynamicResource WD.PrimaryNormalSolidColorBrush}" />
</Trigger>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="BorderBrush" Value="{DynamicResource WD.PrimaryNormalSolidColorBrush}" />
</Trigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsKeyboardFocused" Value="True" />
<Condition Property="IsMouseOver" Value="False" />
</MultiTrigger.Conditions>
<Setter Property="BorderBrush" Value="{DynamicResource WD.PrimaryNormalSolidColorBrush}" />
</MultiTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style BasedOn="{StaticResource WD.IPEditBox}" TargetType="{x:Type controls:IPEditBox}" />
</ResourceDictionary>

[1]
原文链接: https://github.com/WPFDevelopersOrg/WPFDevelopers