本文來自http://hi.baidu.com/wingingbob/item/a2cb3fc0fe3bd1bb0d0a7b5bhtml
《WinForm IME輸入法BUG測試》裏,我描述了在.NET Framework 2.0的WinForm中,中文輸入法的BUG。這個BUG由來已久,據說在最新的VS2010中也沒有真正獲得解決。在文章後面,我懷疑是那些產生此類BUG的中文輸入法自己在設計上存在的缺陷,才致使了WinForm沒法正確識別。無論怎麼樣,問題出了就得想辦法解決,今天的方法是一個「簡單而有效的」解決辦法,就是在窗體加載時將輸入法預置爲ImeMode.OnHalf。最終的結果如上圖所示,王碼五筆能夠正確切換出來,並且以半角的方式顯示(ImeMode.OnHalf不一樣於ImeMode.On的地方),只是默認使用英文標點符號。可是你不用擔憂,主流的中文輸入法(如搜狗拼音)會默認採用中文標點的。代碼片斷以下:ide
- 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事件中,就沒有問題了。因而,我把輸入法修復的代碼修改以下:this
- #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測試》。spa