如何创建像Windows IP地址字段的Masked TextBox

时间:2016-02-10 19:32:28

标签: c# wpf

如何在<div class="col-md-6 fieldHeight"> <div class="form-group"> <label for="issueFromDate" class="col-md-5">Issue Date Range:</label> <div class="col-md-7"> <div class="row"> <div class="col-md-5 changeWdh"> <input type="text" class="form-control" ng-model="riskAssessmentDTO.issueFromDate" name="issueFromDate" id="issueFromDate" disabled /> </div> <div class="col-md-1"> <label class="control-label padd15"> To:</label> </div> <div class="col-md-5 changeWdh"> <input type="text" class="form-control" ng-model="riskAssessmentDTO.issueToDate" name="issueToDate" id="issueToDate" disabled /> </div> </div> </div> </div> </div> 中创建类似于Windows IP地址字段的numeric Masked TextBox,在点击WPF按钮后跳转到下一个区域

Windows IP Address field

1 个答案:

答案 0 :(得分:19)

我为此使用了UserControl。我确定它不是最完美的一个,但也许这对你来说是一个很好的起点。

UserControl XAML:

<UserControl x:Class="IPTextBoxDemo.IPTextBox"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             mc:Ignorable="d" 
             d:DesignHeight="50" d:DesignWidth="300"
             FocusManager.IsFocusScope="True">
    <UserControl.Resources>
        <Style x:Key="{x:Type TextBox}" TargetType="{x:Type TextBox}">
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="TextBoxBase">
                        <Border BorderThickness="{TemplateBinding Border.BorderThickness}" 
                                BorderBrush="{TemplateBinding Border.BorderBrush}" 
                                Background="{TemplateBinding Panel.Background}" 
                                Name="border" 
                                SnapsToDevicePixels="True">
                            <ScrollViewer HorizontalScrollBarVisibility="Hidden" 
                                          VerticalScrollBarVisibility="Hidden" 
                                          Name="PART_ContentHost" 
                                          Focusable="False" />
                        </Border>
                        <ControlTemplate.Triggers>
                            <Trigger Property="IsEnabled" Value="False">
                                <Setter Property="Opacity" TargetName="border" Value="0.56" />
                            </Trigger>
                        </ControlTemplate.Triggers>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </UserControl.Resources>

    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*" />
            <ColumnDefinition Width="10" />

            <ColumnDefinition Width="*" />
            <ColumnDefinition Width="10" />

            <ColumnDefinition Width="*" />
            <ColumnDefinition Width="10" />

            <ColumnDefinition Width="*" />
        </Grid.ColumnDefinitions>

        <TextBox x:Name="FirstSegment" Grid.Column="0" TextAlignment="Center" MaxLength="3" BorderThickness="1,1,0,1" VerticalContentAlignment="Center" 
                 TextChanged="TextBoxBase_OnTextChanged" PreviewKeyDown="UIElement_OnPreviewKeyDown" DataObject.Pasting="DataObject_OnPasting" />
        <TextBox Grid.Column="1" Text="." TextAlignment="Center" IsReadOnly="True" Focusable="False" BorderThickness="0,1,0,1" VerticalContentAlignment="Center"
                 IsReadOnlyCaretVisible="False"/>

        <TextBox x:Name="SecondSegment" Grid.Column="2" TextAlignment="Center" MaxLength="3" BorderThickness="0,1,0,1" VerticalContentAlignment="Center"
                 TextChanged="TextBoxBase_OnTextChanged" PreviewKeyDown="UIElement_OnPreviewKeyDown" DataObject.Pasting="DataObject_OnPasting" />
        <TextBox Grid.Column="3" Text="." TextAlignment="Center" IsReadOnly="True" Focusable="False" BorderThickness="0,1,0,1" VerticalContentAlignment="Center"
                 IsReadOnlyCaretVisible="False"/>

        <TextBox x:Name="ThirdSegment" Grid.Column="4" TextAlignment="Center" MaxLength="3" BorderThickness="0,1,0,1" VerticalContentAlignment="Center"
                 TextChanged="TextBoxBase_OnTextChanged" PreviewKeyDown="UIElement_OnPreviewKeyDown" DataObject.Pasting="DataObject_OnPasting" />
        <TextBox Grid.Column="5" Text="." TextAlignment="Center" IsReadOnly="True" Focusable="False" BorderThickness="0,1,0,1" VerticalContentAlignment="Center"
                 IsReadOnlyCaretVisible="False" />

        <TextBox x:Name="LastSegment" Grid.Column="6" TextAlignment="Center" MaxLength="3" BorderThickness="0,1,1,1" VerticalContentAlignment="Center"
                 TextChanged="TextBoxBase_OnTextChanged" PreviewKeyDown="UIElement_OnPreviewKeyDown" DataObject.Pasting="DataObject_OnPasting" />
    </Grid>
