- PR -

動的生成コントロールの判定法

1
投稿者投稿内容
Strada
会議室デビュー日: 2006/09/28
投稿数: 4
投稿日時: 2006-09-28 07:40
C++/CLIで下記の様なプログラムを作成したく色々調べていますが、良い方法が思い当たりませんので御指導ください。

1. あるFormに複数個のButtonコントロールを配置する。
2. 複数種のカスタムコントロールが用意されている。
3. 実行時にあるButtonのクリックイベントで、あるカスタムコントロールを動的に生成する。
4. 一つのカスタムコントロールは、位置と大きさを変えて複数回生成されることがある。
5. カスタムコントロールは、生成後、FormからRemoveされることがある。

【実現したいこと】
画面上のアクティブなカスタムコントロールを判定し、そのプロパティを書き替えたり
FormからRemoveするなどを行いたい。

【困っていること】
現在アクティブなコントロールが、同一カスタムコントロールのうちどれであるかを
判定する決め手が無く困っています。
4.のように同一カスタムコントロールを多重で呼び出すと、Form.Controls.Nameには
全てこのコントロールのインスタンス名が同一名で記入されており、名前で区別する
ことができません。また実行中に動的に発生されるため、予め名前を変えて
インスタンスを作成することもできません。
またC++/CLIでは、Form.Control.Collectionのプロパティの中で何故かItemだけが公開
されておらず、コントロールのインデックスで区別することもできません。
また仕様上、座標値や大きさが必ずしも異なるとは限らず、これも判断基準としては
完全ではありません。

【知りたいこと】
Collection.CollectionBaseクラスを継承したクラスで自前のコントロール配列を作り
Indexをpublicメンバにすれば管理ができそうに思いますが、二重管理になりいかにも
冗長です。これしか方法がないものでしょうか?
それとも何か私が誤解しているのでしょうか?
じゃんぬねっと
ぬし
会議室デビュー日: 2004/12/22
投稿数: 7811
お住まい・勤務地: 愛知県名古屋市
投稿日時: 2006-09-28 09:01
引用:

Stradaさんの書き込み (2006-09-28 07:40) より:

4.のように同一カスタムコントロールを多重で呼び出すと、Form.Controls.Nameには全てこのコントロールのインスタンス名が同一名で記入されており、名前で区別することができません。また実行中に動的に発生されるため、予め名前を変えてインスタンスを作成することもできません。またC++/CLIでは、Form.Control.Collectionのプロパティの中で何故かItemだけが公開されておらず、コントロールのインデックスで区別することもできません。


質問の真意を読み取れていない可能性が大ですが、
"名前" や "インデックス" ではなく、インスタンスそのもので判定するというのはダメなのでしょうか?

_________________
C# と VB.NET の入門サイト
じゃんぬねっと日誌
きくちゃん
ぬし
会議室デビュー日: 2003/08/01
投稿数: 854
お住まい・勤務地: 都内某所
投稿日時: 2006-09-28 11:29
Stradaさん、こんにちは。

引用:

またC++/CLIでは、Form.Control.Collectionのプロパティの中で何故かItemだけが公開
されておらず、コントロールのインデックスで区別することもできません。


Controls[0] とか Controls->default[0] とかでコレクションメンバにアクセスできませんか?

Item プロパティ
一郎
ぬし
会議室デビュー日: 2002/10/11
投稿数: 1081
投稿日時: 2006-09-28 14:10
変更したいコントロールへのハンドルを持っているわけじゃないんですよね。
(もし持っているなら、そのハンドルの指すオブジェクトに働きかけてやればいいわけですから)
現在アクティブなコントロールへのハンドルが欲しいということでいいですか?
Control.ContainsFocusというプロパティでいけそうじゃないですかね。
コード:
void timer1_Tick(Object^ sender, EventArgs^ e){
	for each(Control^ ctrl in this->Controls){
		if(ctrl->ContainsFocus)ctrl->BackColor = Color::Green;
	}
}


こんな感じでやると、ある時間にフォーカスがあるコントロールの背景色が変わります。

それとも、ボタンを押した時に、その直前にアクティブだったコントロールのハンドルが欲しいと言うことですか?
Strada
会議室デビュー日: 2006/09/28
投稿数: 4
投稿日時: 2006-09-28 21:54
じゃんぬねっとさん、きくちゃんさん、一郎さん、即刻アドバイスを頂き大変ありがとうございました。

お陰様で解決致しました。.NET初心者なもので大変助かりました。
分かり難い質問で申し訳ありませんでした。

