コントロール

【エラー処理つき】C#「別Fromフォームを開く」

はじめに

C# .NET Frameworkで「Fromから別Formを開く方法」を解説します。

別Formは、Formを作成しShow関数を実行することで簡単に開くことができます。

やり方は一見簡単ですが、「2度開く」「開いた後に再度開く」「削除後に開く」などを考えると、実はバグが発生しやすい箇所でもあります。そのため、今回は上記のバグが発生しないよう、簡単なエラー処理も含めて解説したいと思います。参考にしていただけたらうれしいです。

※ クリックすると各項目へジャンプできます。

環境

この記事は以下の環境で作成しています。

環境バージョンなど備考
VisualStudio   2019 Community 2017でも使用できます
.NET4.7.2
プロジェクトFormアプリケーション(.NET Framework) 
OSWindows10Win7でもOKです

実行結果

別のFormの開くコードの実行結果(GIF動画)です。

後述の設定方法とコードで、メインFormのボタンから、別Formを表示するプログラムを作成できます。ボタンを押された場合は、別フォームの最小化を解除して、前面に表示します。

別Formを開くコードの実行結果を説明するGif動画

.NET Framework プロジェクトの作成

別Formを開くプログラムを VisualStudioの、Widows フォームアプリケーション(.NET Framework) プロジェクトで作成します。

プロジェクトの作成方法については、以下の記事をご覧ください。

別Formとボタンの作成

自動生成された「メインForm」に、以下2つの項目を設定します。

  1. 「別Formを表示する」ボタンを作成
  2. 「別フォーム」の作成

① 「別Formを表示する」ボタンを追加する

以下の手順で、メインのFormに別Formを表示するためのボタンを追加します。

メインフォームにボタンを追加する

VisualStudioの左端のメニューで「ツールボックス」を開き「Button」を選択します。

Buttonの追加方法を説明する画像

メインFormに「Button」をドラック&ドロップして追加します。

ボタンをメインFormに追加する方法を説明する画像

ボタンのサイズと表示内容を変更する

ボタンのデフォルトのサイズ・表示内容では、何をするボタンなのか分かりずらいため、Sizeプロパティ・Textプロパティで「大きさ」「表示内容」を変更します。

ボタンのサイズ・表示内容を修正する方法を説明する画像

※ ボタンの名称(ソースコード上で指定する名前)はデフォルトのまま「button1」としています。

② 「別フォーム」を追加する

以下の手順で別フォームを作成します。

ファイルの追加

「ソリューションエクスプローラー」を表示し、「プロジェクト名称を右クリック」します。

メニューが表示されるので、「追加」「新しい項目」を選択します。

別Formを追加する方法を説明する画像

表示された「新しい項目の追加」画面で「フォーム(Windowsフォーム)」を選択して、「追加」ボタンを押します。

ファイルの確認

ソリューションエクスプローラーに、新しく追加した「Form2」が表示されていることを確認します。

Formが追加完了の確認方法を説明する画像

これで「別Formを開く」ボタン、「別フォーム」の作成は完了です。

全体コード

別のFormを表示するプログラムの、全体コードです。

あらかじめ、前述した「別Formを開くボタン」「別Form」が作成されていることを前提としています。概要は以下の通りです。

項目名称 ファイル名
メインFormform1Form1.cs
別Formを開くボタンbutton1Form1.cs
別Formform2Form2.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の仕様に関連してバグが発生しやすい箇所でもあります。

この手の問題は一度発生してしまうと見つけるのが大変で、運よく見つけらえた場合でも「他は大丈夫?」の追撃を受けること間違いなしの問題でもあります。

そんな状況に、皆さんが陥らないための参考になってもらえたらうれしいです。

コントロールについてもっと知りたい

コントロール深く知るためには、使い方に加えて、C#自体についても知る必要があります。また、作成していると当然バグにもぶつかるため、デバッグの知識も重要です。身近な人に教えてもらうことが一番ですが、そうでない方は書籍での勉強をおすすめします。

とは言っても、C#の書籍にはコントロールについて触れないものや・高度な内容から解説するものも多いので、初心者~中級者向けとして以下の本を選んでみました。

「プログラムはなぜ動くのか?」から始まり、基礎知識・コントロール(デスクトップアプリ)の作り方・デバッグ方法など、実際にコントロールを使うための内容が、イラスト入りで分かりやすく解説されています。初心者~中級者の方にちょうどいい内容です。

「ネットで十分じゃない?」という意見もあるかと思いますが、ネット情報は個別のコントロールなどの「スポット」の情報であることが多いです。書籍の購入には多少の費用が掛かりますが、手頃な値段で基礎~応用、デバッグの方法まで学べることは、会社でも学校でも個人の開発でも有利にはたらきます。スクール等に比べて金銭的負担も断然少ないです。Amazonなら購入前に試し読みもできるので、気になった方は試し読みから初めてみてください。

ちなみに分厚くはないので枕や鈍器には向きませんが、紙版を購入すると会社の机に並べて、(読む読まないに関わらず)「私、勉強しています。」のアピールに使うことができます。その様な用途を求めている方には是非紙版をおすすめします。

楽天ブックス
¥2,640 (2021/12/18 00:26時点 | 楽天市場調べ)

応援・要望お待ちしてます

ブログを見ていて「この辺を詳しく知りたい」「このライブラリの使い方を知りたい」「こんなことで困ってる」...etc があれば、コメント・問い合わせ・Twitterで教えてください。質問・ご要望に合わせて解説記事を作ります。

ブログを気に入っていただけたり、「応援してもいいよ」という方がいたら、ブログやSNSでの紹介をお願いします。 あたたかい応援は、中の人の更新の大きな励みになります。

ABOUT ME
えす
現役のソフトウェアエンジニアです。 C++ C# Python を使ってます。10年ちょい設計/開発部門にいましたが、今はQAエンジニアっぽいことをしています。

COMMENT

メールアドレスが公開されることはありません。