投稿者 魔界の仮面弁士  (社会人) 投稿日時 2023/5/25 09:42:47
> そのとおりです。
> よろしくお願いいたします。

画面がその構成であるとして、
> フォームを動的に作成して実行
という質問とどう繋がるのかが、やっぱりわかりません。C# のバージョンはおろか、
そもそも Web アプリなのか、WPF なのか WinForms なのか MAUI なのかさえ不明ですし…。


とりあえず Windows Forms だと仮定してみますが、話を聞く限りではデザイン時にあらかじめ
メニュー画面上に、各画面に遷移するためのボタンをすべての画面の分だけ用意しておき、
 button1.Enabled = false;
 button2.Visible = false;
などとして、ログイン時の部署ごとの権限にあわせて、各遷移ボタンの
使用可能/使用不可、表示/非表示を切り替えてしまえば済むように思います。
(必要なら Location や Text や文字色も変更します)


あるいはボタンも動的に生成したいという話なら、先に回答した
>> 動的に Controls.Add
を使えば良い話ですよね。



ボタン自体はそのままにして、Click 時の遷移先の画面を切り替えたいだけなら
 private void button1_Click(object sender, EventArgs e)
 {
  var childForm
   = (ログイン部署 == "部署1") ? new pgm0001()
   :  (ログイン部署 == "部署2") ? new pgm0003()
   : default(Form);
  if (childForm != null)
  {
   using(childForm) { childForm.ShowDialog(this); }
  }
 }
のように、条件分岐でどの画面のインスタンスを生成するのかを変更すれば良いかと。


この時、先の回答のように、
>> public static Dictionary<string, Func<Form>> Creator
などを用意しておけば、各ボタンの Text に画面名をセットしておくことで
 // このイベントハンドラは、すべての遷移 Button から共通して呼ばれる
 private void buttons_Click(object sender, EventArgs e)
 {
  string caption = ((Button)sender).Text;  // ボタンのタイトルを取得
  if (Program.Creator.TryGetValue(caption, out Form childForm))
  {
   using (childForm)
   {
    childForm.ShowDialog(this);
   }
  }
 }
のように書けるでしょう。
部署ごとの判断が固定的なら、それも Dictionary などで階層管理するという手もありますね
(たとえば Dictionary の Key をタプルにするとか、Dictionary の Value を子階層の Dictionary にするとか)


権限判断の条件がもう少し複雑になるような場合は、その判断を行うための
 public Form GetFormInstance(string 部署名, int 画面連番)
な感じのメソッドを自分で用意しておくのも良いでしょう。
その場合は、そのメソッドを用いて
 //var childForm = GetFormInstance("部署1", 1); // = new pgm0001(); に相当
 //var childForm = GetFormInstance("部署1", 2); // = new pgm0002(); に相当
 //var childForm = GetFormInstance("部署2", 1); // = new pgm0003(); に相当
 var childForm = GetFormInstance(loginAccount.Department, buttonIndex);
 if (childForm != null)
 {
  using(childForm)
  {
   childForm.ShowDialog(this);
  }
 }
などとして呼べる形にもできますね。

上記はいずれも ShowDialog によるモーダル呼び出しにしていますが、
using を外したうえで、Show によるモードレス呼び出しにもできます。



あるいはそうした権限情報をデータベース等で保持しているのなら、
呼び出したい画面の完全修飾名(名前空間を含めたクラス名のこと)をデータベースから取得して、
 Type formType = Type.GetType(画面の完全修飾名, true);
 var childForm = (Form)Activator.CreateInstance(formType);
 using(childForm)
 {
  childForm.ShowDialog(this);
 }
などと書くこともできます。これは先に回答した
>> "pgm0001" の部分などを文字列で指定したいなら、
>> Activator.CreateInstance メソッドを併用
のことです。Activator.CreateInstance の使用例はこちら。
https://atmarkit.itmedia.co.jp/ait/articles/0512/09/news136.html

なお、自プロジェクト上にある画面を呼ぶ場合は上記コードでいけますが、
他の DLL ファイルや EXE ファイル上で定義されている画面を動的に呼び出す場合は、
前回の回答で紹介した Assembly.LoadFrom メソッドや、
下記(VB 版ですが)で使われている Assembly.LoadFile メソッドを併用します。
http://vbnettips.blog.shinobi.jp/_form/%E4%BB%96%E3%81%AEexe%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%81%AB%E3%81%82%E3%82%8B%E3%83%95%E3%82%A9%E3%83%BC%E3%83%A0%E3%82%92%E8%A1%A8%E7%A4%BA%E3%81%99%E3%82%8B%E6%96%B9%E6%B3%95%EF%BC%88ac
http://vbnettips.blog.shinobi.jp/_form/%E4%BB%96%E3%81%AEexe%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%81%AB%E3%81%82%E3%82%8B%E3%83%95%E3%82%A9%E3%83%BC%E3%83%A0%E3%82%92%E8%A1%A8%E7%A4%BA%E3%81%99%E3%82%8B%E6%96%B9%E6%B3%95%E3%81%9D%E3%81%AE%EF%BC%92