WPF实现自带触控键盘的文本框

作者:一团静火 时间:2023-08-25 15:23:20 

一 引入

项目有个新需求,当点击或触碰TextBox时,基于TextBox的相对位置,弹出一个自定义的Keyboard,如下图所示:

WPF实现自带触控键盘的文本框

二 KeyboardControl

先实现一个自定义的KeyboardControl,它继承自Window。

Xaml代码如下:

<Window x:Class="WpfApp1.KeyboardControl"
       xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
       xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
       xmlns:local="clr-namespace:WpfApp1" AllowsTransparency="True" WindowStyle="None"
       ResizeMode="NoResize" Background="Transparent" Height="290" Width="668">
   <FrameworkElement.Resources>
       <ResourceDictionary>
           <Style TargetType="{x:Type Button}" x:Key="btnNum">
               <Setter Property="Width" Value="50"/>
               <Setter Property="Height" Value="50"/>
               <Setter Property="Margin" Value="0 0 5 5"/>
               <Setter Property="HorizontalContentAlignment" Value="Center" />
               <Setter Property="VerticalContentAlignment" Value="Center" />
               <Setter Property="Cursor" Value="Hand"/>
               <Setter Property="Template">
                   <Setter.Value>
                       <ControlTemplate TargetType="{x:Type Button}">
                           <Border Name="border" BorderBrush="#FF474747" BorderThickness="1" CornerRadius="6">
                               <Border.Background>
                                   <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
                                       <GradientStop Color="#FFCCCCCC" />
                                       <GradientStop Color="WhiteSmoke" Offset="0.5" />
                                       <GradientStop Color="#FFCCCCCC" Offset="1" />
                                   </LinearGradientBrush>
                               </Border.Background>
                               <ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"
                                   TextElement.Foreground="#333333" TextElement.FontSize="18" />
                           </Border>
                       </ControlTemplate>
                   </Setter.Value>
               </Setter>
           </Style>
           <Style TargetType="{x:Type Button}" x:Key="btnFunc">
               <Setter Property="HorizontalContentAlignment" Value="Center" />
               <Setter Property="VerticalContentAlignment" Value="Center" />
               <Setter Property="Width" Value="50"/>
               <Setter Property="Height" Value="50"/>
               <Setter Property="Margin" Value="0 0 5 5"/>
               <Setter Property="Foreground" Value="#333333"/>
               <Setter Property="Cursor" Value="Hand"/>
               <Setter Property="Template">
                   <Setter.Value>
                       <ControlTemplate TargetType="{x:Type Button}">
                           <Border
                               Name="border"
                               BorderBrush="#FF565656"
                               BorderThickness="1"
                               CornerRadius="6"  Background="Orange">
                               <ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"
                                   TextElement.Foreground="White" TextElement.FontSize="18" />
                           </Border>
                       </ControlTemplate>
                   </Setter.Value>
               </Setter>
           </Style>
           <local:CapsConverter x:Key="CapsConverter"/>
       </ResourceDictionary>
   </FrameworkElement.Resources>

<Border Background="Gray" CornerRadius="6" BorderThickness="1" BorderBrush="#333333">
       <StackPanel Margin="5 10 5 5" >
           <Grid>
               <TextBox Name="tbValue" FontSize="28" Height="40"
                   Background="Transparent" BorderBrush="Silver" BorderThickness="1"
                   Foreground="White" HorizontalContentAlignment="Right"
                   SelectionChanged="tbValue_TextChanged"
                   TextChanged="tbValue_TextChanged" />
           </Grid>
           <WrapPanel  Orientation="Vertical" >
               <WrapPanel Margin="0 10 0 0">
                   <Button Content="1" Click="Button_Click"  Style="{StaticResource btnNum}" />
                   <Button Content="2" Click="Button_Click"  Style="{StaticResource btnNum}" />
                   <Button Content="3" Click="Button_Click"  Style="{StaticResource btnNum}" />

<Button Content="4" Click="Button_Click"  Style="{StaticResource btnNum}" />
                   <Button Content="5" Click="Button_Click"  Style="{StaticResource btnNum}" />
                   <Button Content="6" Click="Button_Click"  Style="{StaticResource btnNum}" />

