添加链接
link之家
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接
WPF上位机自定义控件系列:触摸屏数字输入键盘 WxNumericalKeyboard

WPF上位机自定义控件系列:触摸屏数字输入键盘 WxNumericalKeyboard

WPF 上位机自定义控件系列

  • 触摸屏数字输入键盘 WxNumericalKeyboard

实际项目中,客户在上位机触摸屏中输入数值时,希望有类似 PLC 触摸屏上的数字键盘输入效果,而不弹出系统自带的虚拟键盘

  • 最大值、最小值
  • 浮点数(两位小数)、整数

初始化时传入当前值,关闭窗体时获取最终值

xaml 文件

<Window x:Class="WpfControlsX.ControlX.WxNumericalKeyboard"
        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:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:cx="clr-namespace:WpfControlsX.ControlX"
        mc:Ignorable="d"
        Background="{DynamicResource BrushRegion}"
        BorderBrush="{DynamicResource BrushPrimary}"
        BorderThickness="1"
        WindowStyle="None"
        ShowInTaskbar="False"
        ResizeMode="NoResize"
        WindowStartupLocation="CenterOwner"
        Title="数值输入" 
        Height="230" Width="350">
    <Window.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="../../Styles/Theme.xaml"/>
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Window.Resources>
    <StackPanel Orientation="Vertical">
        <Grid Background="{DynamicResource BrushPrimary}">
            <Grid.ColumnDefinitions>
                <ColumnDefinition/>
                <ColumnDefinition Width="auto"/>
            </Grid.ColumnDefinitions>
            <UniformGrid x:Name="PART_Double" Columns="2" Visibility="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=cx:WxNumericalKeyboard}, Path=IsDouble, Converter={StaticResource Bool2Visibility}}">
                <cx:WxTextBox Title="MIN:" FontSize="14" Foreground="{DynamicResource BrushReversedText}" HorizontalContentAlignment="Left" BorderThickness="0" IsHitTestVisible="False"
                              Text="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=cx:WxNumericalKeyboard}, Path=Minimum, StringFormat={}{0:F2}}"/>
                <cx:WxTextBox Title="MAX:" FontSize="14" Foreground="{DynamicResource BrushReversedText}" HorizontalContentAlignment="Left" BorderThickness="0" IsHitTestVisible="False"
                              Text="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=cx:WxNumericalKeyboard}, Path=Maximum, StringFormat={}{0:F2}}"/>
            </UniformGrid>
            <UniformGrid x:Name="PART_Int" Columns="2" Visibility="{Binding ElementName=PART_Double, Path=Visibility, Converter={StaticResource Visible2Reverse}}">
                <cx:WxTextBox Title="MIN:" FontSize="14" Foreground="{DynamicResource BrushReversedText}"
                              HorizontalContentAlignment="Left" BorderThickness="0" IsHitTestVisible="False"
                              Text="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=cx:WxNumericalKeyboard}, Path=Minimum}"/>
                <cx:WxTextBox Title="MAX:" FontSize="14" Foreground="{DynamicResource BrushReversedText}"
                              HorizontalContentAlignment="Left" BorderThickness="0" IsHitTestVisible="False"
                              Text="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=cx:WxNumericalKeyboard}, Path=Maximum}"/>
            </UniformGrid>
            <cx:WxButton Grid.Column="1" ButtonType="Icon" Icon="{DynamicResource IconClose}" IconSize="12" Padding="5" Margin="5"
                         HorizontalAlignment="Right" VerticalAlignment="Top" ToolTip="Close" IsTabStop="False"
                         Background="{DynamicResource BrushRegion}" Foreground="{DynamicResource BrushPrimary}" Click="WxButtonClose_Click"/>
        </Grid>
        <StackPanel Orientation="Horizontal">
            <cx:WxTextBox BorderThickness="1" Background="{DynamicResource BrushDataGridBackgroundLight}" CornerRadius="5" Width="295"
                          Text="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=cx:WxNumericalKeyboard}, Path=Value, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
            <cx:WxButton ButtonType="Icon" Icon="{DynamicResource IconLeftBold}" IconSize="18" Padding="0" Margin="0" Width="40" Height="32"
                         HorizontalAlignment="Right" VerticalAlignment="Center" ToolTip="Close" IsTabStop="False"
                         Background="{DynamicResource BrushDataGridBackgroundLight}" Foreground="{DynamicResource BrushPrimary}" Click="WxButtonBackspace_Click"/>
        </StackPanel>
        <UniformGrid Columns="5">
            <cx:WxButton ButtonType="Text" Content="1" FontSize="22" HorizontalAlignment="Stretch" Margin="2" Background="{DynamicResource BrushDataGridBackgroundLight}"
                         Foreground="{DynamicResource BrushPrimary}" CornerRadius="5" Click="WxButton_Click"/>
            <cx:WxButton ButtonType="Text" Content="2" FontSize="22" HorizontalAlignment="Stretch" Margin="2" Background="{DynamicResource BrushDataGridBackgroundLight}"
                         Foreground="{DynamicResource BrushPrimary}" CornerRadius="5" Click="WxButton_Click"/>
            <cx:WxButton ButtonType="Text" Content="3" FontSize="22" HorizontalAlignment="Stretch" Margin="2" Background="{DynamicResource BrushDataGridBackgroundLight}"
                         Foreground="{DynamicResource BrushPrimary}" CornerRadius="5" Click="WxButton_Click"/>
            <cx:WxButton ButtonType="Text" Content="0" FontSize="22" HorizontalAlignment="Stretch" Margin="2" Background="{DynamicResource BrushDataGridBackgroundLight}"
                         Foreground="{DynamicResource BrushPrimary}" CornerRadius="5" Click="WxButton_Click"/>
            <cx:WxButton ButtonType="Text" Content="-" FontSize="22" HorizontalAlignment="Stretch" Margin="2" Background="{DynamicResource BrushDataGridBackgroundLight}"
                         Foreground="{DynamicResource BrushPrimary}" CornerRadius="5" Click="WxButton_Click"/>
        </UniformGrid>
        <UniformGrid Columns="5">
            <cx:WxButton ButtonType="Text" Content="4" FontSize="22" HorizontalAlignment="Stretch" Margin="2" Background="{DynamicResource BrushDataGridBackgroundLight}"
                         Foreground="{DynamicResource BrushPrimary}" CornerRadius="5" Click="WxButton_Click"/>
            <cx:WxButton ButtonType="Text" Content="5" FontSize="22" HorizontalAlignment="Stretch" Margin="2" Background="{DynamicResource BrushDataGridBackgroundLight}"
                         Foreground="{DynamicResource BrushPrimary}" CornerRadius="5" Click="WxButton_Click"/>
            <cx:WxButton ButtonType="Text" Content="6" FontSize="22" HorizontalAlignment="Stretch" Margin="2" Background="{DynamicResource BrushDataGridBackgroundLight}"
                         Foreground="{DynamicResource BrushPrimary}" CornerRadius="5" Click="WxButton_Click"/>
            <cx:WxButton ButtonType="Text" Content="·" FontSize="22" HorizontalAlignment="Stretch" Margin="2" Background="{DynamicResource BrushDataGridBackgroundLight}"
                         Foreground="{DynamicResource BrushPrimary}" CornerRadius="5" Click="WxButton_Click"/>
            <cx:WxButton ButtonType="Text" Content="CLR" FontSize="22" HorizontalAlignment="Stretch" Margin="2" Background="{DynamicResource BrushDataGridBackgroundLight}"
                         Foreground="{DynamicResource BrushPrimary}" CornerRadius="5" Click="WxButtonClear_Click"/>
        </UniformGrid>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="3*"/>
                <ColumnDefinition Width="2*"/>
            </Grid.ColumnDefinitions>
            <UniformGrid Columns="3">
                <cx:WxButton ButtonType="Text" Content="7" FontSize="22" HorizontalAlignment="Stretch" Margin="2" Background="{DynamicResource BrushDataGridBackgroundLight}"
                             Foreground="{DynamicResource BrushPrimary}" CornerRadius="5" Click="WxButton_Click"/>
                <cx:WxButton ButtonType="Text" Content="8" FontSize="22" HorizontalAlignment="Stretch" Margin="2" Background="{DynamicResource BrushDataGridBackgroundLight}"
                             Foreground="{DynamicResource BrushPrimary}" CornerRadius="5" Click="WxButton_Click"/>
                <cx:WxButton ButtonType="Text" Content="9" FontSize="22" HorizontalAlignment="Stretch" Margin="2" Background="{DynamicResource BrushDataGridBackgroundLight}"
                             Foreground="{DynamicResource BrushPrimary}" CornerRadius="5" Click="WxButton_Click"/>
            </UniformGrid>
            <cx:WxButton Grid.Column="1" ButtonType="Text" Content="Enter" FontSize="22" HorizontalAlignment="Stretch" Margin="2" Background="{DynamicResource BrushDataGridBackgroundLight}"
                         Foreground="{DynamicResource BrushPrimary}" CornerRadius="5" Click="WxButtonEnter_Click"/>
        </Grid>
    </StackPanel>
