はじめに
C# .NET Frameworkで「Fromから別Formを開く方法」を解説します。
結論から言うと、別Formは、Formを作成して、Show関数を実行することで開くことができます。
やり方は一見簡単ですが、「2度開く」「開いた後に再度開く」「削除後に開く」などを考えると、実はバグが発生しやすい箇所でもあります。そのため、今回は上記のバグが発生しないよう、簡単なエラー処理も含めて解説したいと思います。参考にしていただけたらうれしいです。
※ クリックすると各項目へジャンプできます。
環境
この記事は以下の環境で作成しています。
環境 | バージョンなど | 備考 |
VisualStudio | 2019 Community | 2017でも使用できます |
.NET | 4.7.2 | |
プロジェクト | Formアプリケーション(.NET Framework) | |
OS | Windows10 | Win7でもOKです |
実行結果
別のFormの開くコードの実行結果(GIF動画)です。
後述の設定方法とコードで、メインFormのボタンから、別Formを表示するプログラムを作成できます。ボタンを押された場合は、別フォームの最小化を解除して、前面に表示します。
.NET Framework プロジェクトの作成
別Formを開くプログラムを VisualStudioの、Widows フォームアプリケーション(.NET Framework) プロジェクトで作成します。
プロジェクトの作成方法については、以下の記事をご覧ください。
別Formとボタンの作成
自動生成された「メインForm」に、以下2つの項目を設定します。
- 「別Formを表示する」ボタンを作成
- 「別フォーム」の作成
① 「別Formを表示する」ボタンを追加する
以下の手順で、メインのFormに別Formを表示するためのボタンを追加します。
メインフォームにボタンを追加する
VisualStudioの左端のメニューで「ツールボックス」を開き「Button」を選択します。
メインFormに「Button」をドラック&ドロップして追加します。
ボタンのサイズと表示内容を変更する
ボタンのデフォルトのサイズ・表示内容では、何をするボタンなのか分かりずらいため、Sizeプロパティ・Textプロパティで「大きさ」「表示内容」を変更します。
※ ボタンの名称(ソースコード上で指定する名前)はデフォルトのまま「button1」としています。
② 「別フォーム」を追加する
以下の手順で別フォームを作成します。
ファイルの追加
「ソリューションエクスプローラー」を表示し、「プロジェクト名称を右クリック」します。
メニューが表示されるので、「追加」「新しい項目」を選択します。
表示された「新しい項目の追加」画面で「フォーム(Windowsフォーム)」を選択して、「追加」ボタンを押します。
ファイルの確認
ソリューションエクスプローラーに、新しく追加した「Form2」が表示されていることを確認します。
これで「別Formを開く」ボタン、「別フォーム」の作成は完了です。
全体コード
別のFormを表示するプログラムの、全体コードです。
あらかじめ、前述した「別Formを開くボタン」「別Form」が作成されていることを前提としています。概要は以下の通りです。
項目 | 名称 | ファイル名 |
メインForm | form1 | Form1.cs |
別Formを開くボタン | button1 | Form1.cs |
別Form | form2 | Form2.cs |
ポイント部分は後述の「コードのポイント」で説明します。
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace FormAppOpenClose
{
public partial class Form1 : Form
{
// Form2型のオブジェクト定義します
Form2 form2 = null;
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
//
// form2が "万が一" 破棄された場合の処理です
// NULLのformに、isDisposeを実行すると例外が発生するので
// NULLのチェックを先に実施します。
//
if ( form2 == null )
{
// フォームを生成して、表示します
form2 = new Form2();
form2.Show();
}
//
// form2が閉じられている場合に、
// 再度form2を生成する処理です。
//
if (form2.IsDisposed == true )
{
form2 = new Form2();
form2.Show();
}
//
// form2が閉じられていない場合の処理です
// form2が背面に隠れている場合に前面に表示させます。
// 最小化している場合は元に戻します。
//
else
{
form2.WindowState = FormWindowState.Normal;
form2.Activate();
}
}
}
}
form2のコードです。自動生成されたままの状態でOKです。
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace FormAppOpenClose
{
public partial class Form2 : Form
{
public Form2()
{
InitializeComponent();
}
}
}
コードのポイント
ポイント部分を以下で詳しく説明します。
別Formを表示する
ボタンが押された際に、別Formを開くためのコードです。別Formは、Formの生成(new)・Show関数の実行で開くことができます。
if文は、初めて「別Formを開く」ボタンが押された時と「万が一、別Formが破棄(NULL)された場合」のために入れています。
※「万が一」については最後の項目で説明します。
//
// form2が "万が一" 破棄された場合の処理です
// NULLのformに、isDisposeを実行すると例外が発生するので
// NULLのチェックを先に実施します。
//
if ( form2 == null )
{
// フォームを生成して、表示します
form2 = new Form2();
form2.Show();
}
「閉じられた」場合はIsDisposedでチェックする
ボタンが押された際に、別フォーム(form2)が「既に閉じられている」時のためのコードです。
.NET Framework では、Form画面が閉じられると、即削除はされずに「Dispose」という状態になります。
そのため「既に閉じられるいるか?」の確認には、「NULL」ではなく「IsDisposed プロパティ」を使って確認する必要があります。
//
// form2が閉じられている場合に、
// 再度form2を生成する処理です。
//
if (form2.IsDisposed == true )
{
form2 = new Form2();
form2.Show();
}
Disposeはざっくりいうと「もう使わないのでそのうち削除しといてね」という状態です。オブジェクトの「存在と削除(NULL)のあいだ」を表します。
(イメージとしては、粗大ごみを家のごみ捨て場に捨てた時が近いです。捨てた後しばらくは手もとにありますが(Disposed)、いずれごみ収集車が来たら手元に存在しなくなります(NULL) )
「もう使わないもの」なので、Show()を行うと例外が発生してしまいます。
「開いている場合」は、最小化を解除して最前面に表示する
ボタンが押された際に、別Formが「開いている場合」時のコードです。
閉じられていない(Dispose状態ではない)ので、Formを生成する必要はありません。
「そのまま何もしない」としてしまっても問題ありませんが、「最小化の解除」「最前面に表示するコード」を入れておくと、よりユーザにやさしいプログラムにすることができます。
//
// form2が閉じられていない場合の処理です
// form2が背面に隠れている場合に前面に表示させます。
// 最小化している場合は元に戻します。
//
else
{
form2.WindowState = FormWindowState.Normal;
form2.Activate();
}
最初のif文でNULLをチェックする意味
Disposeを説明した上で、「万が一を考えてNULLをチェックする」理由は以下の通りです。
Disposeは「そのうち削除される」状態で、IsDisposeを使って確認します。ですが、「本当に削除された(NULLになった)」FormにIsDisposeでアクセスすると、例外が発生します。
基本的に、削除はGC(ガベージコレクタ)というC#の組み込みの機能が、状況を加味して自動的に行うもので、あまり気にする人は少ないかもしれません。
ですが、C#ではFormに対して「強制的にNULLを代入」してもエラーにはならず、「コード上でGCを強制的に実行」することもできるため、ヒューマンエラーとしてNULLになる状況が発生することがあります。
また、再起動を行わずに運用される場合は、システムの負荷に合わせて(正しく)GCが呼ばれることもあります。
そういった状況は、特に大規模プロジェクトや、コードで開発者のスキルや状況に応じて発生することが高く、タイミングとしても、リリース直前や運用開始後の「嫌なタイミング」で見つかるが多いです。
発生することは万が一かもしれませんが、「転ばぬ先の杖」として最初から処理の初めにチェックを入れておくことをおすすめします。
//
// form2が "万が一" 破棄された場合の処理です
// NULLだった場合、isDisposeを実行すると例外が発生するので
// NULLのチェックを先に実施します。
//
if ( form2 == null )
{
// フォームを生成して、表示します
form2 = new Form2();
form2.Show();
}
まとめ
C# .NET Frameworkで「Fromから別Formを開く方法」と、開く処理に関連したエラー処理について解説しました。
Formを開く処理は単純ですが、 DisposeやNULLの仕様に関連して、バグが発生しやすい箇所です。この手の問題は一度発生してしまうと見つけるのが大変で、運よく見つけらえた場合でも「他は大丈夫か?」と不安になる処理でもあります。予め対処してしまえば後のバグや不安はなくなるので、事前に対処しておくようにしましょう。
参考になればうれしいです。
お知らせ
今月号のSoftware Designは「もっとTypeScriptの力を引き出そう」
JavaScriptの拡張言語でしょ?と思っているかたへ、Union型などTypeScriptの持つ秘めたる力を解説する特集となっています。
個人的には第2特集の「Ubuntuの現代的な使い方」がの方が気になりました。より詳しい内容は以下のリンクからご覧ください。
質問・要望 大歓迎です
「こんな解説記事作って」「こんなことがしたいけど、〇〇で困ってる」など、コメント欄で教えてください。 質問・要望に、中の人ができる限り対応します。
使えたよ・設定できたよの一言コメントも大歓迎。気軽に足跡を残してみてください。記事を紹介したい方はブログ、SNSにバシバシ貼ってもらってOKです。