FormのUserControl差替え時のコントロールイベント追加について

512 views
Skip to first unread message

masataka.s...@gmail.com

unread,
Sep 11, 2017, 1:13:25 AM9/11/17
to OpenTouryoProject

お世話になっております。

FormAのPanelコントロール上のUserControlを
例えばUserControl1からUserControl2へ差替えた場合、
UserControl2上のボタンコントロールクリックイベントの追加について質問です。

UserControl2には、UOCメソッドを記述しています。

差替えの方法は
1. Panel上のコントロールをClear
2. PanelにUserControl2をAdd
します。

MyBaseControllerWinクラスのaddControlEventメソッドに以下を追加し
--------------------------------------------------------------------------------------------------------------
// BUTTON
prefix = GetConfigParameter.GetConfigValue(FxLiteral.PREFIX_OF_BUTTON);
if (!string.IsNullOrEmpty(prefix))
{
    prefixAndEvtHndHt.Add(prefix, new System.EventHandler(this.Button_Click));
}
--------------------------------------------------------------------------------------------------------------
差替え後にUOC_CMNFormInitを呼びましたが追加がされませんでした。

このようにUserControlを差替えた場合
コントロールイベントの追加はどのようにすればいいでしょうか。

また、追加がされた場合、以前の登録されているイベントは削除するのでしょうか。

よろしくお願いします。

daisukenishino

unread,
Sep 11, 2017, 4:55:59 AM9/11/17
to OpenTouryoProject
度々お世話になります。

Windows FormsのUserControlのイベントハンドルのサンプルを眺めてみました。

本題は、下記の亜種かと思います(Windows FormsでUserControlを動的に変更する)。

FAQ - ASP.NET P層フレームワーク - Open 棟梁 Wiki
  P層イベント処理機能の対応コントロールを追加したい。 
  > 補足 > 動的に生成したコントロールのイベント ハンドラには、
    個別にコントロール共通のイベントハンドラをセットします。

イベントハンドラはprotectedになっているので、Formのイベントハンドラの中から仕掛けられると思います。

  protected void Button_Click(object sender, EventArgs e)

以下のthisが、UserControlのインスタンスであれば自動的に設定できると思います。

  // コントロール検索&イベントハンドラ設定
  RcFxCmnFunction.GetCtrlAndSetClickEventHandler2(this, prefixAndEvtHndHt, this.ControlHt);

・prefixAndEvtHndHtは作成する。
・ControlHtはoutパラメタ。

イベントハンドラの削除は(イベントハンドラの場合、メモリリークの原因にもならないので)必要は無いと思います。


2017年9月11日月曜日 14時13分25秒 UTC+9 masataka.s...@gmail.com:

masataka.s...@gmail.com

unread,
Sep 12, 2017, 12:55:12 AM9/12/17
to OpenTouryoProject
ありがとうございます。

UserControlインスタンスを作成後に、Formから
   protected void Button_Click(object sender, EventArgs e)
を仕掛けました。
が、イベントが発生しませんでした。

BaseControllerWinのGetMethodNameメソッドをみたところ
の部分で、UserControlImplementingMethodが空文字となっており
UserControl上のコントロールであると認識できていないようです。

また、
// コントロール検索&イベントハンドラ設定
  RcFxCmnFunction.GetCtrlAndSetClickEventHandler2(this, prefixAndEvtHndHt, this.ControlHt);
ですが、MyBaseControllerWinを継承したFormでは
GetCtrlAndSetClickEventHandler2がinternalとなっており
アクセス制限となりました。

よろしくお願いします。

2017年9月11日月曜日 17時55分59秒 UTC+9 daisukenishino:

daisukenishino

unread,
Sep 12, 2017, 4:29:57 AM9/12/17
to OpenTouryoProject
すいません、
ちょっと大掛かりな修正が必要になるかもしれません。
GetCtrlAndSetClickEventHandler2と、
GetUserControlをprotectedにしてすればイイのかなぁ...。

本日中に確認します。

2017年9月12日火曜日 13時55分12秒 UTC+9 masataka.s...@gmail.com:

daisukenishino

unread,
Sep 12, 2017, 6:28:54 AM9/12/17
to OpenTouryoProject
気合で修正しました。
ワリとキレイな修正になったと思います。


以下の修正を取り込み、
root/programs/C#/Frameworks/Infrastructure/Business/RichClient/Presentation/MyBaseControllerWin.cs
root/programs/C#/Frameworks/Infrastructure/Business/RichClient/Util/RcMyCmnFunction.cs
root/programs/C#/Frameworks/Infrastructure/Framework/RichClient/Presentation/BaseControllerWin.cs
root/programs/C#/Frameworks/Infrastructure/Framework/RichClient/Util/RcFxCmnFunction.cs

