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

简介: 原文:通通WPF随笔(4)——通通手写输入法(基于Tablet pc实现)         从我在博客园写第一篇博客到现在已经有1年半了,我的第一篇博客写的就是手写识别,当时,客户需求在应用中加入手写输入功能,由于第三方的手写输入法都无法定制界面,所以领导决定自主开发,所以我就很简单地基于Tablet pc写了一个WPF控件,由于几个瓶颈问题,导致这个输入功能只能在我们的UI框架里使用,而无法做到像输入法那样可以输入到任意窗口。
原文: 通通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 = (-20);
            // 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 = (-20);
            // 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 = 1;
            hint.Guide.Rows = 1;
            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 = 0; i < theInkAnalyer.GetAlternates().Count; i++)
                {
                    Button thisButton = this.FindName("b" + i.ToString()) as Button;
                    string resultStr = theInkAnalyer.GetAlternates()[i].RecognizedString;
                    if (resultStr.Length==1)
                    {
                        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

 

 

 

后记


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

 

 

 

目录
打赏
0
0
0
0
216
分享
相关文章
WPF钟表效果实现
中WPF中的RotateTransform实现UI元素的旋转,并模拟钟表的秒针、分针和时针。
1217 0
WPF钟表效果实现
WPF实现强大的动态公式计算
数据库可以定义表不同列之间的计算公式,进行自动公式计算,但如何实现行上的动态公式计算呢?行由于可以动态扩展,在某些应用场景下将能很好的解决实际问题。本文就探讨一下如何在WPF中实现一种基于行字段的动态公式计算。
1111 0
WPF实现强大的动态公式计算
C# WPF上位机实现和下位机TCP通讯
C# WPF上位机实现和下位机TCP通讯下位机使用北京大华程控电源DH1766-1,上位机使用WPF。实现了电压电流实时采集,曲线显示。上午在公司调试成功,手头没有程控电源,使用TCP服务端模拟。昨天写的TCP服务端正好排上用场。
2438 0
在WPF中实现图片一边下载一边显示
原文 在WPF中实现图片一边下载一边显示 当我们上网查看一个较大的图片时,浏览器能一边下载一边显示,这样用户体验是比较好的,但在WPF程序中,当我们通过如下方式显示一幅图片时:     img.Source = new BitmapImage(new Uri("http://localhost:8000/www/test.jpg")); 只能等到图片下载完成时才能显示出来,当图片较大时需要等待很久,即使在旁边放个进度条给人的感觉仍然不好。
1082 0
WPF实现三星手机充电界面
原文:WPF实现三星手机充电界面 GitHub地址:https://github.com/ptddqr/wpf-samsung-phone-s5-charging-ui/tree/master 先上效果图 这个效果来自于三星S5的充电界面,版权归三星所有,这里仅仅是技术实现.
1721 0
WPF实现物理效果 拉一个小球
原文:WPF实现物理效果 拉一个小球 一直以来都对物理效果有神秘感,完全不知道怎么实现的.直到看到了周银辉在老早前写的一篇博客:http://www.cnblogs.com/zhouyinhui/archive/2007/06/23/793724.
983 0
WPF实现Twitter按钮效果
原文:WPF实现Twitter按钮效果 最近上网看到这个CSS3实现的Twitter按钮,感觉很漂亮,于是想用WPF来实现下. 实现这个效果,参考了CSS3 原文地址:http://www.html5tricks.
1254 0
WPF 多语言实现
原文:WPF 多语言实现 很多国际化的程序都提供了多语言的选项,这样方便不同国家的使用者更方便的使用软件。这篇博客中将介绍在WPF中实现多语言的方式。 方式一,使用WPF动态资源的方式实现。先简单介绍下StaticResource和DynamicResource,这两者的区别在于动态资源改变后会实时的体现出来,而静态资源只加载一次,后面对资源的任何改变都不会体现出来。
1602 0
WPF 界面实现多语言支持 中英文切换 动态加载资源字典
原文:WPF 界面实现多语言支持 中英文切换 动态加载资源字典 1、使用资源字典,首先新建两个字典文件en-us.xaml、zh-cn.xaml。定义中英文的字符串在这里面【注意:添加xmlns:s="clr-namespace:System;assembly=mscorlib】 zh-cn.
3241 0
WPF中获取TreeView以及ListView获取其本身滚动条的方法,可实现自行调节scoll滚动的位置(可相应获取任何控件中的内部滚动条)
原文:WPF中获取TreeView以及ListView获取其本身滚动条的方法,可实现自行调节scoll滚动的位置(可相应获取任何控件中的内部滚动条) 对于TreeView而言: TreeViewAutomationPeer lvap = new TreeViewAuto...
2427 0