2010. 5.27.
2011. 3.17. 「上書き保存」メソッドで、menuSaveAs_Click(sender,s);をmenuSaveAs_Click(sender,e);に
石立 喬

Visual C++ 2010 Express の易しい使い方(7)

――― マウスでパターンを入力し、ファイルに格納する―――

 画面上でマウスをクリックして、視覚的にデータを入力できれば便利である。具体的には、7×11のマトリックス状の桝目に合わせて英数文字フォントを作成する場合を紹介する。このプログラムでは、あらかじめ7×11の桝目を画面上に表示しておき、桝目をマウスでクリックすることにより、フォントを作成する。
  このようにして作成したフォントは別の処理で使用するので、これをデータとして保存し、再び取り出せるようにする。
 プログラム自体は平凡なものであるが、マウスクリックによる座標の取得や、ファイル入出力などが用いられているので、参考になると思われる。

準備
 すでに説明した方法で、フォームを準備する。念のために、簡単に説明する。
1)「新しいプロジェクト」→「CLR」→「Windowsフォーム アプリケーション」を選択する。
2)「Form1.h[デザイン]」ウインドウで、フォームの中心付近を右クリックし、「プロパティ」ウインドウで、
    表示 BackColor ---- Window
        Text -------- マウス入力とファイル入出力
3)「表示」→「その他のウインドウ」→「ツールボックス」→「メニューとツールバー」で「MenuStrip」をクリック
4)メニューキャプションを入力する。「ファイル(&F)」(括弧内は半角)とするとショートカットを指定できる。
5)フォームのサイズは、とりあえず、マウスでドラッグして大きめにしておき、後で修正する。

メニューの作成

 すでに説明した方法(メニューを右クリックして「プロパティ」を開く。一旦開いた「プロパティ」ウインドウは、別のメニューをクリックするとそのまま別の「プロパティ」ウインドウになる。)に従って下記のようにメニューを設定する。
メニューのキャプション 書き換えた名称 イベントハンドラ名 ハンドラの内容
ファイル menuFile menuFile_Click() 何もしない。
開く menuOpen menuOpen_Click() OpenFileDialogを起動。指定されたファイル名のファイルを開き、dataに読み込む。
上書き保存 menuSave menuSave_Click() 読み込み時に使ったファイル名のファイルを開き、dataを書き込む。
名前を付けて保存 menuSaveAs menuSaveAs_Click() SaveFileDialogを起動。指定されたファイル名のファイルを開き、dataを書き込む。
全クリヤ menuClear menuClear_Click() Dataをすべて0にリセットする。

ファイルへの出力
◎「上書き保存」の場合
 すでに取得しているファイル名fnameを使用して、
     StreamWriter^ swriter=gcnew StreamWriter(fname);
でStreamWriterのオブジェクトを生成する。あとは、swriter->WriteLine(string);で保存し、最後は、swriter->Close();でファイルを閉じる。ファイルを読み込んでいないのに、誤って「上書き保存」をクリックした場合は、fnameが存在しないので、「名前を付けて保存」に誘導する。
◎「名前を付けて保存」の場合
 保存用のダイアログボックスを、
     SaveFileDialog^ sfdlg=gcnew SaveFileDialog();
で生成し、if(sfdlg->ShowDialog()==Windows::Forms::DialogResult::OK) で「OK」を確認し、ダイアログから、sfdlg->FileName; で得たファイル名fnameでStreamWriterを生成する。以後は、「上書き保存」の場合と同じである。

ファイルからの読込み
 「ファイルを開く」ダイアログボックスを、
    OpenFileDialog^ ofdlg=gcnew OpenFileDialog();
で生成し、 if(ofdlg->ShowDialog()==Windows::Forms::DialogResult::OK) で「OK」を確認し、ofdlg->FileName; で得たファイル名fnameを用いて、
    StreamReader^ sreader=gcnew StreamReader(fname);
