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

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

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

Re[11]: Re: 動的に作成した大量のボタンのイベント分岐につい


(過去ログ 151 を表示中)

[トピック内 18 記事 (1 - 18 表示)]  << 0 >>

■87881 / inTopicNo.1)  動的に作成した大量のボタンのイベント分岐について
  
□投稿者/ なっとう (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();
}

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

引用返信 編集キー/
■87884 / inTopicNo.2)  Re[1]: 動的に作成した大量のボタンのイベント分岐について
□投稿者/ WebSurfer (1550回)-(2018/07/11(Wed) 11:15:02)
No87881 (なっとう さん) に返信

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

それではダメなのでしょうか?
引用返信 編集キー/
■87885 / inTopicNo.3)  Re[2]: 動的に作成した大量のボタンのイベント分岐について
□投稿者/ なっとう (2回)-(2018/07/11(Wed) 11:47:04)
回答ありがとうございます。

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

今後フォームが増えたときに、AddButton()とは別に、Clickイベント内のSwitchにも追加しなければならないというのが面倒に感じたため、AddButtonのパラメータにフォームを渡せないものかと考えた次第です。
引用返信 編集キー/
■87886 / inTopicNo.4)  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 プロパティに格納しておいた値でほげほげするとか。

引用返信 編集キー/
■87887 / inTopicNo.5)  Re[2]: 動的に作成した大量のボタンのイベント分岐について
□投稿者/ なっとう (3回)-(2018/07/11(Wed) 12:05:24)
ありがとうございます。

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

>Tag プロパティに格納しておいた値でほげほげするとか。
それでも構いませんので、可能でしたら教えて頂けないでしょうか。
引用返信 編集キー/
■87888 / inTopicNo.6)  Re[3]: 動的に作成した大量のボタンのイベント分岐について
□投稿者/ WebSurfer (1551回)-(2018/07/11(Wed) 12:07:49)
No87885 (なっとう さん) に返信

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

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

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

私が質問者さんが「面倒」と思うポイントを理解できてないのかもしれませんが、なんと
なく本末転倒的な気がするのですが・・・
引用返信 編集キー/
■87890 / inTopicNo.7)  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();
}

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

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

「面倒」というのは、デザイナとコードをあっちこっち移動して追いかけるのに慣れていないからそう感じるのだと思います。
引用返信 編集キー/
■87892 / inTopicNo.9)  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();
	};
}

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

ありがとうございました。
解決済み
引用返信 編集キー/
■87894 / inTopicNo.11)  Re[6]: 動的に作成した大量のボタンのイベント分岐につい
□投稿者/ なちゃ (245回)-(2018/07/11(Wed) 14:44:14)
方法はいろいろあります。
柔軟な方法としてはLazy<T>を使うというのもあります。
これを使うとnewするときにパラメータを指定するとかの汎用性を持たせることも出来ます。
引用返信 編集キー/
■87898 / inTopicNo.12)  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が必要になります。

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

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

引用返信 編集キー/
■87910 / inTopicNo.13)  Re[4]: 動的に作成した大量のボタンのイベント分岐について
□投稿者/ WebSurfer (1555回)-(2018/07/12(Thu) 18:55:57)
No87891 (なっとう さん) に返信

一つ言い忘れました。

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


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

特に保守を考えた場合。
引用返信 編集キー/
■87915 / inTopicNo.14)  Re[7]: 動的に作成した大量のボタンのイベント分岐につい
□投稿者/ 774RR (616回)-(2018/07/13(Fri) 06:58:47)
条件によってボタンを
- 動的生成する
- 静的生成済みボタンを Visible=True/False する
のでは、使う側からするとほぼ同じことなので、オイラが以前に作ったソフトでは Visible を操作したっす。

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

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

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

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

考え方の違いとかメリットデメリットのどこを重視するかで変わるとか、個人ごとに意見は違うとか、多く場合はいろんな考え方があると思うところですが、今回の件はそういうレベルではなくて明確にメンテナンス性に差があるというイメージです。
引用返信 編集キー/
■87930 / inTopicNo.16)  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();
        }

    }



引用返信 編集キー/
■87955 / inTopicNo.17)  Re: 動的に作成した大量のボタンのイベント分岐につい
□投稿者/ ぶなっぷ (186回)-(2018/07/20(Fri) 09:54:54)
2018/07/20(Fri) 10:00:57 編集(投稿者)
話がかなり進んでいるので、他の方の発言は読んでいません。
ただ、なんとなく話がバラバラに見えます。

なっとうさんがやりたいのって、こういうこと?
手元にFormのサンプルがないので、WPFで試してしまいましたが、
普通に動作しましたよ。

// nameの文字列が表示されたボタンを押すと、Wndが表示される
private void AddButton(string name, Window Wnd)
{
    var button1 = new Button();
    button1.Name = name;
    button1.Content = name;
    button1.Click += (obj, e) =>
    {
        Wnd.Show();
    };
    MainPanel.Children.Add(button1);
}

Form流に書き直すとこんな感じかな?
こっちは動作確認してないです。
private void AddButton(string name, Form frm)
{
    var button1 = new Button();
    button1.Name = name;
    button1.Text = name;
    button1.Click += (sender, e) =>
    {
        frm.Show();
    }
    this.flowLayoutPanel1.Controls.Add(button1);
}

引用返信 編集キー/
■87957 / inTopicNo.18)  Re[11]: Re: 動的に作成した大量のボタンのイベント分岐につい
□投稿者/ なっとう (10回)-(2018/07/20(Fri) 10:53:40)
解決済みとして処理をしたので既にクローズしたつもりで見ていませんでしたが、誰かが回答すると「解決済み」が消えてしまう仕様なのでしょうか・・。

■WebSurfer 様
 一般的に考えれば個別にイベントハンドラを付けたりswitchが良いのだと思います。
 よく考えると、今回はShow()しか絶対に実行しないような場面において、ボタンを1行で追加する方法が知りたかったのでした。もう少しハッキリと言い切るべきだったと感じています。

■774RR 様
>Visibleを操作する方法
 今回は開発中にフォーム(クラス)が増えたり、消えたりするので使えませんが今後の参考にさせて頂きたいと思います。

■なちゃ様
 仰る通り私もいちいちデザイナを開くよりコード1行で表現出来る方がずっとメンテがしやすいと感じます。

No87955 (ぶなっぷ さん) に返信
 なるほど。ボタン生成と同時にフォームのインスタンスも生成しておけばすごくシンプルに書けるのですね。
 }の後に;が不足していましたが、無事動作確認できました。
 今回はコンストラクタに渡すパラメータの都合もあって見送りますが、大変参考になりました。

以上、今度こそこれで締めたいと思います。ありがとうございました。
解決済み
引用返信 編集キー/


トピック内ページ移動 / << 0 >>

このトピックに書きこむ

過去ログには書き込み不可

管理者用

- Child Tree -