WPF开发经验-实现自带触控键盘的TextBox
一 引入
项目有个新需求,当点击或触碰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实现了自带触控键盘的效果。
以上。
WPF开发经验-实现自带触控键盘的TextBox的更多相关文章
- WPF开发随笔收录-带递增递减按钮TextBox
一.前言 今天分享一下如何实现带递增递减按钮的TextBox控件 二.正文 1.之前的博客分享了一篇自定义XamlIcon控件的文章,这次就直接在那个项目的基础上实现今天这个自定义控件 2.首先添加两 ...
- C# WPF 解压缩7zip文件 带进度条 sevenzipsharp
vs2013附件 :http://download.csdn.net/detail/u012663700/7427461 C# WPF 解压缩7zip文件 带进度条 sevenzipsharp W ...
- 忘带U盘了??别急!一行python代码即可搞定文件传输
近日发现了python一个很有趣的功能,今天在这里给大伙儿做一下分享 需求前提 1.想要拷贝电脑的文件到另一台电脑但是又没有U盘2.手机上想获取到存储在电脑的文件3.忘带U盘- 您也太丢三落四了吧,但 ...
- wpf企业应用之带选项框的TreeView
wpf里面实现层次绑定主要使用HierarchicalDataTemplate,这里主要谈一谈带checkbox的treeview,具体效果见 wpf企业级开发中的几种常见业务场景. 先来看一下我的控 ...
- WPF开发经验-实现Win10虚拟触摸键盘
一 引入 项目有个需求,需要实现纯触控操作进行键盘输入.项目部署在Win10系统上,考虑有两种方案来实现. 通过调用Win10自带的触摸键盘来实现: 通过WPF实现一个触摸键盘来实现: 二 调用Win ...
- WPF开发经验
UpdateSourceTrigger 0.在一个项目中在用到绑定的时候一直有一个问题,虽然设置了Mode=TwoWay,界面的值修改了,但是后天绑定的值没有变化.最终发现了问题,在于UpdateSo ...
- 基于<MediaElement>的WPF视频播放器(带部分特效)【2】
一.前言 上回说到需要做放视频的使用向导,这两天公司里的老司机一直帮我答疑解惑,让这个任务变得挺顺的,真心感谢他们! 这次与[1]中的不同之处在于: (1)播放和暂停按钮集成在<Me ...
- 菜鸟学开店—自带U盘的打印机
本文旨在提供最简单.便宜.有效的解决方案,解决普通用户最困扰的问题.今天提供普通用户一个低价的小票打印机驱动安装解决方案 相信很多用户都碰到过这种情况,电脑的重装了系统,打印机的驱动没有备份,要用打印 ...
- WPF 重写微调自带的样式,ListView、DataGrid、TreeView等所有控件的默认样式
不知道各位在开发中有没有遇到这样的窘迫,开发一个UI,设计给出的效果图和自带的样式的区别很大,然后有的样式通过属性是修改不了的,比如TreeView的子项TreeViewItem,想完全透明背景色就做 ...
随机推荐
- Centos7较为彻底的删除mysql
Centos7下较为彻底的删除mysql(root 身份操作) 删除mysql安装包 1. yum检查 yum list installed | grep mysql 安装则直接删除 示例:yum r ...
- c:找到出现次数最多的递增数字串
如题,如何在一亿位整数组成的字符串中找到出现次数最多的递增数字串? 答案: #include <stdio.h> #include <string.h> #define MAX ...
- 基于NoCode构建简历编辑器
基于NoCode构建简历编辑器 基于NoCode构建简历编辑器,要参加秋招了,因为各种模版用起来细节上并不是很满意,所以尝试做个简单的拖拽简历编辑器. 描述 Github | Resume DEMO ...
- Python3的原生协程(Async/Await)和Tornado异步非阻塞
原文转载自「刘悦的技术博客」https://v3u.cn/a_id_113 我们知道在程序在执行 IO 密集型任务的时候,程序会因为等待 IO 而阻塞,而协程作为一种用户态的轻量级线程,可以帮我们解决 ...
- Gulp介绍及安装使用教程
一.简介 gulp是前端开发过程中对代码进行构建的工具,是自动化项目的构建利器,不仅能对网站资源进行优化,而且在开发过程中很多重复的任务能够使用正确的工具自动完成,使用她,我们不仅可以很愉快的编写代码 ...
- 一文理解Hadoop分布式存储和计算框架入门基础
@ 目录 概述 定义 发展历史 发行版本 优势 生态项目 架构 组成模块 HDFS架构 YARN架构 部署 部署规划 前置条件 部署步骤 下载文件(三台都执行) 创建目录(三台都执行) 配置环境变量( ...
- vscode 插件 Cnblogs Client For VSCode
目录 简介 主要功能 登录 / 授权 将本地 markdown 文件发布到博客园 博客园博文列表 将本地文件关联到博客园博文 拉取远程博文内容更新本地文件 图片上传 博文分类管理 导出 pdf 博文设 ...
- 什么?WPF 不支持 SVG ?
什么?WPF 不支持 SVG ? 控件名:SharpVectors 作者:Elinam LLC (Japan) 项目地址: https://github.com/ElinamLLC/SharpVect ...
- 微信小程序/校园社区论坛/微信云开发/云函数
一.框架来源 1.非常非常感谢B站up主"梦千的曾哥哥"的开源框架. 没有他就没有这个小程序.如果有想做的同学,可以直接移步他的视频那里开始学习.我水平不够,就不教具体怎么配置环境 ...
- 随机存取文件流--RandomAccessFile
* RandomAccessFile的使用* 1.RandomAccessFile直接维承于java.Lang.Object类,实现了DataInputDataOutput接口* 2.RandomAc ...