前回、本シリーズを始めるにあたってMicrosoft Learnのサンプルコード

について、

 

(1)不要なコンポーネントのDLLを参照し、宣言している。

(2)コントロールの宣言がコンストラクターに置かれたり、InitializeComponentに置かれたりしてバラバラ。

(3)コントロールの二重定義もある。

(4)コントロール名(label3、label5)をそのまま初期表示している。(↑参照)

(5)イメージを削除しても削除したイメージが表示され続ける。(表示に段差がありますね↓参照)

(6)イメージを削除し、リストボックスが未選択になって、再度削除ボタンを押すと落ちる。(↓参照)

(7)ウィンドウサイズは可変ですが、コントロールは微動だにしません。

 

と書きました。それを解説してゆきます。

 

(1)不要なコンポーネントのDLLを参照し、宣言している。

まず、

    using System.ComponentModel;

ComponentoModel名前空間を使い、

    private System.ComponentModel.IContainer components;

IContainerインターフェィス注1)のインスタンスとして宣言し、さらにVisual Studio定番のInitializeComponent()関数の中で、

    this.components = new System.ComponentModel.Container();

Containerクラス注2)としてインスタンスを生成しています。(注3

注1:「コンテナーの機能を提供します。 コンテナーは、論理的に 0 個以上のコンポーネントを格納するオブジェクトです。」(Microsoft Learn)

注2:「0 個以上のコンポーネントをカプセル化します。」「クラスはContainer、インターフェイスの既定のIContainer実装です。」(Microsoft Learn)

注3:Visual Studioを使っていると機械自動生成のコード、ファイルが他にあり、全体が見えないことがままあります。これはどうもこういうことのようです。この記事では「components』変数はクラス内で使用されるフィールド変数で、コンポーネントを管理するためのものだ(「コンテナ」と呼ばれる)。コンポーネントについては、改訂版 C#入門の「Column - コンポーネントとコントロール -」を参照してほしい。」と書かれており、Visual StudioによるウィンドウのUI定義、実装を行う肝の様です。

 

そして最終的には、

    this.imageList1 = new System.Windows.Forms.ImageList(this.components);
ImageListクラスインスタンスのコンストラクターに引数として渡されます。そしてコード全体のどこを見ても、このcomponentsが何なのかは全く分かりません。因みにImageListクラスを見ると、Containerクラスの引数付きコンストラクターがあり、「コンテナーに関連付けて、ImageListクラスの新しいインスタンスを初期化します。」と書かれていますが、このプログラムについては何も参照が無く寿限無寿限無です。

 

従って、components変数は意味がなくなる(Visual Studioではユーザー定義が無ければnullになるようです)為、ここは思い切ってcomponents関連コードはすべて削除しましょう。(読んでいただければわかりますが、本プログラムではImageLIstにイメージを読み込む仕様となっているので、引数無しコンストラクターでも全く差し支えありません。)

 

(2)コントロールの宣言がコンストラクターに置かれたり、InitializeComponentに置かれたりしてバラバラ。

(3)コントロールの二重定義もある。

これは何かの人為的間違いのようなので一遍にやりましょう。

先ず、クラス内で参照されるものはクラス内変数として宣言される点は問題ありません。しかし、例外としてImageListクラスインスタンスがコンストラクター内で、

            imageList1 = new ImageList () ;
            // The default image size is 16 x 16, which sets up a larger
            // image size. 
            imageList1.ImageSize = new Size(255,255);
            imageList1.TransparentColor = Color.White;

と実装、定義されているのに対して他のコントロールはInitializeComponent()関数内でなされています。

統一性がないな」と感じたのも束の間、よくよく見るとInitializeComponent()関数内でも

            this.imageList1 = new System.Windows.Forms.ImageList(this.components);    //二重生成
            .

            .

            .

            this.imageList1.ImageSize = new System.Drawing.Size(16, 16);    //二重設定
            this.imageList1.TransparentColor = System.Drawing.Color.Transparent;    //二重設定

になっています。

どうしてこんなことになったのでしょうか?私はVisual Studioを使っていませんが、内部チェック(シンタックスや文法、ロジックチェック)で引っかかるのじゃないかなと感じますが。いずれにしてもご多忙な様子、職業プログラマーは大変です。これはおそらく最初はVisual Studioで初め、InitializeComponent()関数が機械生成コードで、後から人間がそれと異なるコードをマニュアルで追加したのでしょう。これは纏めてInitComponent()関数()に収容しましょう。

:IContainerで使用される”InitializeComponent"という名称は避けました。

 

(4)コントロール名(label3、label5)をそのまま初期表示している。

マニュアルでプログラミングする際には可読性を上げるために、「それが何か」直感で分かるようにしました。以下に対比させます。

<Original>

        private System.ComponentModel.IContainer components;
        private System.Windows.Forms.ListBox listBox1;
        private System.Windows.Forms.Label label3;
        private System.Windows.Forms.Button button1;
        private System.Windows.Forms.Button button2;
        private System.Windows.Forms.Button button3;
        private System.Windows.Forms.Button button4;
        private System.Windows.Forms.PictureBox pictureBox1;
        private System.Windows.Forms.ImageList imageList1;
        private System.Windows.Forms.OpenFileDialog openFileDialog1;
        protected Graphics myGraphics;
        private System.Windows.Forms.Panel panel1;
        private System.Windows.Forms.Label label5;
        private int currentImage = 0;

<Modified>

        private ListBox listBox;
        private Label num_label;
        private Button open_button;
        private Button disp_button;
        private Button del_button;
        private Button clr_button;
        private Button exit_button;
        private PictureBox pictureBox;
        private ImageList imageList;
        protected Graphics graphics;
        private Panel panel;
        private Label path_label;
        private int currentImage = 0;

 

(5)イメージを削除しても削除したイメージが表示され続ける。

これは

        private void button2_Click (object sender, System.EventArgs e)
        private void button3_Click (object sender, System.EventArgs e)

で「リストボックスとイメージリストの要素を削除」しかしていないからです。これに加えてパネルとピクチャーボックスの画像消去とラベルの文字列消去が欲しいですね。

        private void del_button_Click (object sender, System.EventArgs e)
        private void clr_button_Click (object sender, System.EventArgs e)

で修正しています。

 

(6)イメージを削除し、リストボックスが未選択になって、再度削除ボタンを押すと落ちる。

リストボックスとイメージリストの要素は0から始まる整数で、currentImage変数で管理しています。オリジナルコードは要素の削除を行ってもcurrentImage変数を変えていないので、イメージの削除や表示を行おうとすると「配列範囲外エラー」になるわけです。エラーハンドラーのコードを追加します。

 

(7)ウィンドウサイズは可変ですが、コントロールは微動だにしません。

オリジナルは機械作成のInitializeComponent関数でコントロールを設定していますが、余り熱心に配置していなかったので、アンカーなどはついていません。又英語表記で短くて済んだものが日本語表記にするとはみ出してしまうことが有ります。その為、UIレイアウト一から見直しし、アンカーも付けました。

 

次回は修正済のプログラム

のコードを紹介しましょう。