using System; using System.Collections.Generic; using System.Text; using System.ComponentModel; using System.Drawing; using System.Windows.Forms; using System.Runtime.InteropServices; namespace HotelPms.Share.Windows.Component { /// /// 拡張機能を持ったTextBoxです。 /// IMEを利用して入力したコンテキストを従属するコントロールまたはイベントイベントデータとして渡す機能を持ったTextBox。 /// 使用例:全角入力された文字を半角カナ文字に変換して渡す。 /// [ToolboxBitmap(typeof(System.Windows.Forms.TextBox))] public class TextBoxFurigana : CTextBox { /// /// ImeComposed イベントを処理するメソッドを表します。 /// /// イベントのソース。 /// 変換文字列情報。 public delegate void ImeComposedEventHandler(object sender, ImeComposedEventArgs e); /// /// IME入力文字が確定されたとき発生するイベントを定義します。 /// [Description("IME入力文字が確定されたとき発生するイベントです。"), Category("キー")] public event ImeComposedEventHandler ImeComposed = null; private const int WM_CHAR = 0x0102; private const int WM_IME_COMPOSITION = 0x010F; private const int GCS_RESULTREADSTR = 0x0200; private System.Windows.Forms.Control m_Control = null; // 連結する子コントロール /// /// TextBoxEx クラスの新しいインスタンスを初期化します。 /// public TextBoxFurigana() { } /// 指定されたウィンドウに関連付けられている入力コンテキストを取得します。 [DllImport("Imm32.dll")] public static extern int ImmGetContext(IntPtr hWnd); /// 変換文字列に関する情報を取得します。 [DllImport("Imm32.dll")] public static extern int ImmGetCompositionString(int hIMC, int dwIndex, StringBuilder lpBuf, int dwBufLen); /// /// 入力コンテキストを解放し、コンテキスト内の関連メモリのロックを解除します。 /// アプリケーションで ImmGetContext 関数を呼び出したら、必ず対応する ImmReleaseContext 関数を呼び出さなければなりません。 /// [DllImport("Imm32.dll")] public static extern bool ImmReleaseContext(IntPtr hWnd, int hIMC); // IMEの状態取得 [DllImport("Imm32.dll")] private static extern int ImmGetOpenStatus(int hIMC); /// /// 従属するコントロールを取得または設定します。 /// [Description("連結する子コントロールを取得または設定します。"), Category("動作"), DefaultValue(null)] public Control ChildControl { get { return m_Control; } set { m_Control = value; } } /// /// キーが押された時呼ばれます。 /// /// 処理対象の System.Windows.Forms.Message。 protected override void WndProc(ref Message m) { if ((ImeComposed != null || ChildControl != null)) { if (m.Msg == WM_IME_COMPOSITION) { // 変換が確定状態か確認する if (((int)m.LParam & GCS_RESULTREADSTR) > 0) { // 入力コンテキストを取得します int hIMC = ImmGetContext(this.Handle); //int status = ImmGetOpenStatus(hIMC); // 変換文字列に関する情報を取得するのに必要なバッファサイズを取得します。 int strLen = ImmGetCompositionString(hIMC, GCS_RESULTREADSTR, null, 0); // 文字列バッファを確保します。 StringBuilder str = new StringBuilder(strLen); // 変換文字列に関する情報を取得します。 ImmGetCompositionString(hIMC, GCS_RESULTREADSTR, str, str.Capacity); // 入力コンテキストを解放します。 ImmReleaseContext(this.Handle, hIMC); //System.Diagnostics.Debug.WriteLine(string.Format("Status:{0},String:{1}",status, str.ToString(0, strLen))); //イベントデータの設定&起動 if (ImeComposed != null) { //文字列を対象長さ分切り出して(後ろのゴミを排除して)イベントデータを生成する if (str.Length >= strLen) { ImeComposed(this, new ImeComposedEventArgs(str.ToString(0, strLen), GetSelectionMode())); } } //従属コントロールに入力文字文字列を設定する if (ChildControl != null) { if (str.Length >= strLen) { SetCompositionStr(ChildControl, str.ToString(0, strLen)); } else { //MessageBox.Show("エラー::取得する長さが異常です。" + "Text=" + str.ToString() + "/長さ=" + str.Length + "/取得長さ=" + strLen); //SetCompositionStr(ChildControl, str.ToString(0, str.Length)); } } } else if ((int)m.LParam == 6144) { try { //従属コントロールに入力文字文字列を設定する if (ChildControl != null) { //従属コントロールに入力文字文字列を設定 if (InputLanguage.CurrentInputLanguage.LayoutName.Contains("ATOK")) { SetCompositionStr(ChildControl, " "); } } } catch { } } } else if (m.Msg == WM_CHAR) { //半角英数字 //20121002 毛利 Redmine#2795 #2796 int hIMC = ImmGetContext(this.Handle); if (ImmGetOpenStatus(hIMC) == 0) { char chr = Convert.ToChar(m.WParam.ToInt32() & 0xff); if (m.WParam.ToInt32() >= 32) { //イベント起動 string str = chr.ToString(); //System.Diagnostics.Debug.WriteLine(string.Format("String:{0}", str)); //イベントデータの設定&起動 if (ImeComposed != null) { ImeComposed(this, new ImeComposedEventArgs(str, GetSelectionMode())); } //従属コントロールに入力文字文字列を設定する if (ChildControl != null) { try { //従属コントロールに入力文字文字列を設定 SetCompositionStr(ChildControl, str); } catch { } } } } ImmReleaseContext(this.Handle, hIMC); } } //if (!this.IsDisposed) //{ // if (this.Parent != null) // { // if (!base.IsDisposed) // { // try // { base.WndProc(ref m); // } // catch // { // //伝票入力で存在するコードを入力した後、科目名のテキストボックスをクリックすると // //なんかここで落ちてた。回避の方法がよくわからないので、とりあえずtry-catchで。 // } // } // } //} } /// /// 従属するコントロールに入力文字を設定するメッソド /// /// 入力した文字列を設定するコントロール。 /// IMEを利用して入力した文字列。 protected virtual void SetCompositionStr(System.Windows.Forms.Control cntl, string inputStr) { // 入力開始位置が先頭か確認する inputStr.Replace("'", string.Empty); if (this.SelectionStart == 0) { // 選択範囲が既入力文字列全体と一致するか確認する if (this.SelectionLength == this.Text.Length) { // 既入力文字列を選択して入力する場合、入力文字列で置換える cntl.Text = inputStr; } else { // 既入力文字列の先頭に入力文字列を挿入する cntl.Text = inputStr + cntl.Text; } } else { // 先頭以外の場合、全て追加設定する cntl.Text += inputStr; } //20100108 ogi DBの制限を超えないように対応 if (cntl.Text.Length > (cntl as TextBox).MaxLength) { cntl.Text = cntl.Text.Substring(0, (cntl as TextBox).MaxLength); } } private int GetSelectionMode() { if (this.SelectionStart == 0) { // 選択範囲が既入力文字列全体と一致するか確認する if (this.SelectionLength == this.Text.Length) { return 0; } else { // 既入力文字列の先頭に入力文字列を挿入する return 1; } } else { // 先頭以外の場合、全て追加設定する return 2; } } /// /// COMMENT:202061012 毛利 機能追加 /// メインテキストが空白になった場合、子テキストも空白に。 /// /// protected override void OnTextChanged(EventArgs e) { if (this.Text == "") { if (ChildControl != null) { this.ChildControl.Text = ""; } if (ImeComposed != null) { ImeComposed(this, new ImeComposedEventArgs(string.Empty, 3)); } } base.OnTextChanged(e); } /// /// 変換文字列情報イベントデータ。 /// [Serializable] public class ImeComposedEventArgs : System.EventArgs { /// 変換文字列。 private string m_InputStr; private int m_SelectionMode; //0:置換える、1:先頭に挿入、2:追加 /// /// 変換文字列情報イベントデータクラスの新しいインスタンスを初期化します。 /// public ImeComposedEventArgs() { m_InputStr = ""; } /// /// 変換文字列情報イベントデータクラスの新しいインスタンスを初期化します。 /// /// IMEを利用して入力した文字列。 public ImeComposedEventArgs(string inputStr, int selectionMode) { m_InputStr = inputStr; m_SelectionMode = selectionMode; } /// /// IMEを利用して入力した文字列を取得します。 /// public string InputString { get { return m_InputStr; } } /// /// 0:置換える、1:先頭に挿入、2:追加 /// public int SelectionMode { get { return m_SelectionMode; } } } } }