C# と VB.NET の質問掲示板

わんくま同盟

ASP.NET、C++/CLI、Java 何でもどうぞ

C# と VB.NET の入門サイト

ツリー一括表示

動的に作成した大量のボタンのイベント分岐について /なっとう (18/07/11(Wed) 09:45) #87881
Re[1]: 動的に作成した大量のボタンのイベント分岐について /WebSurfer (18/07/11(Wed) 11:15) #87884
Re[1]: 動的に作成した大量のボタンのイベント分岐について /774RR (18/07/11(Wed) 11:48) #87886
  ├ Re[2]: 動的に作成した大量のボタンのイベント分岐について /なっとう (18/07/11(Wed) 11:47) #87885
  │└ Re[3]: 動的に作成した大量のボタンのイベント分岐について /WebSurfer (18/07/11(Wed) 12:07) #87888
  └ Re[2]: 動的に作成した大量のボタンのイベント分岐について /なっとう (18/07/11(Wed) 12:05) #87887
    ├ Re[3]: 動的に作成した大量のボタンのイベント分岐について /なっとう (18/07/11(Wed) 13:44) #87891
    │├ Re[4]: 動的に作成した大量のボタンのイベント分岐について /WebSurfer (18/07/11(Wed) 19:59) #87898
    │└ Re[4]: 動的に作成した大量のボタンのイベント分岐について /WebSurfer (18/07/12(Thu) 18:55) #87910
    └ Re[3]: 動的に作成した大量のボタンのイベント分岐につい /PANG2 (18/07/11(Wed) 13:30) #87890
      └ Re[4]: 動的に作成した大量のボタンのイベント分岐につい /PANG2 (18/07/11(Wed) 13:53) #87892
        └ Re[5]: 動的に作成した大量のボタンのイベント分岐につい /なっとう (18/07/11(Wed) 14:01) #87893 解決済み
          └ Re[6]: 動的に作成した大量のボタンのイベント分岐につい /なちゃ (18/07/11(Wed) 14:44) #87894
            └ Re[7]: 動的に作成した大量のボタンのイベント分岐につい /774RR (18/07/13(Fri) 06:58) #87915
              └ Re[8]: 動的に作成した大量のボタンのイベント分岐につい /なちゃ (18/07/14(Sat) 02:49) #87929
                └ Re[9]: 動的に作成した大量のボタンのイベント分岐につい /shu (18/07/14(Sat) 07:24) #87930


親記事 / ▼[ 87884 ] ▼[ 87886 ]
■87881 / 親階層)  動的に作成した大量のボタンのイベント分岐について
□投稿者/ なっとう (1回)-(2018/07/11(Wed) 09:45:50)

分類:[C#] 

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

VS C# 2017を使用してWindowsフォームアプリケーション開発の練習中です。

List<Button> Btnを宣言し、それぞれのボタンに各種フォームをShowするためのイベントを割り当てたいと考えております。
ボタンの配置についてはFlowLayoutPanelを使用して自動整列させています。

下記のコードでは、どのボタンを押してもForm2しか表示することが出来ませんが、
次のようにして、ボタンの追加と同時にクラスを指定することは可能でしょうか。
AddButton("フォーム1", Form2のクラス)

現在の構文
private List<System.Windows.Forms.Button> Btns = new List<Button>();

private void AddButton(string name, ここにフォームクラスを渡す)
{
var button1 = new System.Windows.Forms.Button();
button1.Location = new System.Drawing.Point(3, 3);
button1.Name = name;
button1.Size = new System.Drawing.Size(75, 23);
button1.TabIndex = 0;
button1.Text = name;
button1.UseVisualStyleBackColor = true;
button1.Click += new System.EventHandler(this.button_Click);
Btns.Add(button1);
this.flowLayoutPanel1.Controls.Add(button1);
}

private void button_Click(object sender, EventArgs e)
{
//↓本当はボタンごとに異なるフォームをShowしたい
var frm = new Form2();
frm.Show();
}

以上、よろしくお願いいたします。

[ □ Tree ] 返信 編集キー/

▲[ 87881 ] / 返信無し
■87884 / 1階層)  Re[1]: 動的に作成した大量のボタンのイベント分岐について
□投稿者/ WebSurfer (1550回)-(2018/07/11(Wed) 11:15:02)
No87881 (なっとう さん) に返信

Form2 〜 FormX クラスは定義済・ビルド済みで、Button.Name などに応じて当該 Form を
new して Show すればいいのであれば、switch 文を使って分ければよさそうな気がします。

