none
textBoxに振り仮名を自動入力時に制限するキーを設定したいのですが RRS feed

  • 質問

  •  

    下の青字のように制限するキーを設定しているのですが、このほかにも

      Home、PageUp、PageDown、ScrollRock、PauseBreak

    などを制限したくてずらずらと並べてはみたのですが、もっとスマートな方法がありはしないかと思ったりします。

     

    これらのキーを押すと

      タナカタナカタナカタナカタナカタナカタナカタナカタナカ

    というように押した回数だけダブって表示されてしまいます。

    (氏名を入力しているときにまさかこのようなキーを押す人はいないと思うのですが、念のために)

     

    このことについてアドバイスをいただけないでしょうか。

     

    「振り仮名自動入力」のクラスを作れたらなぁと夢見ているのですが、相当に難しいのでしょうね。

    何を勉強したらいいでしょうか。

     

    Code Snippet

    using System;
    using System.Text;
    using System.Windows.Forms;
    using System.Runtime.InteropServices;

    namespace tbxKatakana
    {
        public partial class frmtbxKatakana : Form
        {
            public string Furigana = "";
            const int GCS_RESURTREADSTR = 0x0200;

            [DllImport("imm32.dll")]
            static extern IntPtr ImmGetContext(IntPtr hWnd);
            [DllImport("imm32.dll")]
            static extern int ImmGetCompositionString(
                IntPtr hiMC, int dwIndex, StringBuilder lpBuf, int dwBufLen);
            [DllImport("imm32.dll")]
            static extern bool ImmReleaseContext(IntPtr hWnd, IntPtr hiMC);

            public frmtbxKatakana()
            {
                InitializeComponent();
            }

            private void textBox1_TextChanged(object sender, EventArgs e)
            {
                if (textBox1.Text == "")
                {
                    textBox2.Text = "";
                    Furigana = "";
                }
            }

            private void textBox1_KeyDown(object sender, KeyEventArgs e)
            {
                string tmpStr;

                IntPtr himc = ImmGetContext(textBox1.Handle);
                if (himc == IntPtr.Zero || e.KeyCode == Keys.Return
                    || e.KeyCode == Keys.Back || e.KeyCode == Keys.Left
                    || e.KeyCode == Keys.Right || e.KeyCode == Keys.Up
                    || e.KeyCode == Keys.Down || e.KeyCode == Keys.Delete  
                    || e.KeyCode == Keys .Shift || e.KeyCode == Keys .Control
                    || e.KeyCode == Keys .Insert || e.KeyCode == Keys .End )
                {
                    return;
                }
                try
                {
                    int dwSize = ImmGetCompositionString(himc, GCS_RESURTREADSTR, null, 0);
                    StringBuilder lpBuf = new StringBuilder(dwSize);

                    ImmGetCompositionString(himc, GCS_RESURTREADSTR, lpBuf, dwSize);
                    lpBuf.Length = dwSize;
                    tmpStr = lpBuf.ToString();
                    Furigana += tmpStr;
                    textBox2.Text = Furigana;
                }
                finally
                {
                    ImmReleaseContext(textBox1.Handle, himc);
                }
            }
        }
    }

     

     

    2008年6月10日 2:00

