StackOverflowには、Form1を非表示にしてForm2を表示する方法を尋ねる質問がたくさんあります。そして、通常、いくつかの異なる答えが現れます:
1)
_// Program.cs
Application.Run(new Form1());
// Form1.cs
Form2 form2 = new Form2();
form2.Show();
this.Hide();
_
2)
_// Program.cs
Form1 form1 = new Form1();
Form2 form2 = new Form2();
form1.Show();
form2.Show();
Application.Run();
_
_...etc..
_
#1のような簡単な使い捨てソリューションを探しているのではありません。フォーム管理のベストプラクティスを探しています。 5〜8個のフォームがあり、頻繁に相互に開閉するアプリケーション-これらのフォームを管理する最良の方法は何ですか?
私の考えは、各フォームを(レイジー?)シングルトンにして、それらをある種のFormsManager
クラスに埋め込むことでした(ソリューション#2のように++)。そして、個々のフォームはFormsManager.GetForm<WelcomeDialog>()
のようなものを呼び出すかもしれません。
しかし、私はもっと経験のある人が何を使っているのかと思っていました。繰り返しますが、これらのソリューションは簡単なハックではありません。それらはdesign-oriented、おそらくarchitectural、そしてlong-term solutionである必要があります。
編集:
これは、同じ問題が発生する可能性のある人にとっては、かなり一般的な質問です(要件はかなりオープンです)。私の状況に固有ですが、起動時に複数のフォームを表示する必要はありません。また、MDI=フォームはありません。いくつかのモーダルフォームがあるかもしれませんが、ほとんどが非モーダルです。
ここでは一般的な方法でお答えします。
シングルトンパターンがフォーム管理にうまく適合しないと思います。一般に、いくつかのコンテキストパラメータをフォームに渡し、同じフォームの複数のインスタンスを開きたい場合があります。したがって、シングルトンはIMOにうまく適合しません。
フォーム管理はシンプルにすべきだと思います。
たとえば、別のフォームからモーダルフォームを表示したい場合は、非常に簡単なものを記述します。
_private void button1_Click(object sender, EventArgs e)
{
using (ModalForm1 frm = new ModalForm1(myParam))
{
frm.ShowDialog();
if (frm.MyResultProperty == ...)
{
// Do some job here
}
}
}
_
もちろん、多くのモーダルフォームを表示したい場合に、コード/コードの重複を避けるために、いくつかのインターフェイス/ジェネリック構文を書くことができます。
_public interface IFormResult<T>
{
T Result { get; set; }
}
public class ModalForm1 : Form, IFormResult<string>
{
public ModalForm1()
{
InitializeComponent();
this.Result = "My result";
}
public string Result { get; set; }
}
private void button1_Click(object sender, EventArgs e)
{
string res = ShowModalForm<ModalForm1, string>();
}
private static T2 ShowModalForm<T1, T2>()
where T1 : Form, IFormResult<T2>, new()
{
using (T1 form = new T1())
{
form.ShowDialog();
return form.Result;
}
}
_
でも正直なところ、少しやり過ぎだと感じています。
2番目のポイント:フォームがこの特定の動作に正確に従っていない場合(ShowDialog()
の場合、Result
プロパティが設定されます)、別のインターフェイスなどを記述する必要があります。
このタイプの構文(ジェネリック、インターフェイスなど)で書かれたコードの行数が減らない場合OR複雑さOR保守性(そして、私たちはそれが本当にここに当てはまると言うことはできません)、それからそれはかなり役に立たないIMOです。
編集:
フォーム管理は実際にはユースケースに依存します。
FormManager
の概念を検討する必要があります(または、より良い方法:開いている可能性のあるフォームの数を減らしてユーザーエクスペリエンスを向上させる方法について考えます)。一般に、アプリケーションの開始に使用されるフォーム(つまり、閉じるときにプログラムを停止するフォーム、つまりApplication.Run()
のパラメーターであるフォーム)は、他のフォームを担当します。 1つのメインフォームと複数の子フォームがあります。あなたのケースが本当に異なる場合は、おそらくもっと賢いものを書くでしょうが、それはあなたのケースに依存します。フォーム管理の一般的な問題に対する一般的な良い答えを提供できるとは思いません。
正直なところ、本当に保守しやすいものが必要な場合は、同時に表示できるフォームの数を(可能な限り)減らすようにしてください。同時に複数のモードレスフォームが表示されると、ほとんどの場合、優れたユーザーエクスペリエンスが提供されず、フォームが互いに依存している場合、フォームの有効期間の管理に問題が生じる可能性があります。
最も単純なシナリオ以外の場合-存続期間の短い子フォームを使用して、アプリケーションの存続期間中に実行される単一のメインフォーム- ApplicationContext
から継承するクラスを作成することをお勧めします 。それほど複雑ではありません。
_class FormManager : ApplicationContext {
//When each form closes, close the application if no other open forms
private void onFormClosed(object sender, EventArgs e) {
if (Application.OpenForms.Count == 0) {
ExitThread();
}
}
//Any form which might be the last open form in the application should be created with this
public T CreateForm<T>() where T : Form, new() {
var ret = new T();
ret.FormClosed += onFormClosed;
return ret;
}
//I'm using Lazy here, because an exception is thrown if any Forms have been
//created before calling Application.SetCompatibleTextRenderingDefault(false)
//in the Program class
private static Lazy<FormManager> _current = new Lazy<FormManager>();
public static FormManager Current => _current.Value;
//Startup forms should be created and shown in the constructor
public FormManager() {
var f1 = CreateForm<Form1>();
f1.Show();
var f2 = CreateForm<Form2>();
f2.ShowDialog();
}
}
_
および_Application.Run
_の_Program.cs
_は、FormManager
の静的インスタンスを使用できます。
_static class Program {
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main() {
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(FormManager.Current);
}
}
_
アプリケーションの存続期間中、CreateForm
メソッドをonFormClosed
イベントに登録するには、FormClosed
を介して新しいフォームを作成する必要があります。
_var f3 = FormManager.Current.CreateForm<Form3>();
f3.Show();
var f4 = FormManager.Current.CreateForm<Form4>();
f4.ShowDialog();
_
_FormManager.CreateForm
_の呼び出しよりもnew Form3();
を使用したい場合は、RegisterForm
にFormManager
メソッドを作成できます。
_public void RegisterForm(Form frm) {
frm.FormClosed += onFormClosed;
}
_
新しいRegisterForm
ごとにForm
を呼び出します:
_var f3 = new Form3();
FormManager.Current.RegisterForm(f3);
var f4 = new Form4();
FormManager.Current.RegisterForm(f4);
_
(すべてのフォームが基本クラスから継承する場合、新しいインスタンスごとに手動でRegisterForm
を呼び出す代わりに、基本クラスコンストラクターで呼び出すことができます。)
_Application.OpenForms
_は、現在表示されているフォームのみを返すことに注意してください。非表示のフォームが開いている限りアプリケーションを終了しない場合、FormManager
はすべてのフォームを追跡するためにいくつかのコレクションを使用する必要があります。そのコレクションは、アプリケーションを終了するかどうかを決定します。
_class FormManager : ApplicationContext {
private List<Form> forms = new List<Form>();
private void onFormClosed(object sender, EventArgs e) {
forms.Remove((Form)sender);
if (!forms.Any()) {
ExitThread();
}
}
public void RegisterForm(Form frm) {
frm.FormClosed += onFormClosed;
forms.Add(frm);
}
public T CreateForm<T>() where T : Form, new() {
var ret = new T();
RegisterForm(ret);
return ret;
}
private static Lazy<FormManager> _current = new Lazy<FormManager>();
public static FormManager Current => _current.Value;
}
_
私はこのトリックを使います。 form1がメインフォームであるとしましょう:
private void button1_Click(object sender, EventArgs e)
{
LoadForm(new Form2());
}
private void LoadForm(Form frm)
{
frm.FormClosed += new FormClosedEventHandler(frm_FormClosed);
this.Hide();
// Here you can set a bunch of properties, apply skins, save logs...
// before you show any form
frm.Show();
}
void frm_FormClosed(object sender, FormClosedEventArgs e)
{
this.Show();
}
したがって、閉じたフォーム(form1を除く)を開くと、form1が再び表示されます。
更新
using (Form2 frm = new Form2())
{
if (frm.ShowDialog() = DialogResult.ok)
{
//Do some things...
}
}
この場合、以前のフォームを非表示にする必要はありません。
アプリケーションのサイズに応じて、Microsoft Enterpriseライブラリ、特にCABブロックを確認するとします。
それはあなたに良いスタートを与えるでしょう。