</Window>

后台文件

using System.Windows;
namespace WpfControlsX.ControlX
    /// <summary>
    /// WxNumericalKeyboard.xaml 的交互逻辑
    /// </summary>
    public partial class WxNumericalKeyboard : Window
        /// <summary>
        /// 当前值
        /// </summary>
        public string Value
            get { return (string)GetValue(ValueProperty); }
            set { SetValue(ValueProperty, value); }
        public static readonly DependencyProperty ValueProperty =
            DependencyProperty.Register("Value", typeof(string), typeof(WxNumericalKeyboard), new PropertyMetadata(null));
        /// <summary>
        /// 最小值
        /// </summary>
        public double Minimum
            get { return (double)GetValue(MinimumProperty); }
            set { SetValue(MinimumProperty, value); }
        public static readonly DependencyProperty MinimumProperty =
            DependencyProperty.Register("Minimum", typeof(double), typeof(WxNumericalKeyboard), new PropertyMetadata(0d));
        /// <summary>
        /// 最大值
        /// </summary>
        public double Maximum
            get { return (double)GetValue(MaximumProperty); }
            set { SetValue(MaximumProperty, value); }
        public static readonly DependencyProperty MaximumProperty =
            DependencyProperty.Register("Maximum", typeof(double), typeof(WxNumericalKeyboard), new PropertyMetadata(100d));
        /// <summary>
        /// 数据类型:浮点数、整数
        /// </summary>
        public bool IsDouble
            get { return (bool)GetValue(IsDoubleProperty); }
            set { SetValue(IsDoubleProperty, value); }
        public static readonly DependencyProperty IsDoubleProperty =
            DependencyProperty.Register("IsDouble", typeof(bool), typeof(WxNumericalKeyboard), new PropertyMetadata(true));
        public double Result { get; set; } = 0;
        public WxNumericalKeyboard(double value)
            InitializeComponent();
            Value = value.ToString();
            Result = value;
        private void WxButtonClose_Click(object sender, RoutedEventArgs e)
            Close();
        private void WxButtonBackspace_Click(object sender, RoutedEventArgs e)
            if (Value.Length > 1)
                Value = Value.Substring(0, Value.Length - 1);
                Value = "";
        private void WxButtonClear_Click(object sender, RoutedEventArgs e)
            Value = "";
        private void WxButtonEnter_Click(object sender, RoutedEventArgs e)
            if (double.TryParse(Value, out double result))
                Result = result;
            Close();
        private void WxButton_Click(object sender, RoutedEventArgs e)
            string name = (sender as WxButton).Content.ToString();
            name = name.Replace("·", ".");
            Value += name;
            // 浮点数
            if (IsDouble)
                // 首部不能连续两个 0
                if (Value.StartsWith("00"))
                    WxButtonBackspace_Click(null, null);
                // 首部不能单独 0
                if (Value.StartsWith("0") && !Value.StartsWith("0."))
                    Value = Value.Substring(1, Value.Length - 1);
                // 只能一个小数点
                int idx1 = Value.IndexOf(".");
                int idx2 = Value.LastIndexOf(".");
                if (idx1 != idx2)
                    WxButtonBackspace_Click(null, null);
                // 保留两位小数
                int idx = Value.IndexOf(".");
                if (idx > 0 && idx < Value.Length - 3)
                    WxButtonBackspace_Click(null, null);
            // 整数
                // 整数不能 0 和 . 开头
                if (Value.StartsWith("0") || Value.StartsWith("."))
                    Value = Value.Substring(1, Value.Length - 1);
                // 整数不能有小数点
                if (Value.Contains("."))
                    WxButtonBackspace_Click(null, null);
            // 尾部不能有 -
            if (Value.Length > 1 && Value.EndsWith("-"))
                WxButtonBackspace_Click(null, null);
            // 限定范围
            if (double.TryParse(Value, out double result))
                if (result < Minimum || result > Maximum)
                    WxButtonBackspace_Click(null, null);