</UserControl>

XAML由一个主Grid组成,其中有七列,其中四列用于TextBox es,您将输入您的号码,三个用于IP地址中的固定点。

UserControl代码隐藏:

namespace IPTextBoxDemo
{
    using System.Collections.Generic;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Input;

    /// <summary>
    /// Interaction logic for IPTextBox.xaml
    /// </summary>
    public partial class IPTextBox : UserControl
    {
        private static readonly List<Key> DigitKeys = new List<Key> { Key.D0, Key.D1, Key.D2, Key.D3, Key.D4, Key.D5, Key.D6, Key.D7, Key.D8, Key.D9 };
        private static readonly List<Key> MoveForwardKeys = new List<Key> { Key.Right };
        private static readonly List<Key> MoveBackwardKeys = new List<Key> { Key.Left };
        private static readonly List<Key> OtherAllowedKeys = new List<Key> { Key.Tab, Key.Delete };

        private readonly List<TextBox> _segments = new List<TextBox>();

        private bool _suppressAddressUpdate = false;

        public IPTextBox()
        {
            InitializeComponent();
            _segments.Add(FirstSegment);
            _segments.Add(SecondSegment);
            _segments.Add(ThirdSegment);
            _segments.Add(LastSegment);
        }

        public static readonly DependencyProperty AddressProperty = DependencyProperty.Register(
            "Address", typeof (string), typeof (IPTextBox), new FrameworkPropertyMetadata(default(string), AddressChanged)
            {
                BindsTwoWayByDefault = true
            });

        private static void AddressChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
        {
            var ipTextBox = dependencyObject as IPTextBox;
            var text = e.NewValue as string;

            if (text != null && ipTextBox != null)
            {
                ipTextBox._suppressAddressUpdate = true;
                var i = 0;
                foreach (var segment in text.Split('.'))
                {
                    ipTextBox._segments[i].Text = segment;
                    i++;
                }
                ipTextBox._suppressAddressUpdate = false;
            }
        }

        public string Address
        {
            get { return (string) GetValue(AddressProperty); }
            set { SetValue(AddressProperty, value); }
        }

        private void UIElement_OnPreviewKeyDown(object sender, KeyEventArgs e)
        {
            if (DigitKeys.Contains(e.Key))
            {
                e.Handled = ShouldCancelDigitKeyPress();
                HandleDigitPress();
            }
            else if(MoveBackwardKeys.Contains(e.Key))
            {
                e.Handled = ShouldCancelBackwardKeyPress();
                HandleBackwardKeyPress();
            }
            else if (MoveForwardKeys.Contains(e.Key))
            {
                e.Handled = ShouldCancelForwardKeyPress();
                HandleForwardKeyPress();
            } else if (e.Key == Key.Back)
            {
                HandleBackspaceKeyPress();
            }
            else if (e.Key == Key.OemPeriod)
            {
                e.Handled = true;
                HandlePeriodKeyPress();
            }
            else
            {
                e.Handled = !AreOtherAllowedKeysPressed(e);
            }
        }

        private bool AreOtherAllowedKeysPressed(KeyEventArgs e)
        {
            return e.Key == Key.C && ((e.KeyboardDevice.Modifiers & ModifierKeys.Control) != 0) ||
                   e.Key == Key.V && ((e.KeyboardDevice.Modifiers & ModifierKeys.Control) != 0) ||
                   e.Key == Key.A && ((e.KeyboardDevice.Modifiers & ModifierKeys.Control) != 0) ||
                   e.Key == Key.X && ((e.KeyboardDevice.Modifiers & ModifierKeys.Control) != 0) ||
                   OtherAllowedKeys.Contains(e.Key);
        }

        private void HandleDigitPress()
        {
            var currentTextBox = FocusManager.GetFocusedElement(this) as TextBox;

            if (currentTextBox != null && currentTextBox.Text.Length == 3 &&
                currentTextBox.CaretIndex == 3 && currentTextBox.SelectedText.Length == 0)
            {
                MoveFocusToNextSegment(currentTextBox);
            }
        }

        private bool ShouldCancelDigitKeyPress()
        {
            var currentTextBox = FocusManager.GetFocusedElement(this) as TextBox;
            return currentTextBox != null && 
                   currentTextBox.Text.Length == 3 && 
                   currentTextBox.CaretIndex == 3 && 
                   currentTextBox.SelectedText.Length == 0;
        }