でStreamReaderのオブジェクトを生成する。あとは、sreader->ReadLine(); で読込み、sreader->Close(); で閉じる。

マウスによる座標の取得
 マウスのボタンを押したことによるイベントハンドラForm1_MouseDown()で、
    x=e->X;
    y=e->Y;
により、座標を取得する。
 左ボタンか右ボタンかの区別は、
    if(e->Button==Windows::Forms::MouseButtons::Left)

    if(e->Button==Windows::Forms::MouseButtons::Right)
で行なう。

プログラムの概要
1) StreamReaderとStreamWriterを使用するために、名前空間System::IOを記述しておく。
2) 二次元配列dataは、グローバル変数として宣言の後、Form1_Load()で、具体的にサイズを設定する。
  Form1_Load()を記述するには、下記の手続きを行う。
   ・「Form1.h[デザイン]」画面で、フォームの内部を右クリックし、「プロパティ」ウインドウを開く。
   ・「イベント」ボタン(稲妻の絵が描いてある)をクリックし、「動作」欄の「Load」をダブルクリックする。
   ・「Load」の右に、「Form1_Load」と表示され、同時に、エディタ画面が現れる。
     図1には、「Form1」の「プロパティ」ウインドウで、「イベント」ボタンをクリックし、主要部を示したものである。
     後述の「MouseDown」や「Paint」についても設定が行われている。


図1 「form1」の中央部を右クリックして開いた「プロパティ」ウインドウで設定後の様子


   ・エディタ画面にスケルトンのForm1_Load()ができているので、そこに必要なコードを記述する。
3) ファイル入出力関係のメニューのために、menuOpen_Click()、menuSave_Click()、menuSaveAs_Click()を記述し、マウスクリックして得た入力データdataを一斉に消去するためのmenuClear_Click()を記述する。
4) マウスクリックを検出して、座標を取得するために、Form1_MouseDown()を記述する。左クリックと右クリックを区別するために、
    if(e->Button==:Windows::Forms::MouseButtons::Left)
などを使用する。左クリックではdataが1にセットされ、右クリックではdataが0にリセットされる。
5) 得られた二次元配列dataを表示するために、Form1_Paint()を記述する。ここでは、7 x 11の枠を描き、dataが1であれば、赤で塗りつぶし、右側に、dataを表示する。

プログラム
 自動設定以外の、実際にコード入力した部分のみを示す。

 「Form1.h」最上部の「using namespace」が並んでいる最後に付け加える。

    using namespace System::IO;

「private: System::」が並んでいる最後に付け加える。配列の宣言方法は、「array<型名、次元>^ 配列名」とする。

    static int X0=20,X1=170,Y0=40;
    String^ fname;
    array<int,2>^ data;

フォーム中央付近でダブルクリックして、自動的に生成された「Form1_Load()」に記述する。

    //起動時に呼び出される
    private: System::Void Form1_Load(System::Object^ sender, System::EventArgs^ e) {

        data=gcnew array<int,2>(7,11);

    }

