- PR -

ファンクションキーコントロールについて

1
投稿者投稿内容
マリン
常連さん
会議室デビュー日: 2006/05/28
投稿数: 41
投稿日時: 2006-05-28 16:37
VB2005でGrapeCityのInputManに含まれているファンクションキーコントロールのようなものを自作したいと思っています。
@IT内のユーザビリティを高めるファンクション・バーの実装が参考になるかと思ったのですが、ファンクションキーの検出をメニューバーのショートカットキーに頼る作りだったようなので私が求めているものとはちょっと違うようでした。そこで…

  1. Windowsコントロールライブラリプロジェクトを新規作成
  2. 継承元をSystem.Windows.Forms.ToolStripに変更
  3. InitializeComponentのMe.AutoScaleModeが無効なのでコメントアウト
  4. コンストラクタでInitializeComponent呼び出し前にMyBase.New()を追加
  5. デザイン画面である程度決まったファンクションキー設定をItemsコレクションに設定
  6. Private WithEvents _ParentForm As Formを宣言
  7. ParentChangedイベントでMe._ParentForm = Me.FindForm()とする
  8. _ParentForm_KeyDownでファンクションキーを検出して独自イベントを発行
  9. このコントロールを貼り付けたフォーム側では↑のイベントに対して処理コードを書くだけ

上記のようにしてみたところフォームに直接配置されている場合はうまく検出できるものの、Panel等のコンテナ上に配置された場合にはParentChangedのタイミングではFindFormがNothingなためうまくいきません。
GrapeCityのファンクションキーコントロールだとコンテナ上に配置してもコントロール独自のFunctionKeyPressイベントが発生してくれるのですがこれはどのような作りで検出・実現しているのでしょうか?
じゃんぬねっと
ぬし
会議室デビュー日: 2004/12/22
投稿数: 7811
お住まい・勤務地: 愛知県名古屋市
投稿日時: 2006-05-28 16:56
引用:

マリンさんの書き込み (2006-05-28 16:37) より:

上記のようにしてみたところフォームに直接配置されている場合はうまく検出できるものの、Panel等のコンテナ上に配置された場合にはParentChangedのタイミングではFindFormがNothingなためうまくいきません。


CreateHandle と両方で実装する必要があると思います。
ParentChange は親が変更された時のためのもので、通常の場合は CreateHandle で行うものだと思います。

_________________
C# と VB.NET の入門サイト
じゃんぬねっと日誌
R・田中一郎
ぬし
会議室デビュー日: 2005/11/03
投稿数: 979
投稿日時: 2006-05-28 17:54
引用:

マリンさんの書き込み (2006-05-28 16:37) より:

VB2005でGrapeCityのInputManに含まれているファンクションキーコントロールのようなものを自作したいと思っています。


InputMan は有名ですよね。どういうものかは使ったことがないのでわかりませんが。
ファンクションキーコントロールは、僕も自作して多用しています。
ボタンコントロールを12ヶ並べて、キー入力とマウスクリックに対応させてます。

引用:

マリンさんの書き込み (2006-05-28 16:37) より:

GrapeCityのファンクションキーコントロールだとコンテナ上に配置してもコントロール独自のFunctionKeyPressイベントが発生してくれるのですがこれはどのような作りで検出・実現しているのでしょうか?


InputMan は使ったことがないですし、中で何をしているのかは分かりませんが、僕がやっているやり方が参考になれば・・・キーを受け取っている部分のクラスです。

以前は VB で書いたのですが、その後 C# に移行して諸々デバッグしたので、C# しか残っていません。

