WPF实现自带触控键盘的文本框
作者:一团静火 发布时间:2023-08-25 15:23:20
标签: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实现了自带触控键盘的效果。
来源:https://www.cnblogs.com/wwwen/p/16747668.html


猜你喜欢
- 1、Json的制作package com.example.usingjson2; import org.json.
- ViewStub可以在运行时动态的添加布局。帮助文档给定的定义是:"A ViewStub is an invisible, zer
- 本部分需要用到微信的JS-SDK,微信JS-SDK是微信公众平台面向网页开发者提供的基于微信内的网页开发工具包。通过使用微信JS-SDK,网
- 前言本文是我之前写的这篇文章《Android图文混排-实现EditText图文混合插入上传》的升级版,除了在EditText实现了图片上传之
- 最近,Oracle 宣布 Java 14(或 Oracle JDK 14)公开可用。如果你想进行最新的实验或者开发的话,那么你可以试试在 L
- 本文实例为大家分享了Spring MVC多文件上传的具体代码,供大家参考,具体内容如下1)创建工程并导入JAR包2)创建多文件选择页面在 W
- Tab与TabHost:这就是Tab,而盛放Tab的容器就是TabHost 。如何实现?? 每一个Tab还对应了一个布局,这个就有点好玩了。
- 在早期开发的时候,我们完成的都是静态页面也就是html页面,随着时间轴的发展,慢慢的引入了jsp页面,当在后端服务查询到数据之后可以转发到j
- 我们在编写完Spring的代码后,往往需要测试代码的正确性,这个时候就需要用到单元测试了。我们这里使用的版本是junit4.一个程序的入口是
- 目录前言示例参考:前言按需加载对象延迟加载实际是推迟进行创建对象,直到对其调用后才进行创建初始化,延迟(懒加载)的好处是提高系统性能,避免不
- 一.数组的三种声明方式总结public class WhatEver { public static void main(Str
- XML假如有这样一个XML格式的数据:<?xml version="1.0" encoding="utf
- 前言二进制文件读写两个重要的函数 , fread 和 fwrite , fread 用于读取文件 , fwrite 用于写出文件 ;frea
- AnyChat(全名叫Anychat SDK),也叫音视频互动开发平台;是一套跨平台的即时通讯解决方案,基于先进的H.264视频编码标准、A
- 在文件夹中,我们经常有类似s_1.txt、s_2.txt、s_10.txt、s_11.txt这样的命名方式,我们期望的排序方式是s_1.tx
- 这篇山寨一个新版QQ的列表滑动删除,上篇有说到QQ的滑动删除,推测原理就是ListView本身每个item存在一个Button,只不过普通的
- 前言在上一篇,我们谈到了jvm垃圾回收算法详细解析,并了解了JVM中针对堆区中不同的分代采用不同的垃圾回收算法在了解了垃圾回收算法之后,很多
- 本文介绍了Android 删除所有build编译文件,翻译磁盘空间,分享给大家,也给自己留个笔记,具体如下: public static v
- 本文实例讲述了java实现列表、集合与数组之间转化的方法。分享给大家供大家参考。具体实现方法如下:package test; i
- 前言本文简单介绍抽象类,接口以及它们的异同点,另附简单的代码举例。一、抽象类是什么?在 Java 语言中使用 abstract class