メニュー「開く」をダブルクリックして、自動的に生成された「menuFile_Click()」に記述する。そのほかのメニューについても同様。

    //メニュー「開く」
    private: System::Void menuOpen_Click(System::Object^ sender, System::EventArgs^ e) {

        OpenFileDialog^ ofdlg=gcnew OpenFileDialog();
        ofdlg->Filter="Dataファイル(*.dat)|*.dat";
        if(ofdlg->ShowDialog()!=Windows::Forms::DialogResult::OK) return;
        fname=ofdlg->FileName;
        StreamReader^ sreader=gcnew StreamReader(fname);
        for(int j=0;j<11;j++)
           for(int i=0;i<7;i++)
               data[i,j]=int::Parse(sreader->ReadLine());
        sreader->Close();
        Invalidate();

    }

    //メニュー「上書き保存」
    private: System::Void menuSave_Click(System::Object^ sender, System::EventArgs^ e) {

        //ファイルから読み込んでいないのに上書きしようとした場合
        if(!fname){
            menuSaveAs_Click(sender,e);
            return;
        }
        StreamWriter^ swriter=gcnew StreamWriter(fname);
        for(int j=0;j<11;j++)
            for(int i=0;i<7;i++)
                swriter->WriteLine(data[i,j].ToString());
        swriter->Close();

    }

    //メニュー「名前を付けて保存」
    private: System::Void menuSaveAs_Click(System::Object^ sender, System::EventArgs^ e) {

        SaveFileDialog^ sfdlg=gcnew SaveFileDialog();
        sfdlg->Filter="Dataファイル(*.dat)|*.dat";
        if(sfdlg->ShowDialog()!=Windows::Forms::DialogResult::OK) return;
        fname=sfdlg->FileName;
        StreamWriter^ swriter=gcnew StreamWriter(fname);
        for(int j=0;j<11;j++)
            for(int i=0;i<7;i++)
                swriter->WriteLine(data[i,j].ToString());
        swriter->Close();

    }

    //メニュー「全クリヤ」
    private: System::Void menuClear_Click(System::Object^ sender, System::EventArgs^ e) {

        for(int j=0;j<11;j++)
            for(int i=0;i<7;i++)
                data[i,j]=0;
        Invalidate();

    }

「Form1」の「プロパティ」ウインドウを開き、「イベント」ボタン(稲妻が描かれている)をクリックする。「マウス」欄の「MouseDown」をダブルクリックすると、「Form1_MouseDoun()」ができているので、そこに記述する。

    //マウスクリック
    private: System::Void Form1_MouseDown(System::Object^ sender,
                          System::Windows::Forms::MouseEventArgs^ e) {

        int x=e->X;
        int y=e->Y;
        if(x>X0 && x<X0+112 && y>Y0 && y<Y0+182){    //枠の範囲内
            if(e->Button==Windows::Forms::MouseButtons::Left)  //右クリック
                data[(x-X0)/16,(y-Y0)/16]=1;
            if(e->Button==Windows::Forms::MouseButtons::Right) //左クリック
                data[(x-X0)/16,(y-Y0)/16]=0;
        }
        Invalidate();

    }

同様に、「Form1」の「プロパティ」→「イベント」ボタン→「表示」の下の「Paint」をダブルクリックすると、「Form1_Paint()」が出来るので、下記のように記述する。

    //画面の描画
    private: System::Void Form1_Paint(System::Object^ sender,
                          System::Windows::Forms::PaintEventArgs^ e) {

        Graphics^ g=e->Graphics;

        //枠を描く
        for(int j=0;j<=11;j++)
            g->DrawLine(Pens::Black,X0,Y0+j*16,X0+112,Y0+j*16);
        for(int i=0;i<=7;i++)
            g->DrawLine(Pens::Black,X0+i*16,Y0,X0+i*16,Y0+176);

        //dataにより赤で塗りつぶす
        for(int j=0;j<11;j++)
            for(int i=0;i<7;i++)
                if(data[i,j]==1)
                    g->FillRectangle(Brushes::Red,X0+i*16+1,Y0+j*16+1,15,15);

        //dataを表示する
        System::Drawing::Font^ font1=gcnew System::Drawing::Font("MS ゴシック",11);
        for(int j=0;j<11;j++)
            for(int i=0;i<7;i++)
                g->DrawString(data[i,j].ToString(),font1,Brushes::Black,X1+i*16,Y0+j*16);

    }

得られた画面
 図2は、このプログラムによるForm1を起動したところで、ファイル関係のメニューができている。まだマウスによる
入力をしていないので、データはすべて0になっている。


図2 未入力で、ファイル関係のメニューを開いたところ


 図3は、マウスクリックによって数字「6」を入力したところで、右側に、対応するデータが表示されている。


図3 マウスで「6」を入力したところ。右にdataが表示されている。


「Visual C++ の勉強部屋」(目次)へ