<Button Content="7" Click="Button_Click"  Style="{StaticResource btnNum}" />
                   <Button Content="8" Click="Button_Click"  Style="{StaticResource btnNum}" />
                   <Button Content="9" Click="Button_Click"  Style="{StaticResource btnNum}" />

<Button Content="0" Click="Button_Click"  Style="{StaticResource btnNum}" />
                   <Button Content="-" Click="Button_Click"  Style="{StaticResource btnNum}" />
                   <Button Content="Del" Click="DELButton_Click"   Style="{StaticResource btnFunc}"  Margin="0 0 0 5"/>
               </WrapPanel>
               <WrapPanel Margin="25 0 0 0">
                   <Button Style="{StaticResource btnNum}" Content="{Binding Path=Caps,RelativeSource={RelativeSource AncestorType={x:Type local:KeyboardControl}},Converter={StaticResource CapsConverter},ConverterParameter=q}"
                           Click="Button_Click"/>
                   <Button Style="{StaticResource btnNum}" Content="{Binding Path=Caps,RelativeSource={RelativeSource AncestorType={x:Type local:KeyboardControl}},Converter={StaticResource CapsConverter},ConverterParameter=w}"
                           Click="Button_Click"/>
                   <Button Style="{StaticResource btnNum}" Content="{Binding Path=Caps,RelativeSource={RelativeSource AncestorType={x:Type local:KeyboardControl}},Converter={StaticResource CapsConverter},ConverterParameter=e}"
                           Click="Button_Click"/>
                   <Button Style="{StaticResource btnNum}" Content="{Binding Path=Caps,RelativeSource={RelativeSource AncestorType={x:Type local:KeyboardControl}},Converter={StaticResource CapsConverter},ConverterParameter=r}"
                            Click="Button_Click"/>
                   <Button Style="{StaticResource btnNum}" Content="{Binding Path=Caps,RelativeSource={RelativeSource AncestorType={x:Type local:KeyboardControl}},Converter={StaticResource CapsConverter},ConverterParameter=t}"
                            Click="Button_Click"/>
                   <Button Style="{StaticResource btnNum}" Content="{Binding Path=Caps,RelativeSource={RelativeSource AncestorType={x:Type local:KeyboardControl}},Converter={StaticResource CapsConverter},ConverterParameter=y}"
                            Click="Button_Click"/>
                   <Button Style="{StaticResource btnNum}" Content="{Binding Path=Caps,RelativeSource={RelativeSource AncestorType={x:Type local:KeyboardControl}},Converter={StaticResource CapsConverter},ConverterParameter=u}"
                            Click="Button_Click"/>
                   <Button Style="{StaticResource btnNum}" Content="{Binding Path=Caps,RelativeSource={RelativeSource AncestorType={x:Type local:KeyboardControl}},Converter={StaticResource CapsConverter},ConverterParameter=i}"
                            Click="Button_Click"/>
                   <Button Style="{StaticResource btnNum}" Content="{Binding Path=Caps,RelativeSource={RelativeSource AncestorType={x:Type local:KeyboardControl}},Converter={StaticResource CapsConverter},ConverterParameter=o}"
                            Click="Button_Click"/>
                   <Button Style="{StaticResource btnNum}" Content="{Binding Path=Caps,RelativeSource={RelativeSource AncestorType={x:Type local:KeyboardControl}},Converter={StaticResource CapsConverter},ConverterParameter=p}"
                            Click="Button_Click"/>
                   <Button Content="Clear" Click="ClearButton_Click"  Style="{StaticResource btnFunc}"  />
               </WrapPanel>
               <WrapPanel Margin="45 0 0 0">
                   <Button Style="{StaticResource btnNum}" Content="{Binding Path=Caps,RelativeSource={RelativeSource AncestorType={x:Type local:KeyboardControl}},Converter={StaticResource CapsConverter},ConverterParameter=a}"
                            Click="Button_Click"/>
                   <Button Style="{StaticResource btnNum}" Content="{Binding Path=Caps,RelativeSource={RelativeSource AncestorType={x:Type local:KeyboardControl}},Converter={StaticResource CapsConverter},ConverterParameter=s}"
                            Click="Button_Click"/>
                   <Button Style="{StaticResource btnNum}" Content="{Binding Path=Caps,RelativeSource={RelativeSource AncestorType={x:Type local:KeyboardControl}},Converter={StaticResource CapsConverter},ConverterParameter=d}"
                            Click="Button_Click"/>
                   <Button Style="{StaticResource btnNum}" Content="{Binding Path=Caps,RelativeSource={RelativeSource AncestorType={x:Type local:KeyboardControl}},Converter={StaticResource CapsConverter},ConverterParameter=f}"
                            Click="Button_Click"/>
                   <Button Style="{StaticResource btnNum}" Content="{Binding Path=Caps,RelativeSource={RelativeSource AncestorType={x:Type local:KeyboardControl}},Converter={StaticResource CapsConverter},ConverterParameter=g}"
                            Click="Button_Click"/>
                   <Button Style="{StaticResource btnNum}" Content="{Binding Path=Caps,RelativeSource={RelativeSource AncestorType={x:Type local:KeyboardControl}},Converter={StaticResource CapsConverter},ConverterParameter=h}"
                            Click="Button_Click"/>
                   <Button Style="{StaticResource btnNum}" Content="{Binding Path=Caps,RelativeSource={RelativeSource AncestorType={x:Type local:KeyboardControl}},Converter={StaticResource CapsConverter},ConverterParameter=j}"
                            Click="Button_Click"/>
                   <Button Style="{StaticResource btnNum}" Content="{Binding Path=Caps,RelativeSource={RelativeSource AncestorType={x:Type local:KeyboardControl}},Converter={StaticResource CapsConverter},ConverterParameter=k}"
                            Click="Button_Click"/>
                   <Button Style="{StaticResource btnNum}" Content="{Binding Path=Caps,RelativeSource={RelativeSource AncestorType={x:Type local:KeyboardControl}},Converter={StaticResource CapsConverter},ConverterParameter=l}"
                            Click="Button_Click"/>
                   <Button Content="." Click="Button_Click"  Style="{StaticResource btnNum}" />
               </WrapPanel>
               <WrapPanel Margin="70 0 0 0">
                   <Button Content="A/a" Click="CapsButton_Click"  Style="{StaticResource btnFunc}"  />
                   <Button Style="{StaticResource btnNum}" Content="{Binding Path=Caps,RelativeSource={RelativeSource AncestorType={x:Type local:KeyboardControl}},Converter={StaticResource CapsConverter},ConverterParameter=z}"
                            Click="Button_Click"/>
                   <Button Style="{StaticResource btnNum}" Content="{Binding Path=Caps,RelativeSource={RelativeSource AncestorType={x:Type local:KeyboardControl}},Converter={StaticResource CapsConverter},ConverterParameter=x}"
                            Click="Button_Click"/>
                   <Button Style="{StaticResource btnNum}" Content="{Binding Path=Caps,RelativeSource={RelativeSource AncestorType={x:Type local:KeyboardControl}},Converter={StaticResource CapsConverter},ConverterParameter=c}"
                            Click="Button_Click"/>
                   <Button Style="{StaticResource btnNum}" Content="{Binding Path=Caps,RelativeSource={RelativeSource AncestorType={x:Type local:KeyboardControl}},Converter={StaticResource CapsConverter},ConverterParameter=v}"
                            Click="Button_Click"/>
                   <Button Style="{StaticResource btnNum}" Content="{Binding Path=Caps,RelativeSource={RelativeSource AncestorType={x:Type local:KeyboardControl}},Converter={StaticResource CapsConverter},ConverterParameter=b}"
                            Click="Button_Click"/>
                   <Button Style="{StaticResource btnNum}" Content="{Binding Path=Caps,RelativeSource={RelativeSource AncestorType={x:Type local:KeyboardControl}},Converter={StaticResource CapsConverter},ConverterParameter=n}"
                            Click="Button_Click"/>
                   <Button Style="{StaticResource btnNum}" Content="{Binding Path=Caps,RelativeSource={RelativeSource AncestorType={x:Type local:KeyboardControl}},Converter={StaticResource CapsConverter},ConverterParameter=m}"
                            Click="Button_Click"/>
                   <Button Content="Cancel" Click="CancelButton_Click" IsCancel="True" Style="{StaticResource btnFunc}" Width="70"  />
                   <Button Content="OK" Click="OKButton_Click" IsDefault="True" Style="{StaticResource btnFunc}" Width="70"  Margin="0 0 0 5"/>
               </WrapPanel>
           </WrapPanel>
       </StackPanel>
   </Border>