整理して言えば、動的に生成された同一コントロールがForm上に複数配置されているとき、Form側から、その中のどれが現在フォーカスを持っているのか、そのコントロールを特定する方法を知りたかったのです。Form側に「コントロールのフォーカスが移動した」みたいなイベントが無く、ハンドルが取得できないため「一意的」なコントロールの特定をどうすればよいか、色々遠回りをしていました。

仰るとおり、Control[i]で、コレクションメンバへのアクセスはできますが、逆にフォーカスを持つコントロールのハンドルが取得できなければItemにアクセスしてiを特定することができません。そのハンドル取得法がわからず困っていたのです。

というわけで、一郎さんから教えて頂いた、timerを用いてForm側から強制的にイベントを定期的に発生し、ControlFocusプロパティでそのハンドルを所得するという方法で解決致しました。
一郎
ぬし
会議室デビュー日: 2002/10/11
投稿数: 1081
投稿日時: 2006-09-29 11:03
タイマーを使ったのは、ボタンのクリックのイベントなどで説明しようとするとクリックした直前のコントロールへのハンドルを保持しておかなくてはならなくて面倒だったからです。
タイマーの時間間隔よりも短い時間で別のコントロールにフォーカスを移動されるとフォーカスが移動したことが分かりませんよね。
かといって時間間隔を短くすると無駄な処理が増えますし。

もし「コントロールのフォーカスが移動した」みたいなイベントが欲しいのであれば、全ての子コントロールのEnterイベントを受けて処理するなどの方法があります。
面倒なら「コントロールのフォーカスが移動した」みたいなイベントを作るとか。
ちょっと作ってみました。
コード:

public ref class BaseForm : Form{

public:
event ControlEventHandler^ MoveFocus;


protected:
virtual void OnMoveFocus(ControlEventArgs^ e){
MoveFocus(this, e);
}

virtual void OnControlAdded(ControlEventArgs^ e) override{
Form::OnControlAdded(e);
e->Control->Enter += gcnew EventHandler(this, &BaseForm::Controls_Enter);
}

virtual void OnControlRemoved(ControlEventArgs^ e) override{
Form::OnControlRemoved(e);
e->Control->Enter -= gcnew EventHandler(this, &BaseForm::Controls_Enter);
}

private:
void Controls_Enter(Object^ sender, EventArgs^ e){
OnMoveFocus(gcnew ControlEventArgs(dynamic_cast<Control^>(sender)));
}

};


こんな感じでトリビアなイベントMoveFocusを作ります。(実際使うのはOnMoveFocusメソッドの方ですけどね)
処理内容としては、コントロールを追加された時にそれのEnterイベントを受けるイベントハンドラを設定してます。

使う方では、このBaseFormを継承して、
コード:

protected:
virtual void OnMoveFocus(ControlEventArgs^ e) override{
BaseForm::MoveFocus(this, e);

Control^ ctrl = e->Control;

if(TextBox::typeid->IsInstanceOfType(ctrl)){
TextBox^ txt = dynamic_cast<TextBox^>(ctrl);
txt->Text = "フォーカス来た";
}
else if(Button::typeid->IsInstanceOfType(ctrl)){
Button^ btn = dynamic_cast<Button^>(ctrl);
btn->BackColor = Color::Green;
}
}


OnMoveFocusメソッドをオーバーライドします。(MoveFocusイベントを受けてもいいですけど、私はこっちの方が好み)
この例では、フォーカスが当たったコントロールがTextBoxならTextを変えて、Buttonなら背景色を変えています。

[ メッセージ編集済み 編集者: 一郎 編集日時 2006-09-29 11:17 ]
一郎
ぬし
会議室デビュー日: 2002/10/11
投稿数: 1081
投稿日時: 2006-09-29 11:21
一応MoveFocusのイベントハンドラで処理する場合のコードも書いときますね。
コード:
private:
	void Form1_MoveFocus(Object^ sender, ControlEventArgs^ e){
		Control^ ctrl = e->Control;

		if(TextBox::typeid->IsInstanceOfType(ctrl)){
			TextBox^ txt = dynamic_cast<TextBox^>(ctrl);
			txt->Text = "フォーカス来た";
		}
		else if(Button::typeid->IsInstanceOfType(ctrl)){
			Button^ btn = dynamic_cast<Button^>(ctrl);
			btn->BackColor = Color::Green;
		}
	}


Page_Loadは使わないでOnLoadを使いたいという私のような人じゃないと、こっちの方が好まれるかも。

しかし、.NET初心者なのにC++/CLIって大変でしょう。
使えるようになるには、C++と.NETと両方の知識が必要になりますもんね。
1

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