コード:
    #region KeyPushAnnounce Class
    //**************************************************************************************************
    // KeyPushAnnounce Class
    // Windows から直接キー押下メッセージを受け取ってイベントを発行する
    //**************************************************************************************************
    internal class KeyPushAnnounce
    {
        #region Constructor
        //**************************************************************************************************
        // Constructor
        //**************************************************************************************************
        private int[]   checkKeyArray;
        private int     prevKey;
        private Timer   timer;

        // 仮想キーの押下状態を取得する
        [DllImport("user32", EntryPoint = "GetKeyState")]
        private static extern short GetKeyState(int nVirtKey);

        private const int VK_SHIFT  = 0x10;
        private const int VK_CONTROL  = 0x11;
        private const int VK_F1 = 0x70;
        private const int VK_F2 = 0x71;
        private const int VK_F3 = 0x72;
        private const int VK_F4 = 0x73;
        private const int VK_F5 = 0x74;
        private const int VK_F6 = 0x75;
        private const int VK_F7 = 0x76;
        private const int VK_F8 = 0x77;
        private const int VK_F9 = 0x78;
        private const int VK_F10 = 0x79;
        private const int VK_F11 = 0x7A;
        private const int VK_F12 = 0x7B;

        public KeyPushAnnounce() {
            this.checkKeyArray = new int[] {
		        VK_SHIFT, VK_CONTROL,
		        VK_F1, VK_F2, VK_F3, VK_F4, VK_F5, VK_F6, VK_F7, VK_F8, VK_F9, VK_F10, VK_F11, VK_F12
            };
            this.timer = new Timer();
            this.timer.Interval = 100;
            this.timer.Enabled = true;
            this.timer.Start();
            this.timer.Tick += new EventHandler(this.timer_Tick);
        }
        #endregion

        #region KeyUp Event
        //**************************************************************************************************
        // KeyUp Event
        //**************************************************************************************************
        public event KeyEventHandler KeyUp;

        protected virtual void OnKeyUp(KeyEventArgs e) {
            if (this.KeyUp != null) {
                this.KeyUp(this, e);
            }
        }

        #endregion

        #region Event Handler
        //**************************************************************************************************
        // Event Handler
        //**************************************************************************************************
        private void timer_Tick(object sender, EventArgs e) {
            this.timer.Stop();
            this.PushCheck();
            this.timer.Start();
        }

        #endregion

        #region PushCheck
        //**************************************************************************************************
        // PushCheck
        //**************************************************************************************************
        private void PushCheck() {
            int pushKey = this.PushKeyGet();
            if (this.prevKey != 0) {
                if (this.prevKey != pushKey) {
                    this.OnKeyUp(new KeyEventArgs(this.KeyCodeConvert(this.prevKey)));
                }
            }
            this.prevKey = pushKey;
        }

        private Keys KeyCodeConvert(int value) {
            switch (value) {
                case VK_SHIFT:		return Keys.Shift;
                case VK_CONTROL:	return Keys.Control;
                case VK_F1:			return Keys.F1;
                case VK_F2:			return Keys.F2;
                case VK_F3:			return Keys.F3;
                case VK_F4:			return Keys.F4;
                case VK_F5:			return Keys.F5;
                case VK_F6:			return Keys.F6;
                case VK_F7:			return Keys.F7;
                case VK_F8:			return Keys.F8;
                case VK_F9:			return Keys.F9;
                case VK_F10:		return Keys.F10;
                case VK_F11:		return Keys.F11;
                case VK_F12:		return Keys.F12;
                default:            return Keys.None;
            }        
        }

        private int PushKeyGet() {
            foreach (int key in this.checkKeyArray) {
                if (GetKeyState(key) < 0) {
                    return key;
                }
            }
            return 0;
        }

        #endregion
    }
    #endregion



これだと何でもかんでも反応しちゃうので、実行中のデバッグでファンクションキーが押せなくなるのですがw
マリン
常連さん
会議室デビュー日: 2006/05/28
投稿数: 41
投稿日時: 2006-05-28 18:19
引用:

じゃんぬねっとさんの書き込み (2006-05-28 16:56) より:

CreateHandle と両方で実装する必要があると思います。
ParentChange は親が変更された時のためのもので、通常の場合は CreateHandle で行うものだと思います。