以下のようにユーザーコードを書きます。
root/programs/C#/Samples/WS_sample/WSClient_sample/WSClientWin2_sample/Form3.cs


masataka.s...@gmail.com

unread,
Sep 12, 2017, 8:38:34 AM9/12/17
to OpenTouryoProject
イベントを動かすことができるようになりました。
しかし、Application_ThreadExceptionによる例外通知エラーをキャッチするようになりました。
また明日調査をしますが、
問題の切り分けなどヒントがありましたら、よろしくお願いします。
エラーメッセージの画像を添付しています。

また、
不要になったUserControlは破棄しているのですが
LstUserControlもRemoveをしたほうがいいでしょうか。
(メモリリークやその他問題など)

2017年9月12日火曜日 19時28分54秒 UTC+9 daisukenishino:
Message.bmp

daisukenishino

unread,
Sep 12, 2017, 9:01:53 PM9/12/17
to OpenTouryoProject

(1)例外の件は、
ココで例外になっているようです。

この例外には見覚えがあって、loop中で、this.LstUserControlを更新すると、この例外が発生すると思います。

(2)UserControlの破棄については、
通常、親子関係の参照関係は、親がGCされれば子もGCされるのですが、今回の方式では、
親の生存線が継続するpatternだと思うので、子が適切にGCされるように、
this.LstUserControl.Remove(userControl32); 等を実行する必要があると思います。
また、this.ControlHtについても同様ですが、こちらは、ユーティリティ・メソッド内で
利用しているだけなので、this.ControlHt = nullか、this.ControlHt = new Dictionary<string, Control>();
などと、適宜、内容をクリアする必要があると思います。

masataka.s...@gmail.com

unread,
Sep 12, 2017, 11:56:09 PM9/12/17
to OpenTouryoProject
(1)について
Buttonクリックイベントの中でthis.LstUserControlを更新しているのが問題でしょうか。
   1. Buttonクリックイベント開始
   2. UserControlをFormのPanelに追加
   3. this.LstUserControlにAdd
   4. Buttonクリックイベント終了
となっています。

2017年9月13日水曜日 10時01分53秒 UTC+9 daisukenishino:

daisukenishino

unread,
Sep 13, 2017, 12:02:40 AM9/13/17
to OpenTouryoProject
> Buttonクリックイベントの中でthis.LstUserControlを更新しているのが問題でしょうか。

そうだと思います。

// 本画面中にメソッドがない。

のコードブロックなので。

UserControl上のP層イベント処理だからダメ何だと思います。
それ以外の場所ならイケルと思うのですが(適当な場所ありますか?)。


2017年9月13日水曜日 12時56分09秒 UTC+9 masataka.s...@gmail.com:

masataka.s...@gmail.com

unread,
Sep 13, 2017, 1:20:43 AM9/13/17
to OpenTouryoProject
考えましたが
適当な場所が見当たらないです。

Buttonクリックイベントのスレッドが終了したのを確認して
別のスレッド上でということができればいいのですが・・・

2017年9月13日水曜日 13時02分40秒 UTC+9 daisukenishino:

daisukenishino

unread,
Sep 13, 2017, 2:09:03 AM9/13/17
to OpenTouryoProject
> 別のスレッド上でということができればいいのですが・・・

チョット考えてみました。

スレッドはUIスレッドでもイイと思いますが、ただ、
関数のコール・スタックをCMN_Event_Handlerから逸らす必要があるので

masataka.s...@gmail.com

unread,
Sep 13, 2017, 2:59:10 AM9/13/17
to OpenTouryoProject
   1. UserControl1上のButtonクリックイベント開始
   2. Form上のPanelからUserCotnrol1を削除  <------
   3. Form上のPanelにUserControl2を追加   <------
   4. UserControl1上のButtonクリックイベント終了
上記の<----の部分でPanelのControlAddedとControlRemovedが発生しているため
UserControl上のButtonクリックイベント処理内??ということでしょうか

例外が発生しました。

2017年9月13日水曜日 15時09分03秒 UTC+9 daisukenishino:

daisukenishino

unread,
Sep 13, 2017, 3:43:20 AM9/13/17
to OpenTouryoProject
あぁ、もしかすると、最後のUserControlだからエラーになっていないだけですかね。
そして、イベント処理はSendMessageだからCMN_Event_Handlerを抜けてないかもしれません。

もうチョット調べてみます。

masataka.s...@gmail.com

unread,
Sep 13, 2017, 4:02:09 AM9/13/17
to OpenTouryoProject
すみません
よろしくお願いします。