</Window>

后台代码如下:

public partial class KeyboardControl : Window
{
   private int TextIndex { get; set; }
   public string TextStr { get; private set; }//通过该属性,访问Keyboard的文本

public KeyboardControl(string inputStr)//构造方式传入初始文本
   {
       InitializeComponent();

TextStr = inputStr;
       tbValue.Text = inputStr;
       tbValue.Focus();
       tbValue.CaretIndex = inputStr.Length;
   }

public static readonly DependencyProperty CapsProperty = DependencyProperty.Register(
      "Caps", typeof(bool), typeof(KeyboardControl), new PropertyMetadata(default(bool)));
   public bool Caps
   {
       get { return (bool)GetValue(CapsProperty); }
       set { SetValue(CapsProperty, value); }
   }

private void Button_Click(object sender, RoutedEventArgs e)
   {
       Button button = (Button)sender;
       if (TextIndex == 0)
       {
           tbValue.Text += (string)button.Content;
       }
       else
       {
           tbValue.Text = tbValue.Text.Insert(TextIndex, (string)button.Content);
       }
   }

private void tbValue_TextChanged(object sender, RoutedEventArgs e)
   {
       TextBox textBox = (TextBox)sender;
       TextIndex = textBox.CaretIndex;
   }

private void ClearButton_Click(object sender, RoutedEventArgs e)
   {
       tbValue.Text = "";
   }

private void DELButton_Click(object sender, RoutedEventArgs e)
   {
       if (tbValue.Text.Length > 0)
       {
           if (TextIndex == 0 && tbValue.Text.Length >= 1)
           {
               tbValue.Text = tbValue.Text.Remove(tbValue.Text.Length - 1, 1);
           }
           else if (TextIndex > 0)
           {
               tbValue.Text = tbValue.Text.Remove(TextIndex - 1, 1);
           }
       }
   }

private void OKButton_Click(object sender, RoutedEventArgs e)
   {
       TextStr = tbValue.Text;
       DialogResult = true;
       Close();
   }

private void CancelButton_Click(object sender, RoutedEventArgs e)
   {
       DialogResult = false;
       Close();
   }

private void CapsButton_Click(object sender, RoutedEventArgs e)
   {
       Caps = !Caps;
   }
}