引用方法

/// <summary>
/// 数值输入
/// </summary>
/// <param name="idx"></param>
public static double NumericalKeyboard(double value, double min = 0, double max = 1000, bool isDouble = true)
    WxNumericalKeyboard keyboard = new WxNumericalKeyboard(value)
        Minimum = min,
        Maximum = max,
        IsDouble = isDouble,
    Window win = null;
    if (Application.Current.Windows.Count > 0)
        win = Application.Current.Windows.OfType<Window>().FirstOrDefault(o => o.IsActive);
    if (win == null)
        keyboard.Show();
        keyboard.Owner = win;
        _ = keyboard.ShowDialog();
    return keyboard.Result;

鼠标左键双击触发

<cx:WxTextBox Title="浮点数" IsReadOnly="True" Text="0.00" MouseDoubleClick="WxTextBox_MouseDoubleClick"/>
<cx:WxTextBox Title="整数" IsReadOnly="True" Text="0" MouseDoubleClick="WxTextBoxInt_MouseDoubleClick"/>


private void WxTextBox_MouseDoubleClick(object sender, System.Windows.Input.MouseButtonEventArgs e)
    WxTextBox txt = sender as WxTextBox;
    double value = DialogHelper.NumericalKeyboard(double.Parse(txt.Text));
    txt.Text = value.ToString("F2");
private void WxTextBoxInt_MouseDoubleClick(object sender, System.Windows.Input.MouseButtonEventArgs e)