2017年9月13日水曜日 16時43分20秒 UTC+9 daisukenishino:

daisukenishino

unread,
Sep 13, 2017, 4:07:21 AM9/13/17
to OpenTouryoProject
非同期呼び出しフレームワークを使ってみました。

コレでれドウでしょうか?

やっていることは、Control.Invokeと同じです。

SendMessage的に動くので、CMN_Event_Handlerを抜け、
messageループを経由してから実行されるハズです。


2017年9月13日水曜日 16時43分20秒 UTC+9 daisukenishino:

masataka.s...@gmail.com

unread,
Sep 13, 2017, 5:30:51 AM9/13/17
to OpenTouryoProject
ControlAddedの方もControlRemoved同様に修正しました。
が例外が発生してしまいます。

2017年9月13日水曜日 17時07分21秒 UTC+9 daisukenishino:

masataka.s...@gmail.com

unread,
Sep 13, 2017, 5:43:12 AM9/13/17
to OpenTouryoProject
LstUserControlをRemoveする時にすでにUserControlがDisposeされているため
オブジェクトにアクセスできずに例外が発生していました。

LstUserControlをRemoveする処理をはずすと
例外は発生しなくなりました。
非同期なしもやってみます。

2017年9月13日水曜日 18時30分51秒 UTC+9 masataka.s...@gmail.com:

masataka.s...@gmail.com

unread,
Sep 13, 2017, 5:46:28 AM9/13/17
to OpenTouryoProject
非同期なしでは例外が発生するため
非同期の処理は必要なようです。

2017年9月13日水曜日 18時43分12秒 UTC+9 masataka.s...@gmail.com:

masataka.s...@gmail.com

unread,
Sep 13, 2017, 6:06:40 AM9/13/17
to OpenTouryoProject
すみません。
Removeの件は関係ないです。

ControlRemovedの処理せず、ControlAdded時にLstUserControlにAddする処理だけで実行すると
新規のUserControlを追加する場合は例外は発生しませんが、
すでにLstUserControlにあるUserControlの別のインスタンスを追加すると
例外が発生します。

2017年9月13日水曜日 18時46分28秒 UTC+9 masataka.s...@gmail.com:

daisukenishino

unread,
Sep 13, 2017, 6:41:47 AM9/13/17
to OpenTouryoProject
確認ありがとうございます。

非同期呼び出しフレームワークを使用していますが、
別にバックグラウンド・スレッドは必要ないので、
Control.Invokeで書いた方がスマートな気がしたので
(要するに、CMN_Event_Handlerを抜けて
messageループを経由して実行されればイイ)

以下のように修正してみました。

daisukenishino

unread,
Sep 13, 2017, 6:45:10 AM9/13/17
to OpenTouryoProject
状況がちょっとわからなくなってきていますが、
LstUserControlのAddもRemoveも、CMN_Event_Handlerを
抜けてから実行する必要があるのではないでしょうか?

そのために、Control.BeginInvokeを使用すれば解決するのでは?と思っています。

2017年9月13日水曜日 19時06分40秒 UTC+9 masataka.s...@gmail.com:

masataka.s...@gmail.com

unread,
Sep 13, 2017, 9:56:21 PM9/13/17
to OpenTouryoProject
遅くなってしまいすみません。

昨日はいろいろと勘違いをして、状況をお伝えしていました。
改めて確認して
Control.BeginInvokeを使用することで解決をしました。
UserControlが入れ子になっている場合を試していないのですが、
この部分を再起的に呼び出すようさらに修正すればいいでしょうか。


2017年9月13日水曜日 19時45分10秒 UTC+9 daisukenishino:

daisukenishino

unread,
Sep 13, 2017, 10:36:35 PM9/13/17
to OpenTouryoProject
いえいえ。解決して良かったです。

質問の子UserControlがある場合の件は、

取り敢えず、

ControlRemovedが検知するのは、ルートUserControlだけなので、
ルートUserControlを起点に子UserControlを再帰検索し、
子UserControlがあったら、それもLstUserControlから削除する。

という処理を書く必要があると思います。

2017年9月14日木曜日 10時56分21秒 UTC+9 masataka.s...@gmail.com:

masataka.s...@gmail.com

unread,
Sep 14, 2017, 6:50:43 AM9/14/17
to OpenTouryoProject
お世話になっています。
2点追加で質問があります。