回答

  • いよいよ、ウィンドウメッセージです。

    詳しくはやっぱりご自身で調べていただくとして、

    私なりに大雑把に説明しますと、

    TextBoxにキー入力された場合、キー入力されたよ、というメッセージがTextBoxに飛んできます。

    TextBoxはそのメッセージに従って、一仕事します。

    一仕事とは、文字を表示する、などのことです。

    クリックされた場合も、クリックされたよ、というメッセージが飛んできます。

    全ての場合についてメッセージが飛んできます。

    その、飛んでくる全てのメッセージを処理できるプロシージャがWndProcです。

       

    キー入力されたよ、というメッセージは0x102(WM_CHAR)と決められています。

    そこで、そのメッセージが来た時に処理をしてあげればよいことになります。

    厳密にはWM_CHARはTextChangedと同等ではありません。ペーストされた場合などに対応しません。

     

    WndProcは万能ですが、積極的に使うものではありません。

    今回のフリガナの時のように、変換確定イベントが標準では用意されていない場合などに、

    しょうがなく、WndProcで処理します。あまり、わかりやすいコードになりませんからね。

    Code Snippet

       class MyTextBox:TextBox

        {
            //プライベートなメンバ
            private string findstring;

            private const int WM_CHAR = 0x102;
           
            //プロパティとして公開
            public string FindString
            {
                get
                {
                    return findstring;
                }
                set
                {
                    findstring = value;
                }

            }

            //イベント
            public delegate void FindEventHandler(object sender, FindStringEventArgs e);
            public event FindEventHandler FindStringEvent;


            protected override void OnTextChanged(EventArgs e)
            {
                //if (this.findstring != "")
                //{
                //    int pos=this.Text.IndexOf(this.findstring);
                //    if (pos >= 0)
                //    {
                //        this.BackColor = System.Drawing.Color.Red;
                //        FindStringEventArgs fsea=new FindStringEventArgs();
                //        fsea.FindPosition=pos;
                //        FindStringEvent(this,fsea);

                //    }
                //    else
                //    {
                //        this.BackColor = System.Drawing.Color.White;

                //    }
                //}
                //else
                //{
                //    this.BackColor = System.Drawing.Color.White;
                //}

                base.OnTextChanged(e);
            }

            protected override void WndProc(ref Message m)
            {
                base.WndProc(ref m);
               
                if (m.Msg == WM_CHAR)
                {
                    if (this.findstring != "")
                    {
                        int pos = (this.Text ).IndexOf(this.findstring);
                        if (pos >= 0)
                        {
                            this.BackColor = System.Drawing.Color.Red;
                            FindStringEventArgs fsea = new FindStringEventArgs();
                            fsea.FindPosition = pos;
                            FindStringEvent(this, fsea);

                        }
                        else
                        {
                            this.BackColor = System.Drawing.Color.White;

                        }
                    }
                    else
                    {
                        this.BackColor = System.Drawing.Color.White;
                    }
                }
               
               
            }
        }

     

     

    実行して処理を確認します。

     

    これで、一通りの説明は終わりです。

    フリガナコードをものにしてください。

    では、頑張ってください。
    2008年6月15日 1:20

