您还未登录! 登录 | 注册 | 帮助  

您的位置: 首页 > 软件测试管理 > 缺陷管理 > 正文

WinForm IME输入法BUG完美修复

发表于:2017-01-09 作者:网络转载 来源:

  《WinForm IME输入法BUG测试》里,我描述了在.NET Framework 2.0的WinForm中,中文输入法的BUG。这个BUG由来已久,听说在最新的VS2010中也没有真正得到解决。在文章后面,我怀疑是那些产生此类BUG的中文输入法本身在设计上存在的缺陷,才导致了WinForm无法正确识别。不管怎么样,问题出了就得想办法解决,今天的方法是一个“简单而有效的”解决办法,就是在窗体加载时将输入法预置为ImeMode.OnHalf。最终的结果如上图所示,王码五笔可以正确切换出来,而且以半角的方式显示(ImeMode.OnHalf不同于ImeMode.On的地方),只是默认使用英文标点符号。但是你不用担心,主流的中文输入法(如搜狗拼音)会默认采用中文标点的。代码片段如下:
  protected override void OnLoad(EventArgs e)
  {
  KeyPreview = true;
  DrawTextboxes();
  // 让输入法为开启半角状态
  ImeMode = ImeMode.OnHalf;
  base.OnLoad(e);
  }
  完美修复
  现在是通过对imm32.dll API调用,使之输入法状态为开启,这样就保证了WinForm程序其它窗口的输入法状态也正确显示。重写第一个窗口的OnActivited事件即可。注意,不要再使用Control的ImeMode属性了。测试下面代码,点击按钮打开一个新的窗口,仍然可以正确使用输入法。OK,问题得到完美解决~~!
/* WinForm IME输入法BUG完美修复
* 编译:csc.exe /target:winexe WinformImeBugFixed.cs
*/
using System;
using System.Windows.Forms;
using System.Drawing;
using System.Runtime.InteropServices;
namespace WinformImeBugFixed
{
public class Form1 : Form
{
#region 解决输入法BUG
//解决输入法BUG
[DllImport("imm32.dll")]
public static extern IntPtr ImmGetContext(IntPtr hwnd);
[DllImport("imm32.dll")]
public static extern bool ImmSetOpenStatus(IntPtr himc, bool b);
protected override void OnActivated(EventArgs e)
{
base.OnActivated(e);
IntPtr HIme = ImmGetContext(this.Handle);
ImmSetOpenStatus(HIme, true);
}
#endregion
#region 不感兴趣的
private void DrawTextboxes()
{
Controls.Clear();
int x, y, d;
x = y = d = 10;
for (int i = 0; i < 2; i++)
{
var textbox = new TextBox()
{
Width = 200,
Location = new Point(x, y)
};
y += textbox.Height + d;
textbox.DataBindings.Add("Text", textbox, "ImeMode");
Controls.Add(textbox);
}
}
private void DrawButton()
{
var button = new Button()
{
Text = "Show Form2",
Location = new Point(10, 70)
};
button.Click += delegate
{
var form2 = new Form();
form2.Text = "Form2";
var textbox = new TextBox()
{
Width = 200,
Location = new Point(10, 10)
};
form2.Controls.Add(textbox);
form2.Show();
};
Controls.Add(button);
}
protected override void OnLoad(EventArgs e)
{
Text = "IME输入法BUG修复 F5-刷新 F1-博客";
KeyPreview = true;
DrawTextboxes();
DrawButton();
base.OnLoad(e);
}
public Form1()
{
InitializeComponent();
}
protected override void OnKeyDown(KeyEventArgs e)
{
try { HandleKeyDown(e); }
finally { base.OnKeyDown(e); }
}
private void HandleKeyDown(KeyEventArgs e)
{
if (e.KeyCode == Keys.F5) DrawTextboxes();
else if (e.KeyCode == Keys.F1) NavigateBlog();
}
private void NavigateBlog()
{
System.Diagnostics.Process.Start("http://hi.baidu.com/wingingbob/blog/item/20741734532af846251f14f1.html");
}
#endregion
#region Form1设计器
private System.ComponentModel.IContainer components = null;
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
private void InitializeComponent()
{
this.SuspendLayout();
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(240, 100);
this.Name = "Form1";
this.Text = "Form1";
this.ResumeLayout(false);
}
#endregion
#region 入口点
static class Program
{
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
}
#endregion
}
}

  补充:采用OnActivited事件激活输入法,那么这个窗体的TopMost属性应该为false才能使输入法设置有效。这个原因可能是由于设置TopMost为True时,将此句代码写入构造函数里,而此时修复输入法的代码还没有被执行。也就是说,将TopMost=True的代码写在窗体的OnLoad事件中,就没有问题了。于是,我把输入法修复的代码修改如下:
#region 解决输入法BUG
[DllImport("imm32.dll")]
public static extern IntPtr ImmGetContext(IntPtr hwnd);
[DllImport("imm32.dll")]
public static extern bool ImmSetOpenStatus(IntPtr himc, bool b);
delegate void fixImeDele();
protected override void OnLoad(EventArgs e)
{
fixImeDele fixime = delegate
{
IntPtr HIme = ImmGetContext(this.Handle);
ImmSetOpenStatus(HIme, true);
};
this.BeginInvoke(fixime);
this.TopMost = true;
base.OnLoad(e);
}
#endregion
  注意这里使用了一个小技巧,用包含BeginInvoke语句的OnLoad方法代替了原先的OnActivited方法,TopMost=True也写在这个OnLoad重载里,这样,窗口置顶和输入法开启的代码全部有效。BTW,在Onload中加入BeginInvoke的方式还可以解决Control.Focus()方法在Load中失效的问题,原理和这个一样,写在委托里调用就是了,范例参见:《WinForm IME输入法BUG测试》。