(1)
ControlAddedとControlRemovedで
ルートUserControlを起点に子UserControlを再帰検索し、
子UserControlがあったら・・・という処理を書きました。
-------------------------------------------------------------------------
        /// <summary>LstUserControlに追加する</summary>
        /// <param name="cntrl">UserControl</param>
        private void UserControlAdded(UserControl cntrl)
        {
            // 動的ロード後のコントロール検索&イベントハンドラ設定
            this.LstUserControl.Add(cntrl);

            // 子要素にユーザーコントロールがあるかチェック
            this.CheckUserControls(cntrl);
        }

        /// <summary>子コントロールのUserControlをチェック</summary>
        /// <param name="cntrl">UserControl</param>
        private void CheckUserControls(Control cntrl)
        {
            // 子コントロール毎に
            foreach (Control childCtrl in cntrl.Controls)
            {
                switch (childCtrl)
                {
                    // ユーザーコントロールがあれば
                    case UserControl userCtrl:
                        // UserControlAddedを再起する。
                        UserControlAdded(userCtrl);
                        break;
                    case null:
                        break;
                    default:
                        // 子コントロールがある時
                        if (childCtrl.Controls.Count != 0)
                        {
                            // 再起してさらに子コントロールもチェック
                            CheckUserControls(childCtrl);
                        }
                        break;
                }
            }
        }
-------------------------------------------------------------
これを追加すると、気になるレベルで
画面表示までの処理に時間がかかるようになりました。

Open棟梁でも
RcFxCmnFunction.GetCtrlAndSetClickEventHandler2
で全コントロールを見ているので
同じループ内に組み込むことはできますか?

(2)
LstUserControlに無事に子UserControlをAddできた後の
子UserControl上のボタン押下時の処理についてです。
ボタン押下時処理は、子UserControlに記述されています。

以下のような場合
ルートUserControl上のPanel上の子UserControl

https://github.com/OpenTouryoProject/OpenTouryo/blob/develop/root/programs/C%23/Frameworks/Infrastructure/Framework/RichClient/Presentation/BaseControllerWin.cs#L912

FindUCControlの部分で、ルートUserControl上の子UserControlは検索せずにスルーするのですが

Panelなどのコントロールにさらに入れ子になっている場合

retが取得されretがnullでなくなるため

ルートUserControl上にあると判定されてしまっているためか

その後にUOCメソッドをルートUserControlから探しており

ボタンイベント処理が発生していません。


おそらく上記であっているとは思いますが

確認のほどよろしくお願いします。



2017年9月14日木曜日 11時36分35秒 UTC+9 daisukenishino:

daisukenishino

unread,
Sep 14, 2017, 7:21:52 AM9/14/17
to OpenTouryoProject
明日こちらでも、UserControlネスト時の処理を書いてみます。

なんとなくですが、
Add処理はルートからの再起で自動的に行われているので、問題は、Remove処理だと思っています。

これは、もともと、親Formと同じライフサイクルでUserControlも消えるだけと考えていましたが、
今回の処理方式では、UserControlを切り替える方式のため、親Formはずっと生きているので
UserControlのクリア処理が必要になってきたためです。

今回、必要なのは、groupBox3_ControlRemovedの際に、RemoveされたルートUserControlの
子UserControlを検索し、それも含めて、this.LstUserControlから削除する処理だと考えています。

daisukenishino

unread,
Sep 14, 2017, 11:31:38 PM9/14/17
to OpenTouryoProject
すいません、BaseControllerWin.csがバグッてました。

↓で修正できたと思います。ご確認下さい。
他はネストしたUserControlの動的な追加/削除のサンプルです。

masataka.s...@gmail.com

unread,
Sep 15, 2017, 2:23:19 AM9/15/17
to OpenTouryoProject
>他はネストしたUserControlの動的な追加/削除のサンプルです。
ありがとうございます。参考にさせていただきました。

ネストしたUserControl上のボタンも反応するのを確認しました。
修正ありがとうございました。


2017年9月15日金曜日 12時31分38秒 UTC+9 daisukenishino:

daisukenishino

unread,
Sep 17, 2017, 8:27:13 AM9/17/17
to OpenTouryoProject
サンプル・プログラムのモジュールの追加漏れがあったので、その追加とVB版へのマージを行いました。

あと、UI系はメモリリークが怖いので調査方法をチョット調べときます。
# 久しぶりにWinDbg使ってみたんですが動かなかったので。

.NETのメモリ・リーク - マイクロソフト系技術情報 Wiki
  > SOS.dll

daisukenishino

unread,
Sep 19, 2017, 10:52:26 PM9/19/17
to OpenTouryoProject
なんとかできたので、



に手順をメモしました。
これで、リーク等が無いか確認しながら開発を遂行できると思います。

2017年9月17日日曜日 21時27分13秒 UTC+9 daisukenishino:
Reply all
Reply to author
Forward
0 new messages