WPF开发经验-实现自带触控键盘的TextBox

一 引入

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

WPF开发经验-实现自带触控键盘的TextBox

 

二 KeyboardControl

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

Xaml代码如下:

WPF开发经验-实现自带触控键盘的TextBox

<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>

View Code

 后台代码如下:

WPF开发经验-实现自带触控键盘的TextBox

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;     } }

View Code

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

WPF开发经验-实现自带触控键盘的TextBox

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();     } }

View Code

 

三 TouchTextBox

定义一个TouchTextBox的分部类。

WPF开发经验-实现自带触控键盘的TextBox

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);         }     } }

View Code

添加一个名为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实现了自带触控键盘的效果。

 

以上。

发表评论

相关文章

当前内容话题