Xaml代码中用到了一个大小写的转换类:

public class CapsConverter : IValueConverter
{
   public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
   {
       if (parameter == null)
       {
           return "";
       }

if (value == null)
       {
           return parameter.ToString();
       }

if (value is bool b)
       {
           return b ? parameter.ToString().ToUpper() : parameter.ToString().ToLower();
       }
       else
       {
           return parameter.ToString();
       }
   }

public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
   {
       throw new NotImplementedException();
   }
}

三 TouchTextBox

定义一个TouchTextBox的分部类。

public partial class TouchTextBox
{
   private Control hostControl;

//OnClick方法调用时,通过Window.ShowDialog方法,打开KeyboardControl
   public void OnClick(object sender, MouseButtonEventArgs e)
   {
       if (sender is TextBox textBox)
       {
           hostControl = textBox;
           //计算KeyboardControl的位置,弹出KeyboardControl
           var text = Show(textBox.Text, textBox);
           //KeyboardControl关闭后,获取其文本值,赋值给TextBox
           if (!string.IsNullOrEmpty(text))
           {
               textBox.Text = text;
           }
           else
           {
               textBox.Text = string.Empty;
           }
       }
   }

private string Show(string initValue, object sender = null)
   {
       var keyboard = new KeyboardControl(initValue);

SetPosition(keyboard);

bool result = keyboard.ShowDialog().Value;
       if (result)
       {
           return keyboard.TextStr;
       }
       else
       {
           return string.Empty;
       }
   }

private void SetPosition(Window window)
   {
       Point point = hostControl.PointFromScreen(new Point(0.0, 0.0));
       double width = SystemParameters.WorkArea.Width;
       double height = SystemParameters.WorkArea.Height;
       if (-point.Y + hostControl.ActualHeight + 5.0 + window.Height < height)
       {
           window.Top = -point.Y + hostControl.ActualHeight + 5.0;
       }
       else
       {
           window.Top = -point.Y - window.Height - 5.0;
       }
       if (-point.X + window.Width < width)
       {
           window.Left = -point.X;
       }
       else
       {
           window.Left = -point.X - (window.Width - hostControl.ActualWidth);
       }
   }
}

