原文:通通WPF随笔(4)——通通手写输入法(基于Tablet pc实现)


从我在博客园写第一篇博客到现在已经有1年半了,我的第一篇博客写的就是手写识别,当时,客户需求在应用中加入手写输入功能,由于第三方的手写输入法都无法定制界面,所以领导决定自主开发,所以我就很简单地基于Tablet pc写了一个WPF控件,由于几个瓶颈问题,导致这个输入功能只能在我们的UI框架里使用,而无法做到像输入法那样可以输入到任意窗口。

  时隔1年半,随着各种项目的磨练,知识的积累几个问题得以解决,于是就产生了这个手写输入法。

ICO:

UI:

  

符号识别:

  

识别率还是比较高的

  

界面风格是1年半前设计的,当时觉得《创世纪》的视觉效果挺牛X的。

因为是针对于触摸设备设计的,所以没有做退出按钮,由应用来控制关闭。PC上的话,在空白处左键可以拖动,点击右键关闭窗口。

一、瓶颈问题


  由于本人C#出身,实在不想去研究什么IME等等底层的输入法机制,所以就写个exe实现输入法的功能。就面临下面几个问题:

  1、如何向其他窗口发送输入消息呢?

  2、如何知道当前键盘焦点所在窗口呢?

  3、如何在操作输入法窗体的时候不让目标窗口丢失焦点呢?

  这几个问题读起来就比较纠结......有句话说得好啊,“当做一件事时越来越难,就说明方向错了”。所以就绕开这几个问题呗。

二、解决问题


  C#里有个模拟键盘按键的方法 

模拟按键: 

System.Windows.Forms.SendKeys.SendWait("{DEL}");

而且可以发送字符:

System.Windows.Forms.SendKeys.SendWait("你好,美女~!");

  通过这个方法就模拟了键盘上的del键和发送字符串输入消息,而这个方法并没有需要窗口句柄,所以我猜想应该是模拟了底层的按键消息等等,而消息的去向肯定就是键盘焦点所在的窗口咯,这样就绕开了1、2两个问题。关于SendWait()参数的详细说明可以参看:http://msdn.microsoft.com/zh-cn/library/system.windows.forms.sendkeys.aspx

  接下来就需要解决的就只有3号问题了。

  一开始我很单纯的以为只要把窗口里面的所有控件都设置为不能获取焦点就行了

.Focusable = false;

  但这只是第一步,当一个窗口被激活时所有焦点都会跑到新激活的窗体上,所以,还得寻找其他方法。无意中在逛codeproject的时候发现了一段代码:  

[DllImport("user32.dll")]
public static extern int SetWindowLong(IntPtr hWnd, int nIndex, IntPtr dwNewLong); [DllImport("user32.dll", SetLastError = true)]
public static extern UInt32 GetWindowLong(IntPtr hWnd, int nIndex); // style of window?
int GWL_EXSTYLE = (-);
// get - retrieves information about a specified window
GetWindowLong(HWND, GWL_EXSTYLE);
// set - changes the attribute of a specified window - I think this stops it being focused on
SetWindowLong(HWND, GWL_EXSTYLE, (IntPtr)(0x8000000));

  Windows API的参数就是高端啊,完全看不懂是在干嘛,不过能达到目标就行。这样HWND窗口句柄对应的窗口就变得不会获取键盘焦点了。

  这样就解决了3个瓶颈问题。

三、手写识别


  已经实现了如何把字符串输出到其他窗口,剩下的就是识别手写墨迹了,核心在于InkAnalyzer这个类。通过WPF的InkCanvas控件来收集墨迹,然后通过theInkAnalyer.Analyze();方法就可以解析出来,十分方便。代码如下:(需要引用IACore.dll、IALoader.dll、IAWinFX.dll这3个动态库)

 InkAnalyzer theInkAnalyer;
