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

 

一 引入

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

 

二 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实现了自带触控键盘的效果。

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对编程宝库的支持。如果你想了解更多相关内容请查看下面相关链接

 前言随着数据的不断扩大,有的数据表的规模会以几何级增长,当数据达到一定规模时,数据的查询,读取性能就会变得缓慢,这时就需要拆分数据表,接下来在文章中将为大家详 ...