zoukankan      html  css  js  c++  java
  • WinForm IME输入法BUG完美修复

    本文来自http://hi.baidu.com/wingingbob/item/a2cb3fc0fe3bd1bb0d0a7b5b

    《WinForm IME输入法BUG测试》里,我描述了在.NET Framework 2.0的WinForm中,中文输入法的BUG。这个BUG由来已久,听说在最新的VS2010中也没有真正得到解决。在文章后面,我怀疑是那些产生此类BUG的中文输入法本身在设计上存在的缺陷,才导致了WinForm无法正确识别。不管怎么样,问题出了就得想办法解决,今天的方法是一个“简单而有效的”解决办法,就是在窗体加载时将输入法预置为ImeMode.OnHalf。最终的结果如上图所示,王码五笔可以正确切换出来,而且以半角的方式显示(ImeMode.OnHalf不同于ImeMode.On的地方),只是默认使用英文标点符号。但是你不用担心,主流的中文输入法(如搜狗拼音)会默认采用中文标点的。代码片段如下:

    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事件中,就没有问题了。于是,我把输入法修复的代码修改如下:

    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测试》

  • 相关阅读:
    [LeetCode]N-Queens
    [LeetCode]Anagrams
    [LeetCode]Permutations II
    [LeetCode]Permutations
    [STL]heap和priority_queue
    [STL]deque和stack、queue
    10、小易记单词--2017网易春招
    29、剑指offer--最小的K个数
    28、剑指offer--数组中出现次数超过一半的数字
    9、涂棋盘--2017网易春招
  • 原文地址:https://www.cnblogs.com/xiaofengfeng/p/3357307.html
Copyright © 2011-2022 走看看