すべての返信

  • 外池と申します。

     

    「振り仮名」と仰っているので、入力できる文字を仮名文字だけに制限することか・・・、と思ったのですが、違うようですね。

    カーソルやフォーカスの制御を制限したい、というようにお見受けします。

     

    問題点の整理のためにお尋ねしますが、

     

         とりあえず、振り仮名のことは忘れて回答してもよいのでしょうか?

         (私としては、これは比較的簡単だと思うので、回答しやすいのですが。)

     

         カーソルやフォーカスの制御をどのように制限したいのでしょうか?

         (私としては、Windowsの標準のインターフェイスを制限することは避けるべきという意見なので

          何もしないほうが良いと思うんですが・・・)

     

     

         

     

    2008年6月10日 5:02
  •  外池 さんからの引用

         とりあえず、振り仮名のことは忘れて回答してもよいのでしょうか?

    はい、そういうことでお願いします。

     外池 さんからの引用

         カーソルやフォーカスの制御をどのように制限したいのでしょうか?

         (私としては、Windowsの標準のインターフェイスを制限することは避けるべきという意見なので

          何もしないほうが良いと思うんですが・・・)

    Windowsの標準のインターフェイスを制限したいということではなく、BackSpaceとかDelete外のキーの働きはそのままに残っています。しかし、これらのキーを押したときに振り仮名を表示する動作をさせないということなのです。よろしくお願いします。

     

    2008年6月10日 6:09
  • zen73さんって、同じ質問をずっと前、@ITでされてますか?

    そこで出ている、WM_IME_COMPOSITIONを使う方法がいいと思いますよ。

    2008年6月10日 14:06
  • 追記。

    一つ前の質問を見てませんでした。

    DataGridViewなんですね。

    さっきいった方法は、めんどくさい気がします。。。

    2008年6月10日 15:05
  • はなはなさん、ありがとうございます。

    @ITでの質問は確か3・4年前の質問でして、DataGridのセルに振り仮名を自動入力するものでした。

    前回の質問はこれをdataGridViewに適用しようと意図したものでしたが、handcraftさんのご指導のお陰でうまく動いてくれています。

     

    今回の質問はdataGridでもdataGridViewでもなくtextBoxへの振り仮名自動入力の際のキー制限に関することです。

    「振り仮名自動入力クラス」を作ることは、ボケの入っている65歳の私には非常にむずかしいことだとは承知しています。いますぐに作りたいというのではなく遠い将来の目標(まぁ、生きているうちに)なのです。これについては参考本などをご紹介いただけたらと思っています。

     

    tbx1[善浪|   ] → tbx2[ゼンナミ     ]

    ここで、BackSpaceキーを押すと

    tbx1[善|    ] → tbx2[ゼンナミゼンナミ ]

    となっていまいます。Space・矢印キー等の押下でも同じ現象となります。

    そこで、

     if (himc == IntPtr.Zero || e.KeyCode == Keys.Back)

    としたら、

    tbx1[善|   ] → tbx2[ゼンナミ     ]

    となって、tbx2[ゼンナミゼンナミ ]とはならないことがわかったものですから、同様の振る舞いをする他のキーもということで、

    if (himc == IntPtr.Zero || e.KeyCode == Keys.Return
                    || e.KeyCode == Keys.Back || e.KeyCode == Keys.Left
                    || e.KeyCode == Keys.Right || e.KeyCode == Keys.Up
                    || e.KeyCode == Keys.Down || e.KeyCode == Keys.Delete  
                    || e.KeyCode == Keys .Shift || e.KeyCode == Keys .Control
                    || e.KeyCode == Keys .Insert || e.KeyCode == Keys .End )

    としみました。

     

    これで私の思い通りの動きにはなったのですが、振り仮名表示を規制するキーを羅列するのも芸がないなぁと思ったのです。

     

    それでもっとスッキリとスマートに(或は別の方法で)コードを記述する方法があるなら、その方法を知りたいのです。

     

    よろしくお願いします。

     

     

     

     

     

     

     

     

     

     

     

     

    2008年6月10日 22:33
  • 解釈が違っていたら、ごめんなさい。

    私が言いたかったのは、KeyDownで処理せずにWndProcで処理すれば、キーを羅列せずに済み、

    すっきりしますよ、ということだったのです。

    ただ、DataGridViewの中のTextBoxについてならば、ちょっとやることが増えるから、お勧めできないかなぁ、

    と思った次第です。

    普通のTextBoxとして、教えましょうか?

    継承から勉強しないといけませんが。

    2008年6月11日 0:55
  • 外池です。

     

    ご質問の意図、わかりました。

    が・・・、私には回答できる能力が無いので申し訳ありません。

     

    2008年6月11日 1:25
  • 外池さん、はなはなさん、ありがとうございます。

     はなはな さんからの引用

     解釈が違っていたら、ごめんなさい。

    私が言いたかったのは、KeyDownで処理せずにWndProcで処理すれば、キーを羅列せずに済み、

    すっきりしますよ、ということだったのです。

    ただ、DataGridViewの中のTextBoxについてならば、ちょっとやることが増えるから、お勧めできないかなぁ、

    と思った次第です。

    普通のTextBoxとして、教えましょうか?

    継承から勉強しないといけませんが。

    教えていただけるのでしたら是非にもお願いします。

    「WndProcで処理すれば、キーを羅列せずに済み、すっきりしますよ」ということですが、上に示したAPIの部分は何とかDataGridで振り仮名を表示したい一心で考えたものでした。今となっては呆けのせいで解読も覚束ないし、<継承>についても人のコードを見ているとなるほどなぁとは思っても自分で1から書くとなると全く心もとないのですがよろしくお願いします。

     

    2008年6月11日 5:01
  •  zen73 さんからの引用

    それでもっとスッキリとスマートに(或は別の方法で)コードを記述する方法があるなら、その方法を知りたいのです。

     

    昔、諸農さんのコードを元に私が作ったコードが以下にありますので、参考になるかもしれません。振り仮名をイベントで提供するカスタムコントロールを作成しています。

     

    テキストボックスで漢字名を入力した際にルビを振る方法について
    http://forums.microsoft.com/MSDN-JA/ShowPost.aspx?PostID=456214&SiteID=7

    2008年6月11日 6:56
    モデレータ
  • ではでは、私の思うままに、説明します。間違った場合はご指摘を。>ALL

     

    まず、とにかく継承してみる!

     

    1.プロジェクトを作成し、プロジェクトの名前をMyFuriganaとします。

    2.メニューの「プロジェクト」から「クラスの追加」を選択し、クラスの名前をMyTextBoxとします。

     

    ここまでの作業でMyTextBoxの中身は次のようになっています。

    Code Snippet

    using System;
    using System.Collections.Generic;
    using System.Text;


     

    namespace MyFurigana
    {
        class MyTextBox

        {

        }
    }

     

     

    3.クラスMyTextBoxがTextBoxを継承するように指定します。

     

    赤字の部分を追加します。

    Code Snippet

    using System;
    using System.Collections.Generic;
    using System.Text;
    using System.Windows.Forms;

     

    namespace MyFurigana
    {
        class MyTextBox:TextBox

        {

        }
    }

     

     

    4.メニューから「ビルド」→「MyFuriganaのビルド」を選択し、一回ビルドします。

     

    5.Form1.cs[デザイン]の画面を開きます。ツールボックスのMyFuriganaコンポーネントからMyTextBoxをフォームに貼り付けます。

     

    6.実行して、普通のTextBoxとして動作することを確認します。

     

    さて、まずはここまで。

    2008年6月11日 7:13
  • 答えがでてしまったので、私は退散。(^^;

     

    2008年6月11日 7:16
  • はなはなはなさん、trapemiyaさん、ありがとうございます。

    はなはなはなさんのご指導に期待していたのですが残念です。

    trapemiyaさんがお示しくださったクラスの使用法を考えていたのですが結局はわからず、使えないままでいます。

     

    私が今までに作ったり使ったりしていたいたクラスはそこに定義されているメッソッドを使えばいいだけのものでしたのでよかったのですが・・・。

     

    2008年6月13日 7:12
  • あんな感じでいいなら、続けましょうか?

    とりあえず、先の説明はご理解していただけたでしょうか?

    最終的には、trapemiyaさんが示されたものとすり合わせてもらうとして、大雑把な筋を示すことならできます。

    ただ、私は、かなりいい加減なところがありますので、あくまでご自身の検証のもとに。

     

    また、TextBoxをDataGridViewに組み込むのも、最後に。

    2008年6月13日 7:49
  •  zen73 さんからの引用

    trapemiyaさんがお示しくださったクラスの使用法を考えていたのですが結局はわからず、使えないままでいます。

     

    新規プロジェクトからWindowsフォームコントロールライブラリを選択して新しくプロジェクトを作ります。これはユーザーコントロール用のテンプレートなので、このままでは使えません。なぜかVS2008にはWindowsコントロールライブラリというテンプレートがありません。
    私が以前示したコードはパーシャルなクラスではないので、以下のように修正して下さい。クラス名とか名前空間とかファイル名はは適当に変えてもらってかまいません。

     

    FuriganaTextBox2.cs

     

    Code Snippet

    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Drawing;
    using System.Data;
    using System.Linq;
    using System.Text;
    using System.Windows.Forms;
    using System.Runtime.InteropServices;


    namespace WindowsFormsControlLibrary1
    {
     public partial class FuriganaTextBox2 : System.Windows.Forms.TextBox
     {
      public FuriganaTextBox2()
      {
       InitializeComponent();
      }

         const int WM_IME_COMPOSITION = 0x010F;
            const int GCS_RESULTREADSTR = 0x0200;

            [DllImport("Imm32.dll", CharSet = CharSet.Auto)]
            public static extern int ImmGetContext(IntPtr hWnd);

            [DllImport("Imm32.dll")]
            public static extern int ImmGetCompositionString(
                int hIMC,
                int dwIndex,
                IntPtr lpBuf,
                int dwBufLen
                );
           
            [DllImport("Imm32.dll", CharSet = CharSet.Auto)]
            public static extern bool ImmReleaseContext(IntPtr hWnd,int hIMC);

            [Serializable]
                public class CompositionEventArgs:EventArgs
            {
                public string ImeStr;
            }

            #region デリゲート & イベントの定義
            //デリゲート & イベントの定義
            public delegate void CompositionEventHandler(object sender,CompositionEventArgs e);
            public event CompositionEventHandler CompositionEvent;
            #endregion

            protected override void OnPaint(PaintEventArgs pe)
            {
                // TODO: カスタム描画コードをここに追加してください。

                // OnPaint で基本クラスを呼び出し中
                base.OnPaint(pe);
            }

            #region WndProc
            protected override void WndProc(ref Message m)
            {
                if (CompositionEvent != null && m.Msg == WM_IME_COMPOSITION)
                {
                    if (((int)m.LParam & GCS_RESULTREADSTR)>0)
                    {
                        IntPtr buffer = IntPtr.Zero;

                        try
                        {
                            int Imc = ImmGetContext(this.Handle);
                            int bytes = ImmGetCompositionString(Imc, GCS_RESULTREADSTR, IntPtr.Zero, 0);

                            buffer = Marshal.AllocHGlobal(bytes);
                            ImmGetCompositionString(Imc, GCS_RESULTREADSTR, buffer, bytes);
                            byte[] arrByte = new Byte[bytes];
                            Marshal.Copy(buffer, arrByte, 0, bytes);
                            string str = Encoding.GetEncoding(932).GetString(arrByte);

                            ImmReleaseContext(this.Handle,Imc);

                            CompositionEventArgs args = new CompositionEventArgs();
                            args.ImeStr = str;

                            CompositionEvent(this,args);
                        }
                        finally
                        {
                            Marshal.FreeHGlobal(buffer);
                        }
                    }
                }
               
                base.WndProc(ref m);
            }
            #endregion

        }
     
    }

     

     

    FuriganaTextBox2.Designer.cs

     

    Code Snippet

    namespace WindowsFormsControlLibrary1
    {
     partial class FuriganaTextBox2
     {
      ///
      /// 必要なデザイナ変数です。
      ///
      private System.ComponentModel.IContainer components = null;

      ///
      /// 使用中のリソースをすべてクリーンアップします。
      ///
      /// マネージ リソースが破棄される場合 true、破棄されない場合は false です。
      protected override void Dispose(bool disposing)
      {
       if (disposing && (components != null))
       {
        components.Dispose();
       }
       base.Dispose(disposing);
      }

      #region コンポーネント デザイナで生成されたコード

      ///
      /// デザイナ サポートに必要なメソッドです。このメソッドの内容を
      /// コード エディタで変更しないでください。
      ///
      private void InitializeComponent()
      {
       components = new System.ComponentModel.Container();
      }

      #endregion
     }
    }

     

     

    これでコンパイルできることを確認して下さい。

     

    次にこの振り仮名テキストボックスを使うプロジェクトを開きます。

     

    参照設定で上の振り仮名テキストボックスのプロジェクトを追加します。
    ソリューションから上の振り仮名テキストボックスのプロジェクトを追加します(これは必須ではないですが、こうしとくと振り仮名テキストボックスのデバッグができます)。

     

    そうするとツールボックスにFuriganaTextBox2が追加されますので、それをフォームにドラッグします。あとはその振り仮名テキストボックスのCompositonEventイベントで、例えばtextBox1.Text = e.ImeStr; とすれば、textBox1に振り仮名が自動的に入ります。

     

    2008年6月13日 8:27
    モデレータ
  • はなはなはなさんとtrapemiyaさんのお二人には、今回はもとより以前からとてもお世話になりいろんなことを懇切丁寧に教えていただいておりますことに感謝申し上げます。

     はなはなはな さんからの引用

    あんな感じでいいなら、続けましょうか?

    とりあえず、先の説明はご理解していただけたでしょうか?

    はい、確認できました。

    「Form1.cs[デザイン]の画面を開きます。ツールボックスのMyFuriganaコンポーネントからMyTextBoxをフォームに貼り付けます。」ってことは、初めての経験でちょっとおどろきでした。

     

    はなはなはなさんには貴重なお時間を割かれてお教えくださっていることを思うと恐縮ですが、よろしくお願いします。

     

     trapemiya さんからの引用

    あとはその振り仮名テキストボックスのCompositonEventイベントで、例えばtextBox1.Text = e.ImeStr; とすれば、textBox1に振り仮名が自動的に入ります。

    ありがとうございます。早速にも試してみます。


     

     

     

    2008年6月13日 9:35
  • 私は好きでやってますので。タブン、質問に答えるのも、人に教えるのも、好きなんでしょう。ww

     

    とにかく、拡張してみる!

     

    前回で、TextBoxを継承したMyTextBoxは作成できました。

    しかし、これをこのまま使っては、普通のTextBoxとまったく同じで面白くありません。

    なので、機能をかくちょうしましょう♪

     

    フリガナ機能は、テーマとして重いので、ここでは、ある特定の文字が入力されたら

    背景色を変える、というのをして見ます。

     

    MyTextBox.csの中身に以下のコードを付け足してください。

     

     

    Code Snippet

    namespace MyFurigana
    {
        class MyTextBox:TextBox

        {
            protected override void OnTextChanged(EventArgs e)
            {
                if (this.Text.IndexOf("ぜん") >= 0)
                {
                    this.BackColor = System.Drawing.Color.Red;
                }
                else
                {
                    this.BackColor = System.Drawing.Color.White;

                }
                base.OnTextChanged(e);
            }
        }

       
    }

     

     

    実行してみて、いろんな文字を入力してみましょう。

    ”ぜん”という文字が入力されると、背景色が赤になるはずです。

     

    でも、継承なんかしなくてもできるよ、と思われるかもしれません。

    ただ、この仕様のTextBoxが100個いると言われたとしたら、

    あとあと、同じ仕様のTextBoxを別のプロジェクトで使うとしたら、

    継承して作っておいたほうがらくちんです。

    2008年6月13日 10:10
  • はなはなはなさん、確認できました。

     

    私の手元にC#の解説本が結構あるのですが、どれもが「継承」の解説がConsoleベースになっていましたのでwindowsでとなるとその手がかりがみえなかったのですが、教えていただいて「そいうことか」ととても勉強になります。

    教えていただいたことを”はなはな教授の講義録”として印刷すると共にDドライブに保存しています。

     

     

     

    2008年6月13日 12:04
  • いや、実は、こんな話して喜んでくれるかどうかは?なので、私も二の足を踏むところがありますが、

    @ITで、話があそこで切れていた点、zen73さんが夢だと言われた点、

    これまでの投稿から、内容をひとつひとつ理解されたいのかなぁ、と予想して、やってます。

     

    で、とりあえず、続きを。

     

    独自のプロパティをつくってみる!

     

    前回作ったMyTextBoxは、"ぜん"という固定した文字を検索しました。

    これでは、応用が利かないので、指定できるようにします。

     

    Code Snippet

    class MyTextBox:TextBox

        {
            //プライベートなメンバ
            private string findstring;

            //プロパティとして公開
            public string FindString
            {
                get
                {
                    return findstring;
                }
                set
                {
                    findstring = value;
                }

            }
           
            protected override void OnTextChanged(EventArgs e)
            {
                if (this.findstring != "")
                {
                    if (this.Text.IndexOf(this.findstring) >= 0)
                    {
                        this.BackColor = System.Drawing.Color.Red;
                    }
                    else
                    {
                        this.BackColor = System.Drawing.Color.White;

                    }
                }
                else
                {
                    this.BackColor = System.Drawing.Color.White;
                }

                base.OnTextChanged(e);
            }
        }

     

     

    一度、ビルドしてから、Formのほうに以下のコードを加えます。

     

    Code Snippet

    public partial class Form1 : Form
        {
            public Form1()
            {
                InitializeComponent();
            }

            private void Form1_Load(object sender, EventArgs e)
            {
                this.myTextBox1.FindString = "はな";

            }

           
        }

     

     

    これで、指定できるようになりました。

    実行して、"はな"という文字が入力されたら赤色になることを確認します。

    2008年6月13日 13:19
  •  はなはなはな さんからの引用

     いや、実は、こんな話して喜んでくれるかどうかは?なので、私も二の足を踏むところがありますが、

    @ITで、話があそこで切れていた点、zen73さんが夢だと言われた点、

    これまでの投稿から、内容をひとつひとつ理解されたいのかなぁ、と予想して、やってます。

    今まで人からプログラミングの基礎かを教わったことがまったくなかったものですからとても喜んでいます。このように丁寧に教えていただけますと、とてもわかりやすくありがたく思っています。

     

    ここまで、しっかりと理解できました。

     

    2008年6月13日 14:26
  • あと、イベントと、ウィンドウメッセージを習得すれば、trapemiyaさんのコードが読めるはずです。

     

    ということで今回はイベント!

    前回の話で、色もプロパティにしたくなりますが、それはお任せします。

     

    フォームに指定の文字列が見つかったよ、というのを通知してあげましょう。

    まずはdelegateを定義します。ここではFindEventHandlerと名づけます。(Foundのほうがいいかな?)

    引数にobjectとFindStringEventArgsを指定します。

    FindStringEventArgsは上にある、独自クラスです。普通のEventArgsでもよいですが、

    ついでに次のテーマに取り上げる予定ですので、独自クラスを定義しておきます。

    なお、クラスは一つのファイルに一つのほうがいいかもしれません。

    ここでは、めんどくさい、という理由だけで同じファイルに書いています。

    次にdelegateに基づいたeventを定義します。

     

    そこまで済んだら、あとはイベントを起こしたいところでFindStringEventを呼び出すだけです。

    Code Snippet

    namespace MyFurigana
    {
        class FindStringEventArgs : EventArgs
        {
           
        }
       
        class MyTextBox:TextBox

        {
            //プライベートなメンバ
            private string findstring;

            //プロパティとして公開
            public string FindString
            {
                get
                {
                    return findstring;
                }
                set
                {
                    findstring = value;
                }

            }

            //イベント
            public delegate void FindEventHandler(object sender, FindStringEventArgs e);
            public event FindEventHandler FindStringEvent;


            protected override void OnTextChanged(EventArgs e)
            {
                if (this.findstring != "")
                {
                    if (this.Text.IndexOf(this.findstring) >= 0)
                    {
                        this.BackColor = System.Drawing.Color.Red;
                        FindStringEvent(this,new FindStringEventArgs());

                    }
                    else
                    {
                        this.BackColor = System.Drawing.Color.White;

                    }
                }
                else
                {
                    this.BackColor = System.Drawing.Color.White;
                }

                base.OnTextChanged(e);
            }
        }

       
    }

     

     

    ここまで書いたら、いつものごとく、ビルドしましょう。

     

    フォームにリストボックスを一つ貼り付けます。メッセージ表示用です。

    サンプルとしてださい気がしますが、目をつむってください。w

    また、myTextBox1をデザイナで選択し、プロパティシートのイベント表示(カミナリマークのボタン)をして

    FindStringEventを選択して、以下のコードを記述します。

    Code Snippet

    public partial class Form1 : Form
        {
            public Form1()
            {
                InitializeComponent();
            }

            private void Form1_Load(object sender, EventArgs e)
            {
                this.myTextBox1.FindString = "はな";

            }

            private void myTextBox1_FindStringEvent(object sender, FindStringEventArgs e)
            {
                this.listBox1.Items.Add("文字列が見つかりました。");
            }

           
        }

     

     

    実際に動かして、メッセージが表示されることを確認します。
    2008年6月13日 15:06
  • 確認できました。

     

    ”delegateに基づいたeventの定義”が初めての経験でしたので、手元の数冊の解説本で勉強していたものですから、お返事が遅れてしまいました。

    2008年6月14日 9:49
  • ご自分で勉強されたとはすばらしい。(^^)

    わたしが詳しく説明しなかったのは、単に詳しく説明するほど理解していないからです。w

    今度、私がデリゲートで質問したら、答えてくださいね。ww

     

    さて今回は、

    イベント通知時に情報を渡す!

    です。

    言ってませんでしたが、使っているサンプルは文字列が何個もあるばあいとかは考えてません。

    あしからず。

     

    FindStringEventArgsにプロパティを設定します。

    見つかった文字列が何文字目かの情報を設定してからイベントを起こします。

    Code Snippet

    namespace MyFurigana
    {
        class FindStringEventArgs : EventArgs
        {
            private int findposition;

            public int FindPosition
            {
                get
                {
                    return findposition;
                }
                set
                {
                    findposition = value;
                }
            }

               
        }
       
        class MyTextBox:TextBox

        {
            //プライベートなメンバ
            private string findstring;

            //プロパティとして公開
            public string FindString
            {
                get
                {
                    return findstring;
                }
                set
                {
                    findstring = value;
                }

            }

            //イベント
            public delegate void FindEventHandler(object sender, FindStringEventArgs e);
            public event FindEventHandler FindStringEvent;


            protected override void OnTextChanged(EventArgs e)
            {
                if (this.findstring != "")
                {
                    int pos=this.Text.IndexOf(this.findstring);
                    if (pos >= 0)
                    {
                        this.BackColor = System.Drawing.Color.Red;
                        FindStringEventArgs fsea=new FindStringEventArgs();
                        fsea.FindPosition=pos;
                        FindStringEvent(this,fsea);

                    }
                    else
                    {
                        this.BackColor = System.Drawing.Color.White;

                    }
                }
                else
                {
                    this.BackColor = System.Drawing.Color.White;
                }

                base.OnTextChanged(e);
            }
        }

       
    }

     

     

    ここでいつものごとくビルドします。

     

    フォーム側を以下のように変更します。

     

    Code Snippet

    private void myTextBox1_FindStringEvent(object sender, FindStringEventArgs e)
            {
                this.listBox1.Items.Add(e.FindPosition.ToString() + "文字目に文字列が見つかりました。");
            }

     

     

    実行してメッセージが追加されることを確認します。
    2008年6月14日 17:08
  • はなはなはなさん、おはようございます。

    確認できました。(文字列が何個もある場合も)

     はなはなはな さんからの引用

    ご自分で勉強されたとはすばらしい。(^^)

    わたしが詳しく説明しなかったのは、単に詳しく説明するほど理解していないからです。w

    今度、私がデリゲートで質問したら、答えてくださいね。ww

    正直に申しましょう。10年ほど前にに読んだことのある日経BP社「プログラミングC#]という本を本棚から引っ張り出し”デリゲートとイベントハンドラ”という章を開いたところ、当然でしょうけれど<理解不能>というタッグシールがはられているではありませんか。それでも相当量の書き込みがありますから何とか理解しようとしたのでしょう。10才も若い時に理解できなかったのですから今となっては自分の書いた書き込みさえ理解できない情けなさが残るのみでした。

     

    2008年6月14日 20:41
  • いよいよ、ウィンドウメッセージです。

    詳しくはやっぱりご自身で調べていただくとして、

    私なりに大雑把に説明しますと、

    TextBoxにキー入力された場合、キー入力されたよ、というメッセージがTextBoxに飛んできます。

    TextBoxはそのメッセージに従って、一仕事します。

    一仕事とは、文字を表示する、などのことです。

    クリックされた場合も、クリックされたよ、というメッセージが飛んできます。

    全ての場合についてメッセージが飛んできます。

    その、飛んでくる全てのメッセージを処理できるプロシージャがWndProcです。

       

    キー入力されたよ、というメッセージは0x102(WM_CHAR)と決められています。

    そこで、そのメッセージが来た時に処理をしてあげればよいことになります。

    厳密にはWM_CHARはTextChangedと同等ではありません。ペーストされた場合などに対応しません。

     

    WndProcは万能ですが、積極的に使うものではありません。

    今回のフリガナの時のように、変換確定イベントが標準では用意されていない場合などに、

    しょうがなく、WndProcで処理します。あまり、わかりやすいコードになりませんからね。

    Code Snippet

       class MyTextBox:TextBox

        {
            //プライベートなメンバ
            private string findstring;

            private const int WM_CHAR = 0x102;
           
            //プロパティとして公開
            public string FindString
            {
                get
                {
                    return findstring;
                }
                set
                {
                    findstring = value;
                }

            }

            //イベント
            public delegate void FindEventHandler(object sender, FindStringEventArgs e);
            public event FindEventHandler FindStringEvent;


            protected override void OnTextChanged(EventArgs e)
            {
                //if (this.findstring != "")
                //{
                //    int pos=this.Text.IndexOf(this.findstring);
                //    if (pos >= 0)
                //    {
                //        this.BackColor = System.Drawing.Color.Red;
                //        FindStringEventArgs fsea=new FindStringEventArgs();
                //        fsea.FindPosition=pos;
                //        FindStringEvent(this,fsea);

                //    }
                //    else
                //    {
                //        this.BackColor = System.Drawing.Color.White;

                //    }
                //}
                //else
                //{
                //    this.BackColor = System.Drawing.Color.White;
                //}

                base.OnTextChanged(e);
            }

            protected override void WndProc(ref Message m)
            {
                base.WndProc(ref m);
               
                if (m.Msg == WM_CHAR)
                {
                    if (this.findstring != "")
                    {
                        int pos = (this.Text ).IndexOf(this.findstring);
                        if (pos >= 0)
                        {
                            this.BackColor = System.Drawing.Color.Red;
                            FindStringEventArgs fsea = new FindStringEventArgs();
                            fsea.FindPosition = pos;
                            FindStringEvent(this, fsea);

                        }
                        else
                        {
                            this.BackColor = System.Drawing.Color.White;

                        }
                    }
                    else
                    {
                        this.BackColor = System.Drawing.Color.White;
                    }
                }
               
               
            }
        }

     

     

    実行して処理を確認します。

     

    これで、一通りの説明は終わりです。

    フリガナコードをものにしてください。

    では、頑張ってください。
    2008年6月15日 1:20
  • はなはなはなさんありがとうございました。

    trepemiyaさんのコードを見ながら勉強していきます。

     

    2008年6月15日 9:03