それではダメなのでしょうか?
[ 親 87881 / □ Tree ] 返信 編集キー/

▲[ 87881 ] / ▼[ 87887 ]
■87886 / 1階層)  Re[1]: 動的に作成した大量のボタンのイベント分岐について
□投稿者/ 774RR (615回)-(2018/07/11(Wed) 11:48:05)
普通に Designer で各ボタンを事前に生成しておき、各ボタンに別ハンドラを生成すればそれで済むわけですが、
そうしたくない理由がある?

private void button2_Click(object sender, EventArgs e) { Form2 を表示 }
private void button3_Click(object sender, EventArgs e) { Form3 を表示 }
private void button4_Click(object sender, EventArgs e) { Form4 を表示 }


Tag プロパティに格納しておいた値でほげほげするとか。

[ 親 87881 / □ Tree ] 返信 編集キー/

▲[ 87884 ] / ▼[ 87888 ]
■87885 / 2階層)  Re[2]: 動的に作成した大量のボタンのイベント分岐について
□投稿者/ なっとう (2回)-(2018/07/11(Wed) 11:47:04)
回答ありがとうございます。

Form2 〜 FormXですが、これからどんどんフォームが増えていく予定です。
ボタンを大量に列挙するフォームはメニュー画面になるイメージです。

今後フォームが増えたときに、AddButton()とは別に、Clickイベント内のSwitchにも追加しなければならないというのが面倒に感じたため、AddButtonのパラメータにフォームを渡せないものかと考えた次第です。
[ 親 87881 / □ Tree ] 返信 編集キー/

▲[ 87885 ] / 返信無し
■87888 / 3階層)  Re[3]: 動的に作成した大量のボタンのイベント分岐について
□投稿者/ WebSurfer (1551回)-(2018/07/11(Wed) 12:07:49)
No87885 (なっとう さん) に返信

> Form2 〜 FormXですが、これからどんどんフォームが増えていく予定です。

動的に(=プログラムで自動的に)増えていくわけではなくて、増やす FormX+1 は自力で
コードを書くのですよね。

であれば、Form1 のコードにその時に手を加えれば済むことだと思うのですが、(そも
そも、Button も動的に増やす意味はなくて、ツールボックスからドラッグ&ドロップする
方がよほど簡単だと思うのですが)、そうしないのは単に「面倒」という理由ですか? 

私が質問者さんが「面倒」と思うポイントを理解できてないのかもしれませんが、なんと
なく本末転倒的な気がするのですが・・・
[ 親 87881 / □ Tree ] 返信 編集キー/

▲[ 87886 ] / ▼[ 87891 ] ▼[ 87890 ]
■87887 / 2階層)  Re[2]: 動的に作成した大量のボタンのイベント分岐について
□投稿者/ なっとう (3回)-(2018/07/11(Wed) 12:05:24)
ありがとうございます。

特定条件でAddButtonする/しないの分岐がありますので動的に作成したいです。
フォームの増減が非常に多くなる見込みなので、毎回デザイナでボタン作ってイベント書いてしていると結構手間がかかりそうなので、前述のような方法は無いのかと思った次第です。

>Tag プロパティに格納しておいた値でほげほげするとか。
それでも構いませんので、可能でしたら教えて頂けないでしょうか。
[ 親 87881 / □ Tree ] 返信 編集キー/

▲[ 87887 ] / ▼[ 87898 ] ▼[ 87910 ]
■87891 / 3階層)  Re[3]: 動的に作成した大量のボタンのイベント分岐について
□投稿者/ なっとう (4回)-(2018/07/11(Wed) 13:44:48)
仰る通りFormXは自力で作ります。試行錯誤しながら追加・コピペ・削除を繰り返すと思います。

>であれば、Form1 のコードにその時に手を加えれば済むことだと思うのですが
前に述べたように、条件に応じてボタンのON/OFFが必要になります。
ON/OFFは設定ファイル等によって制御することになるかもしれません。

「面倒」というのは、デザイナとコードをあっちこっち移動して追いかけるのに慣れていないからそう感じるのだと思います。
[ 親 87881 / □ Tree ] 返信 編集キー/

▲[ 87891 ] / 返信無し
■87898 / 4階層)  Re[4]: 動的に作成した大量のボタンのイベント分岐について
□投稿者/ WebSurfer (1553回)-(2018/07/11(Wed) 19:59:05)
No87891 (なっとう さん) に返信