添加一个名为TouchTextBox的资源字典。

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                   xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                   x:Class="WpfApp1.TouchTextBox">
   <Style x:Key="TouchTextBox" TargetType="{x:Type TextBox}">
       <EventSetter Event="PreviewMouseLeftButtonDown" Handler="OnClick" />
   </Style>
</ResourceDictionary>

四 效果展示

在App.Xaml中引入TouchTextBox.Xaml资源。

<Application x:Class="WpfApp1.App"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:local="clr-namespace:WpfApp1"
            StartupUri="MainWindow.xaml">
   <Application.Resources>
       <ResourceDictionary>
           <ResourceDictionary.MergedDictionaries>
               <ResourceDictionary Source="/WpfApp1;component/TouchTextBox.xaml" />
           </ResourceDictionary.MergedDictionaries>
       </ResourceDictionary>
   </Application.Resources>
</Application>

MainWindow界面代码:

<Window x:Class="WpfApp1.MainWindow"
       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"
       mc:Ignorable="d"
       Title="MainWindow" Height="800" Width="1200">
   <StackPanel>
       <TextBox Text="Pop up the keyboard after touching" Width="400" HorizontalAlignment="Left"
FontSize="18" Margin="20,20"
                Style="{StaticResource TouchTextBox}"/>
   </StackPanel>
</Window>

设置TextBox的Style为TouchTextBox,则该TextBox实现了自带触控键盘的效果。

来源:https://www.cnblogs.com/wwwen/p/16747668.html

标签:WPF,自带,触控,键盘,文本框
0
投稿

猜你喜欢

  • C语言在输入输出时遇到的常见问题总结

    2021-11-08 15:22:37
  • Kotlin 使用Lambda来设置回调的操作

    2021-07-22 03:08:04
  • C# Socket连接请求超时机制实现代码分享

    2021-10-26 06:52:05
  • springmvc 防止表单重复提交的两种方法

    2023-03-27 17:57:18
  • Android自定义View实现竖直跑马灯效果案例解析

    2021-06-25 04:04:30
  • C# 重写ComboBox实现下拉任意组件的方法

    2022-01-24 03:07:29
  • Android中退出确认框的实现代码

    2022-02-23 06:40:31
  • java 抽象类的实例详解

    2023-06-08 05:52:40
  • C#实现骑士飞行棋

    2022-01-13 18:50:29
  • java异常与错误处理基本知识

    2023-11-25 10:44:59
  • SpringBoot异步调用方法并接收返回值

    2023-08-16 22:33:49
  • Qt TCP实现简单通信功能

    2021-07-07 02:16:30
  • Java中的stream流的概念解析及实际运用总结

    2022-06-10 23:19:10
  • Java基础之Spring5的核心之一IOC容器

    2022-06-04 07:44:14
  • JDK常用命令jps jinfo jstat的具体说明与示例

    2021-08-09 16:03:30
  • C#图片按比例缩放的实现代码

    2022-12-19 03:58:16
  • Java控制台实现猜拳游戏小游戏

    2022-04-20 09:52:55
  • Android自定义分段式进度条

    2023-09-10 20:47:01
  • Spring Cloud详解实现声明式微服务调用OpenFeign方法

    2021-07-04 07:56:41
  • Springboot集成JUnit5优雅进行单元测试的示例

    2021-09-24 07:49:58
  • asp之家 软件编程 m.aspxhome.com