その後、自動生成されるフォームのInitializeComponent内を覗いてみたら、まず親コンテナのインスタンスでControls.Addされており、それからそのコンテナがフォームにControls.AddされていたのでParentChangedのタイミングではFindFormがNothingになるのも当然だと思い、InitializeComponentはエディタで変更するな、とありましたが試しにフォームに親コンテナがControls.Addされた後で今回作ったコントロールが親コンテナにControls.Addされるように入れ替えてみたところ、ParentChanged内のFindFormでも見つかったのでこんなやり方でいいものか悩んでおりました。

教えていただいた通り、ParentChangedではなくCreateHandleの方をオーバーライドしてその中でMe._ParentForm = Me.FindForm()としたらコンテナ上に配置しても検出できるようになりました。
一応の確認ですが、Controls.Addされた段階ではまだフォームや親コンテナとのインスタンス上で関連付け(?)がなされただけであり、実際にフォーム上で実体化する(APIで言うところのCreateWindowされる?)タイミングがCreateHandleである、という理解でだいたいよろしいでしょうか?

とりあえずこれでフォームのデザイナで配置するだけで簡単にファンクションキーを検出・処理実装を書けるコントロールができそうです。
ありがとうございました。
じゃんぬねっと
ぬし
会議室デビュー日: 2004/12/22
投稿数: 7811
お住まい・勤務地: 愛知県名古屋市
投稿日時: 2006-05-28 20:16
引用:

マリンさんの書き込み (2006-05-28 18:19) より:

一応の確認ですが、Controls.Addされた段階ではまだフォームや親コンテナとのインスタンス上で関連付け(?)がなされただけであり、実際にフォーム上で実体化する(APIで言うところのCreateWindowされる?)タイミングがCreateHandleである、という理解でだいたいよろしいでしょうか?


これは、Controls に Add される前に CreateControl メソッドで、
強制的にコントロールを作成してみると、イメージが掴みやすいと思います。

簡単に言えば、中途半端な状態ということです。

それと、すみません。(*_ _)
CreateHandle メソッドをオーバーライドするのではなく、
OnHandleCreated メソッドをオーバーライドすべきです。。。

念のためですが、いずれにしても「基底メソッド」の呼び出しを真っ先に行うようにしてください。

_________________
C# と VB.NET の入門サイト
じゃんぬねっと日誌
マリン
常連さん
会議室デビュー日: 2006/05/28
投稿数: 41
投稿日時: 2006-05-28 21:35
引用:

じゃんぬねっとさんの書き込み (2006-05-28 20:16) より:

それと、すみません。(*_ _)
CreateHandle メソッドをオーバーライドするのではなく、
OnHandleCreated メソッドをオーバーライドすべきです。。。

念のためですが、いずれにしても「基底メソッド」の呼び出しを真っ先に行うようにしてください。



補足ありがとうございます。CreateHandleされる処理をオーバーライドするよりもOnHandleCreatedで親フォームを探すのが妥当なタイミングですね。早速修正いたしました。

それと投稿時間差により、R・田中一郎さんのご回答をすっかり見落としておりました。すみません。
InputManはVB6で開発していた頃に使っていたのですが、なにしろ高価なものなのでよほどInputManでなくては無理ということがなければVB2005からはなるべく自作コントロールでなんとかしようと目論んでおりました(笑)
VC++開発経験もあるので提示していただいたC#ソースもなんとか読めましたが、じゃんぬねっとさんに教えていただいた通りOnHandleCreatedをオーバーライドして親フォームを拾うことができ、簡単に実現できましたのでこの方向で機能追加していこうと思います。

ご回答ありがとうございました。
R・田中一郎
ぬし
会議室デビュー日: 2005/11/03
投稿数: 979
投稿日時: 2006-05-29 09:10
引用:

マリンさんの書き込み (2006-05-28 21:35) より:

それと投稿時間差により、R・田中一郎さんのご回答をすっかり見落としておりまし


解決しようとしている方向性が違うので、アップするの迷ったんですが(^▽^;)マリンさんや他の方に何かのヒントになればと思ってアップしました。

(逆に、今回のやり方は僕も参考になりました)
1

スキルアップ/キャリアアップ(JOB@IT)