> 仰る通りFormXは自力で作ります。試行錯誤しながら追加・コピペ・削除を繰り返すと思います。
> 
> >であれば、Form1 のコードにその時に手を加えれば済むことだと思うのですが
> 前に述べたように、条件に応じてボタンのON/OFFが必要になります。
> ON/OFFは設定ファイル等によって制御することになるかもしれません。

上の質問者さんのレスを読むと、私の提案を理解いただけてないような気がしますので補足します。

一番最初の私のレスで、

> Form2 〜 FormX クラスは定義済・ビルド済みで、Button.Name などに応じて当該 Form を 
> new して Show すればいいのであれば、switch 文を使って分ければよさそうな気がします。

と言った switch 文とは以下のようにすることです。

private void Button_Click(object sender, EventArgs e)
{
    Form frm = null;
    switch (((Button)sender).Name)
    {
        case "button2":
            frm = new Form2();
            break;
        case "button3":
            frm = new Form3();
            break;

        // ・・・中略・・・

        case "buttonX":
            frm = new FormX();
            break;
    }
    frm.Show();
}

FormX+1 のコードを自力で書いて追加する際に、上記のコードに case "buttonX+1" ... を 3 行
追加すれば良いはずです。

上記には、

> 条件に応じてボタンのON/OFFが必要になります。

は影響ないはず。また、既存のコード(すでに完成しているのでは?)はほとんど変更せずに済む
はずです。

他の回答者の方の案と比較して、簡単、現実的、保守がしやすいかなどを考えてどうするかを検討す
ることをお勧めします。

[ 親 87881 / □ Tree ] 返信 編集キー/

▲[ 87891 ] / 返信無し
■87910 / 4階層)  Re[4]: 動的に作成した大量のボタンのイベント分岐について
□投稿者/ WebSurfer (1555回)-(2018/07/12(Thu) 18:55:57)
No87891 (なっとう さん) に返信

一つ言い忘れました。

switch 文を提案しておきながら何ですが、自分的にお勧めは 774RR さんが No87886
に書かれたハンドラを別々に分ける案です。


それ以外は「面倒」を避けようと思ってやった結果がかえって面倒になるという本末転倒
な結果になるような気がします。

特に保守を考えた場合。
[ 親 87881 / □ Tree ] 返信 編集キー/

▲[ 87887 ] / ▼[ 87892 ]
■87890 / 3階層)  Re[3]: 動的に作成した大量のボタンのイベント分岐につい
□投稿者/ PANG2 (220回)-(2018/07/11(Wed) 13:30:32)
2018/07/11(Wed) 13:32:39 編集(投稿者)
Dictionaryでボタンとフォームのセットを保持する

private Dictionary<Button, Type> Btns = new Dictionary<Button, Type>();

Btns.Add(btn1, typeof(Form1));
Btns.Add(btn2, typeof(Form2));


private void button_Click(object sender, EventArgs e)
{
	Type t = Btns[(Button)sender];
	Form frm = (Form)Activator.CreateInstance(t);
	frm.Show();
}

[ 親 87881 / □ Tree ] 返信 編集キー/

▲[ 87890 ] / ▼[ 87893 ]
■87892 / 4階層)  Re[4]: 動的に作成した大量のボタンのイベント分岐につい
□投稿者/ PANG2 (221回)-(2018/07/11(Wed) 13:53:54)
2018/07/11(Wed) 14:00:17 編集(投稿者)
リフレクションを使わない方法

public interface IFormMaker {
	Form Make();
}

public class FormMaker<T> : IFormMaker where T : Form, new() 	
{
	public Form Make()
	{
		return new T();
	}
}

Dictionary<Button, IFormMaker> Btns = new Dictionary<Button, IFormMaker>();
Btns.Add(btn1, new FormMaker<Form1>());
Btns.Add(btn2, new FormMaker<Form2>());


private void button_Click(object sender, EventArgs e)
{
	IFormMaker maker = Btns[(Button)sender];
	Form frm = maker.Make();
	frm.Show();
}

---
追記
あるいは、辞書化せずに

private void AddButton<T>(string name) where T : Form, new()
{
	var button1 = new System.Windows.Forms.Button();
	button1.Click += delegate {
		Form form = new T();
		form.Show();
	};
}

[ 親 87881 / □ Tree ] 返信 編集キー/

▲[ 87892 ] / ▼[ 87894 ]
■87893 / 5階層)  Re[5]: 動的に作成した大量のボタンのイベント分岐につい
□投稿者/ なっとう (5回)-(2018/07/11(Wed) 14:01:25)
PANG2 様
二種類も提案して頂きありがとうございます。
1つ目については、考えていた通りの動きが確認できました。
2つ目については、これから解読させていただき、どちらが適切か考えて使わせていただきます。

