开贴说说文本编辑器的那些事情-捕获输入内容
输入内容,随便Google一下的话,大概会出现以下解决方法:
KeyPress事件的 KeyPressEventArgs 参数 的 e.KeyChar 就是输入的内容(包括直接输入和使用IME输入。)
KeyPress这个事件没有什么问题,的确程序是需要这个事件来捕获输入内容的,但是还是有2个注意点,请大家牢记。
第一:输入法输入 和 .Net版本问题
如果是通过输入法来输入文字的话(中文,日本语等输入法),.Net1.x 和.Net2.0以上有一个明显的区别:
Net1.x只触发一次KeyPress事件,Net2.0以上将触发两次KeyPress事件!!
Net2.0以上IME输入时两次触发KeyPress事件原因:
KeyPress事件的源头是WM_CHAR消息和WM_IME_CHAR消息。当非IME输入的时候,只引发了WM_CHAR消息, KeyPress只触发一次。但是,当内容文字是通过IME输入的时候,在引发WM_CHAR消息的同时,还将引发WM_IME_CHAR消息。这样的 话,通过IME输入文字的时候,我们将两次得到KeyPress事件。不知道Net1.x的KeyPress为什么没有这个问题,可能Net1.x的时 候,KeyPress只对WM_CHAR作出反应吧。
知道了原因的话,那对策也很容易就找到了。在WndProc里面进行预先判断。如果是由WM_IME_CHAR消息引发的KeyPress,则在 KeyPress事件里什么都不做,不然的话,在KeyPress里绘制字符。(所有的输入,均有WM_CHAR消息)
第二个问题:是不是可以绘制的字符
KeyPress事件的 KeyPressEventArgs 参数 的 e.KeyChar ,这里并没有保证所有的字符均是可以绘制的字符,如果你按下一些特殊键和组合键的时候,也会产生e.KeyChar。由于可能涉及到不同的字符集,还不能 用简单的方法过滤掉他们呢。
我的做法是在PreviewKeyDown事件里面做筛选,PreviewKeyDown事件在 KeyPress事件之前发生,我是在KeyPress里面做绘制字符的操作的,如果PreviewKeyDown事件里面判断出按下的键是一些功能键和 控制键,怎在KeyPress里面什么都不做。至于为什么在PreviewKeyDown里做判断,而不是在KeyDown事件里呢,原因是 PreviewKeyDown能捕获一些KeyDown事件不能捕获的KeyCode!
综上所述,在KeyPress事件里面是否绘制字符串,有两个前提条件,第一,通过WndProc截获的消息,判断是不是由于WM_IME_CHAR造成 的两次触发;第二,通过PreviewKeyDown事件来判断是不是控制系的键或操作系的键引发的KeyPress事件。如果是WM_IME_CHAR 触发的KeyPress或控制系的键或操作系的键引发的KeyPress,则不做绘图操作。
<以下代码只是节选,并非完整代码!!>
'IsDrawString如果为假的话,不将字符绘制到窗体
Private IsDrawString As Boolean
'这个内容是否从IME传递过来
Private CharFormIME As Boolean
'*************************************************************************
'* VS2003中,从输入法输入的字符只表示一次,VS2003以后,表示两次
'* 原因据说和两个消息有关,一个是0x286,一个是0x102
'*************************************************************************
Private Sub TextBoxEx_KeyPress(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyPressEventArgs) Handles Me.KeyPress
'如果不是操作系的按键,不是从IME发出的重复信息,则绘制文字
If IsDrawString And CharFormIME = False Then
。。。。。。。。。。。。。。
DrawLine(mDocument.InputPosition.Y)
。。。。。。。。。。。。。。。
End If
End Sub
''' <summary>
''' 捕获系统消息
''' </summary>
''' <param name="m">系统消息</param>
''' <remarks></remarks>
Protected Overrides Sub WndProc(ByRef m As Message)
Select Case m.Msg
'所有需要画的字符都来自语WM_CHAR,中文,日语的话,还回发生WM_IME_CHAR,造成KeyPress里2次同样的事件
'基于这个原因,我们吧WM_IME_CHAR产生的KeyPress过滤掉
Case WM_IME_CHAR
CharFormIME = True
Case WM_CHAR
CharFormIME = False
End Select
MyBase.WndProc(m)
End Sub
****************************************************
关于箭头等无法在KeyDown里面捕获的键只能在PreViewKeyDown里截获了
****************************************************
Private Sub TextBoxEx_PreviewKeyDown(ByVal sender As Object, ByVal e As System.Windows.Forms.PreviewKeyDownEventArgs) Handles Me.PreviewKeyDown
'先假设不能绘制,如果没有被过滤掉的话,则能绘制
IsDrawString = False
'如果Shift被按下,则现在的位置做为选中内容开始位置
Select Case e.KeyCode
Case Keys.ShiftKey
。。。。。。。。
Case Keys.Enter
Case Keys.Back
。。。。。。。。 Case Else
IsDrawString = True
End Select
End Sub