        private void TextBoxBase_OnTextChanged(object sender, TextChangedEventArgs e)
        {
            if (!_suppressAddressUpdate)
            {
                Address = string.Format("{0}.{1}.{2}.{3}", FirstSegment.Text, SecondSegment.Text, ThirdSegment.Text, LastSegment.Text);
            }

            var currentTextBox = FocusManager.GetFocusedElement(this) as TextBox;

            if (currentTextBox != null && currentTextBox.Text.Length == 3 && currentTextBox.CaretIndex == 3)
            {
                MoveFocusToNextSegment(currentTextBox);
            }
        }

        private bool ShouldCancelBackwardKeyPress()
        {
            var currentTextBox = FocusManager.GetFocusedElement(this) as TextBox;
            return currentTextBox != null && currentTextBox.CaretIndex == 0;
        }

        private void HandleBackspaceKeyPress()
        {
            var currentTextBox = FocusManager.GetFocusedElement(this) as TextBox;

            if (currentTextBox != null && currentTextBox.CaretIndex == 0 && currentTextBox.SelectedText.Length == 0)
            {
                MoveFocusToPreviousSegment(currentTextBox);
            }
        }

        private void HandleBackwardKeyPress()
        {
            var currentTextBox = FocusManager.GetFocusedElement(this) as TextBox;

            if (currentTextBox != null && currentTextBox.CaretIndex == 0)
            {
                MoveFocusToPreviousSegment(currentTextBox);
            }
        }

        private bool ShouldCancelForwardKeyPress()
        {
            var currentTextBox = FocusManager.GetFocusedElement(this) as TextBox;
            return currentTextBox != null && currentTextBox.CaretIndex == 3;
        }

        private void HandleForwardKeyPress()
        {
            var currentTextBox = FocusManager.GetFocusedElement(this) as TextBox;

            if (currentTextBox != null && currentTextBox.CaretIndex == currentTextBox.Text.Length)
            {
                MoveFocusToNextSegment(currentTextBox);
            }
        }

        private void HandlePeriodKeyPress()
        {
            var currentTextBox = FocusManager.GetFocusedElement(this) as TextBox;

            if (currentTextBox != null && currentTextBox.Text.Length > 0 && currentTextBox.CaretIndex == currentTextBox.Text.Length)
            {
                MoveFocusToNextSegment(currentTextBox);
            }
        }

        private void MoveFocusToPreviousSegment(TextBox currentTextBox)
        {
            if (!ReferenceEquals(currentTextBox, FirstSegment))
            {
                var previousSegmentIndex = _segments.FindIndex(box => ReferenceEquals(box, currentTextBox)) - 1;
                currentTextBox.SelectionLength = 0;
                currentTextBox.MoveFocus(new TraversalRequest(FocusNavigationDirection.Previous));
                _segments[previousSegmentIndex].CaretIndex = _segments[previousSegmentIndex].Text.Length;
            }
        }

        private void MoveFocusToNextSegment(TextBox currentTextBox)
        {
            if (!ReferenceEquals(currentTextBox, LastSegment))
            {
                currentTextBox.SelectionLength = 0;
                currentTextBox.MoveFocus(new TraversalRequest(FocusNavigationDirection.Next));
            }
        }

        private void DataObject_OnPasting(object sender, DataObjectPastingEventArgs e)
        {
            var isText = e.SourceDataObject.GetDataPresent(DataFormats.UnicodeText, true);
            if (!isText)
            {
                e.CancelCommand();
                return;
            }

            var text = e.SourceDataObject.GetData(DataFormats.UnicodeText) as string;

            int num;

            if (!int.TryParse(text, out num))
            {
                e.CancelCommand();
            }
        }
    }
}

代码隐藏主要处理不同的事件:

  • 如果用户按退格键会发生什么,
  • 如果用户按下左箭头或右箭头,会发生什么,
  • 如果用户按下该点,会发生什么,
  • 如果用户试图粘贴某些内容会发生什么。

依旧等等。

用法如下:

<ipTextBoxDemo:IPTextBox Width="150" Address="{Binding AddressInVM}"></ipTextBoxDemo:IPTextBox>

正如您所看到的,UserControl公开了一个名为string的{​​{1}}属性(默认情况下绑定为双向)。

如果绑定的来源发生变化(此处Address,那么来自VM的更改)则AddressInVm将按点分割文本,并将结果值放入四个{{ 1}} ES。

如果绑定的目标发生变化(用户输入框中的某些内容),则会发现四个UserControl es&#39; TextBox属性将被连接。

为了使这一点正确,你还必须实现验证,因为TextBox中没有这个,但我把它留给你作为练习;)

结果:

The result