AnalysisHintNode hint;
private void Grid_Loaded(object sender, RoutedEventArgs e)
{// style of window?
int GWL_EXSTYLE = (-);
// get - retrieves information about a specified window
GetWindowLong(HWND, GWL_EXSTYLE);
// set - changes the attribute of a specified window - I think this stops it being focused on
SetWindowLong(HWND, GWL_EXSTYLE, (IntPtr)(0x8000000)); theInkAnalyer = new InkAnalyzer();
hint = theInkAnalyer.CreateAnalysisHint();
hint.Guide.Columns = ;
hint.Guide.Rows = ;
hint.WordMode = true;
hint.TopInkBreaksOnly = true; } private void inkCanvs_MouseUp(object sender, MouseButtonEventArgs e)
{
hint.Location.MakeInfinite();
theInkAnalyer.RemoveStrokes(inkCanvs.Strokes);
theInkAnalyer.AddStrokes(inkCanvs.Strokes);
theInkAnalyer.SetStrokesLanguageId(inkCanvs.Strokes, 0x0804); theInkAnalyer.SetStrokesType(inkCanvs.Strokes, StrokeType.Writing);
AnalysisStatus status = theInkAnalyer.Analyze();
if (status.Successful)
{
for (int i = ; i < theInkAnalyer.GetAlternates().Count; i++)
{
Button thisButton = this.FindName("b" + i.ToString()) as Button;
string resultStr = theInkAnalyer.GetAlternates()[i].RecognizedString;
if (resultStr.Length==)
{
thisButton.Content = resultStr;
} } }
else
{
//MessageBox.Show("识别失败");
}
}

最终效果

  

  要在已安装了Tablet pc组件的Win7以上的系统才可以手写识别哦,win7完整版默认安装,但有的精简版把这个组件丢掉了,win8下完美运行。xp下的话得改一些系统文件,相关方法可以查看本人的第一篇博客 http://www.cnblogs.com/tong-tong/archive/2011/10/22/2220446.html

IACore等动态库下载:(包含XML注释文档)

http://files.cnblogs.com/tong-tong/IADLL.zip

demo下载:

http://files.cnblogs.com/tong-tong/TTHandwriting.zip

后记


  最近各种事情还要出钱啊、各种副省长还来参观啊、各种项目还没结束啊......

最新文章

  1. 在eclipse中部署jsp项目
  2. OD调试篇10
  3. C#拼接地图瓦片
  4. 域用户直接登陆(C#,MVC)
  5. HDU 4540 威威猫系列故事——打地鼠
  6. IOS开发——正则表达式验证手机号、密码
  7. MySQL内存表的特性与使用介绍 -- 简明现代魔法
  8. .NET到Node.js
  9. Android摄像头:只拍摄SurfaceView预览界面特定区域内容(矩形框)---完整(原理:底层SurfaceView+上层绘制ImageView)
  10. 利用easygui模块编写的华氏温度与摄氏温度转换的小程序
  11. problem:浏览器如何区分html超文本和普通文本
  12. Excel、VBA与MySQL交互
  13. cxf 例子
  14. 2017/2/5:oracle数据库的三大文件
  15. 1.2OpenCV如何扫描图像,利用查找表和计时
  16. 纯css竟可以做出边框这样长宽度的过渡效果
  17. 【读书笔记】iOS-网络-HTTP-URL百分号编码
  18. 精巧好用的DelayQueue
  19. 浅谈软件配置管理工具(github &amp; SVN)
  20. Kibana基本使用

热门文章

  1. 在 Java 中如何进行 BASE64 编码和解码
  2. docker nginx实现一个主机部署多个站点
  3. CSDN code使用教程之git使用方法具体解释
  4. 5.7-GTID复制搭建
  5. 【t094】区间运算
  6. 基于PCM2912a的USB声卡设计
  7. 分布式服务网关(Web Api)
  8. 高并发场景之RabbitMQ
  9. 利用WPF建立自己的3d gis软件(非axhost方式)(四)在地图上添加FrameworkElement
  10. Matlab Tricks(二十四)—— title 置于图像之下(包括 subplots 的情形)