WPF 实现IP输入控件

控件名:IPEditBox

作   者:WPFDevelopersOrg - 驚鏵

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

  • 框架使用.NET4 至 .NET6

  • Visual Studio 2022;

b1dfd2276fa76ba4424566ad0019ffcf.png d7f774d2dfc08de09e9a4da30c6b823d.png
  • 使用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>
c58411789b65e1c2d69d6d5904757bd8.gif

参考资料

[1]

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值