ありがとうございました。
解決済み
[ 親 87881 / □ Tree ] 返信 編集キー/

▲[ 87893 ] / ▼[ 87915 ]
■87894 / 6階層)  Re[6]: 動的に作成した大量のボタンのイベント分岐につい
□投稿者/ なちゃ (245回)-(2018/07/11(Wed) 14:44:14)
方法はいろいろあります。
柔軟な方法としてはLazy<T>を使うというのもあります。
これを使うとnewするときにパラメータを指定するとかの汎用性を持たせることも出来ます。
[ 親 87881 / □ Tree ] 返信 編集キー/

▲[ 87894 ] / ▼[ 87929 ]
■87915 / 7階層)  Re[7]: 動的に作成した大量のボタンのイベント分岐につい
□投稿者/ 774RR (616回)-(2018/07/13(Fri) 06:58:47)
条件によってボタンを
- 動的生成する
- 静的生成済みボタンを Visible=True/False する
のでは、使う側からするとほぼ同じことなので、オイラが以前に作ったソフトでは Visible を操作したっす。

同一場所に別ボタンを表示したい場合、デザイナ上は別の位置に配置してプログラムで場所をコピーする
なんてことまでやってみた(そこまでしてまでデザイナを使いたい、ってことで)

オイラ的には C# は初心者→中級者になったレベルなんだけど、
高度なことができるようになると、それをあえて使わずに
基本的なコードのままに留めておくのは「後からの」可読性向上にすごく役立つのがわかってきたよ。
( C/C++ の場合性能向上が何より優先され、そのために高度テクニックを使うのはしかたない)

まあテクニックに走ってみるのもよし、走らないのもよしってことで、いろいろやってみるといいね。
いろいろできるようになってみるのもよし、というべきか?

[ 親 87881 / □ Tree ] 返信 編集キー/

▲[ 87915 ] / ▼[ 87930 ]
■87929 / 8階層)  Re[8]: 動的に作成した大量のボタンのイベント分岐につい
□投稿者/ なちゃ (246回)-(2018/07/14(Sat) 02:49:23)
今回の例であればメンテナンス性ならコード呼び出し1行で生成するようにする方法が明らかに一番高いと思います。
というかデザイナのほうが高いという感じの意見があるのが不思議に感じるんですけどね。
※コード量も少ないし修正箇所も一か所だしGUIで操作するという時間のかかる操作は不要だし名前などで間違う可能性もより低いし。

考え方の違いとかメリットデメリットのどこを重視するかで変わるとか、個人ごとに意見は違うとか、多く場合はいろんな考え方があると思うところですが、今回の件はそういうレベルではなくて明確にメンテナンス性に差があるというイメージです。
[ 親 87881 / □ Tree ] 返信 編集キー/

▲[ 87929 ] / 返信無し
■87930 / 9階層)  Re[9]: 動的に作成した大量のボタンのイベント分岐につい
□投稿者/ shu (1132回)-(2018/07/14(Sat) 07:24:32)
提示コードを出来るだけ活かしたサンプル:
TabIndexはなんとかした方がよいと思います。

    public partial class Form1 : Form
    {
        private List<System.Windows.Forms.Button> Btns = new List<Button>();
        public Form1()
        {
            InitializeComponent();
            CreateBtns();
        }

        private void CreateBtns()
        {
            AddButton<Form2>("Open Form2");
            AddButton<Form3>("Open Form3");
        }

        private void AddButton<T>(string name) where T : Form, new()
        {
            var button1 = new System.Windows.Forms.Button()
            {
                //Location = new System.Drawing.Point(3, 3),
                Name = name,
                Size = new System.Drawing.Size(75, 23),
                TabIndex = 0, 
                Text = name,
                UseVisualStyleBackColor = true
            };
            button1.Click += (s, e) => OpenForm<T>();
            Btns.Add(button1);
            this.flowLayoutPanel1.Controls.Add(button1);
        }
        
        private void OpenForm<T>() where T : Form, new()
        {
            T frm = new T();
            frm.Show();
            frm.FormClosed += Frm_closed;
        }

        private void Frm_closed(object sender,EventArgs e)
        {
            Form frm = (Form)sender;
            frm.Dispose();
        }

    }



[ 親 87881 / □ Tree ] 返信 編集キー/


管理者用

- Child Tree -