WinForm IME輸入法BUG完美修復

本文來自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

  1. protected override void OnLoad(EventArgs e)      
  2. {   
  3.      KeyPreview = true;      
  4.      DrawTextboxes();      
  5.   
  6.    // 讓輸入法爲開啓半角狀態      
  7.      ImeMode = ImeMode.OnHalf;      
  8.   
  9.    base.OnLoad(e);      
  10. }  

完美修復函數

如今是經過對imm32.dll API調用,使之輸入法狀態爲開啓,這樣就保證了WinForm程序其它窗口的輸入法狀態也正確顯示。重寫第一個窗口的OnActivited事件便可。注意,不要再使用Control的ImeMode屬性了。測試下面代碼,點擊按鈕打開一個新的窗口,仍然能夠正確使用輸入法。OK,問題獲得完美解決~~!測試

 

  1. /* WinForm IME輸入法BUG完美修復
  2. * 編譯:csc.exe /target:winexe WinformImeBugFixed.cs
  3. */  
  4. using System;   
  5. using System.Windows.Forms;   
  6. using System.Drawing;   
  7. using System.Runtime.InteropServices;   
  8.   
  9. namespace WinformImeBugFixed   
  10. {   
  11.     public class Form1 : Form   
  12.      {
  13.          #region 解決輸入法BUG   
  14.         //解決輸入法BUG   
  15.          [DllImport("imm32.dll")]   
  16.         public static extern IntPtr ImmGetContext(IntPtr hwnd);   
  17.          [DllImport("imm32.dll")]   
  18.         public static extern bool ImmSetOpenStatus(IntPtr himc, bool b);   
  19.   
  20.         protected override void OnActivated(EventArgs e)   
  21.          {   
  22.             base.OnActivated(e);   
  23.              IntPtr HIme = ImmGetContext(this.Handle);   
  24.              ImmSetOpenStatus(HIme, true);   
  25.          }
  26.          #endregion
  27.          #region 不感興趣的   
  28.   
  29.         private void DrawTextboxes()   
  30.          {   
  31.              Controls.Clear();   
  32.             int x, y, d;   
  33.              x = y = d = 10;   
  34.             for (int i = 0; i < 2; i++)   
  35.              {   
  36.                  var textbox = new TextBox()   
  37.                  {   
  38.                      Width = 200,   
  39.                      Location = new Point(x, y)   
  40.                  };   
  41.                  y += textbox.Height + d;   
  42.                  textbox.DataBindings.Add("Text", textbox, "ImeMode");   
  43.                  Controls.Add(textbox);   
  44.              }   
  45.          }   
  46.   
  47.         private void DrawButton()   
  48.          {   
  49.              var button = new Button()   
  50.              {   
  51.                  Text = "Show Form2",   
  52.                  Location = new Point(10, 70)   
  53.              };   
  54.              button.Click += delegate  
  55.              {   
  56.                  var form2 = new Form();   
  57.                  form2.Text = "Form2";   
  58.                  var textbox = new TextBox()   
  59.                  {   
  60.                      Width = 200,   
  61.                      Location = new Point(10, 10)   
  62.                  };   
  63.                  form2.Controls.Add(textbox);   
  64.                  form2.Show();   
  65.              };   
  66.              Controls.Add(button);   
  67.          }   
  68.   
  69.         protected override void OnLoad(EventArgs e)   
  70.          {   
  71.              Text = "IME輸入法BUG修復 F5-刷新 F1-博客";   
  72.              KeyPreview = true;   
  73.              DrawTextboxes();   
  74.              DrawButton();   
  75.             base.OnLoad(e);   
  76.          }   
  77.   
  78.         public Form1()   
  79.          {   
  80.              InitializeComponent();   
  81.          }   
  82.   
  83.         protected override void OnKeyDown(KeyEventArgs e)   
  84.          {   
  85.             try { HandleKeyDown(e); }   
  86.             finally { base.OnKeyDown(e); }   
  87.          }   
  88.   
  89.         private void HandleKeyDown(KeyEventArgs e)   
  90.          {   
  91.             if (e.KeyCode == Keys.F5) DrawTextboxes();   
  92.             else if (e.KeyCode == Keys.F1) NavigateBlog();   
  93.          }   
  94.   
  95.         private void NavigateBlog()   
  96.          {   
  97.              System.Diagnostics.Process.Start("http://hi.baidu.com/wingingbob/blog/item/20741734532af846251f14f1.html");   
  98.          }
  99.          #endregion
  100.          #region Form1設計器   
  101.         private System.ComponentModel.IContainer components = null;   
  102.         protected override void Dispose(bool disposing)   
  103.          {   
  104.             if (disposing && (components != null))   
  105.              {   
  106.                  components.Dispose();   
  107.              }   
  108.             base.Dispose(disposing);   
  109.          }   
  110.   
  111.         private void InitializeComponent()   
  112.          {   
  113.             this.SuspendLayout();   
  114.             this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F);   
  115.             this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;   
  116.             this.ClientSize = new System.Drawing.Size(240, 100);   
  117.             this.Name = "Form1";   
  118.             this.Text = "Form1";   
  119.             this.ResumeLayout(false);   
  120.          }
  121.          #endregion
  122.          #region 入口點   
  123.         static class Program   
  124.          {   
  125.              [STAThread]   
  126.             static void Main()   
  127.              {   
  128.                  Application.EnableVisualStyles();   
  129.                  Application.SetCompatibleTextRenderingDefault(false);   
  130.                  Application.Run(new Form1());   
  131.              }   
  132.          }
  133.          #endregion   
  134.      }   
  135. }  

補充:採用OnActivited事件激活輸入法,那麼這個窗體的TopMost屬性應該爲false才能使輸入法設置有效。這個緣由多是因爲設置TopMost爲True時,將此句代碼寫入構造函數裏,而此時修復輸入法的代碼尚未被執行。也就是說,將TopMost=True的代碼寫在窗體的OnLoad事件中,就沒有問題了。因而,我把輸入法修復的代碼修改以下:this

  1. #region 解決輸入法BUG   
  2. [DllImport("imm32.dll")]   
  3. public static extern IntPtr ImmGetContext(IntPtr hwnd);   
  4. [DllImport("imm32.dll")]   
  5. public static extern bool ImmSetOpenStatus(IntPtr himc, bool b);   
  6.   
  7. delegate void fixImeDele();   
  8. protected override void OnLoad(EventArgs e)   
  9. {   
  10.      fixImeDele fixime = delegate  
  11.      {   
  12.          IntPtr HIme = ImmGetContext(this.Handle);   
  13.          ImmSetOpenStatus(HIme, true);   
  14.      };   
  15.     this.BeginInvoke(fixime);   
  16.     this.TopMost = true;   
  17.     base.OnLoad(e);   
  18. }
  19. #endregion  

注意這裏使用了一個小技巧,用包含BeginInvoke語句的OnLoad方法代替了原先的OnActivited方法,TopMost=True也寫在這個OnLoad重載裏,這樣,窗口置頂和輸入法開啓的代碼所有有效。BTW,在Onload中加入BeginInvoke的方式還能夠解決Control.Focus()方法在Load中失效的問題,原理和這個同樣,寫在委託裏調用就是了,範例參見:《WinForm IME輸入法BUG測試》spa

相關文章
相關標籤/搜索