プログラム実行時のウィンドウのサイズをユーザに変えられないようにサイズを固定するには
フォームのFormBorderStyleプロパティをFixed××に設定します。
なんとなく、FormResizeみたいなプロパティが無いか?探したくなっちゃいます...
ちなみに、こちら↓はVB6.0のプロパティ
VB6.0ではBorderStyleプロパティです。
プログラム実行時のウィンドウのサイズをユーザに変えられないようにサイズを固定するには
フォームのFormBorderStyleプロパティをFixed××に設定します。
なんとなく、FormResizeみたいなプロパティが無いか?探したくなっちゃいます...
ちなみに、こちら↓はVB6.0のプロパティ
VB6.0ではBorderStyleプロパティです。
一般的なウィンドウでは下図のようにOKボタンとCancelボタンを配置しボタンが押されるとウィンドウを閉じるようにしますが、このOKボタンとCancelボタンのどちらのボタンを押されたのか?を取得する方法を紹介します。
まず、buttonコントロールをOKボタンとCancelボタン用の2つを配置します。
次にボタンのDialogResultプロパティの中からボタンに応じてOKやCancelなどを選択します。
DialogResultプロパティを設定することで、ボタンが押されると自動的にウィンドウが閉じられるので、ボタンのイベント処理を記載する必要はありません。 (ShowDialogメソッドを使ってフォームを表示した場合)
ただし、ウィンドウが非表示になるだけなので、ウィンドウが必要なくなったら、フォームを解放しておきます。
以下、Form2が閉じられた時に、何のボタンを押されてウィンドウが閉じられたのか?
を取得する部分のサンプルプログラムです。
private: System::Void button1_Click(System::Object^ sender, System::EventArgs^ e) {
//Form2を開く
Form2^ frm = gcnew Form2();
frm->ShowDialog();
//押されたボタン別の処理
if (frm->DialogResult == System::Windows::Forms::DialogResult::OK)
MessageBox::Show("Okボタンが押されました");
else if (frm->DialogResult == System::Windows::Forms::DialogResult::Cancel)
MessageBox::Show("Cancelボタンが押されました");
//フォームの解放
delete frm;
}
実行例
Form2を開くボタンをクリック
OKボタンをクリックすると、Form2のウィンドウが閉じられ、
と表示されます。
ウィンドウの×ボタンでウィンドウが閉じられた場合はCancelボタンとして認識されます。
前回、フィルタ処理の高速化アルゴリズム(重複した計算を行わない)で紹介した方法ではカーネルの値が全て同じでないと使えないので、今回はフィルタ処理を縦方向と横方向に分けて行う事でフィルタ処理の高速化を行う方法をガウシアンフィルタを例にとって紹介します。
ガウシアンフィルタのカーネルには、
が良く用いられますが、この処理を注目画素の周辺の輝度値をI0~I8とした場合、
ガウシアンフィルタの処理を行列で
と、表すこともでき、この事は縦方向に3×1のガウシアンフィルタ処理をおこなってから、
横方向に1×3のガウシアンフィルタ処理を行うことを意味しています。
(横方向に処理をしてから縦方向に処理をしても同じです。)
このように処理を縦と横に分けることで、カーネルのサイズm×nの場合、通常の処理では
m×n回の掛け算を行うところ、m+n回の掛け算で済む事になります。
(ただし、縦横に処理を分ける事で全画素を2回参照することになるので、カーネルのサイズが
小さいと効果はあまりありません。)
他にも、移動平均フィルタの場合は
ソーベルフィルタの場合は
となります。
ソーベルフィルタの行列を見ると、縦方向にガウシアンフィルタ処理をしてから、横方向に微分処理している事が分かりやすくなっているかと思います。
また、比較的処理の重いメディアンフィルタにおいても、処理を縦と横に分けることによって、
ほぼ、同様な効果を得ることができます。
厳密には同じ結果にはならないのですが、スパイクノイズを除去するという意味では
十分な結果を得る事が出来ます。
試しに何回か、メディアンフィルタ処理を縦方向に1列分の処理を行ってから、横方向に1列分の
処理を行ってみましたが、ほぼ、良好な結果を得る事ができていると思います。
(画像にするともう少し分かりやすいかと思いますが、プログラムが無いもので...)
画像フィルタ処理の高速化のテクニックを移動平均フィルタを例にとって紹介したいと思います。
カーネルのサイズが5×5の移動平均フィルタの場合、注目画素の周辺の5×5の輝度値を合計し、
その輝度値の合計を画素数(5×5=25)で割る処理をラスタスキャンしながら、全画素に対して
処理を行います。
ここで、隣の画素へ処理が移った時に、輝度値の合計の計算処理は前に行った輝度値の合計の
処理とかなりかぶっている(下図の緑色の部分)事に気が付きます。
そこで、最初の輝度値の平均値を計算をした時の輝度値の合計値を保持しておき、最初の輝度値の
合計値から、最初のカーネルの左端の1列分の輝度値(上図の赤色の部分)を引き、
次のカーネルの右端の1列分の輝度値(上図の青色の部分)を足すと、次のカーネル内の
輝度値の合計値を求める事が出来ます。
そうすると、カーネル内の輝度値の合計の計算に25回の足し算をしていたところ、5回の引き算と
5回の足し算の計10回の計算で済ませることが分かります。
この効果はカーネルサイズが大きくなればなる程、大きくなります。
さらに画像の1行分の合計値を確保するメモリを確保しておくと、縦方向に関しても同様の処理が
できるので、高速化が期待できます。
と、今回は画像のフィルタ処理を例にとって紹介していますが、この考え方は他にもいろいろと
応用が効くので、輝度値の合計の計算に留まらず、毎回同じような処理をしているな~と思ったら、
前回行った処理の使いまわしができないか?検討してみると良いでしょう。
先日、C言語によるOpenCVのサンプルプログラムについては、
【OpenCV2.2】新規プロジェクトの作成方法(C言語版)
のページにて紹介しましたが、C言語だとウィンドウの作成やマウスイベント処理などが、少々面倒です。
そこで、.NETの中でもC言語寄りなC++/CLIを使って、OpenCVのIplImageを.NETのBitmapへ変換し、GUI部分は、.NETに任せて、より簡単なウィンドウプログラムを作ってみました。
サンプルプログラムはこちら→OpenCV22_CLISample.zip
動作環境
このサンプルではファイルメニューから画像ファイルを開き、二値化、平滑化などの簡単なフィルタ処理を行い、マウスポインタを画像上で動かすと、左下のステータスバーに輝度値が表示されるようになっています。
このサンプルプログラムは、できるだけ初心者の方にも分かり易く?書いたつもりですが、逆に、ある程度詳しい人にとっては、画像の描画の部分など、無駄に見える部分もあるかと思いますが、そのへんの最適化はみなさんにお任せします。
(あまりマジメに書くと、本職に支障が出そうなので...)
といっても、このサンプル、そこそこ便利だと思います。
2011年4月20日(水)~22日(金)に神奈川県横浜市のパシフィコ横浜にて、カメラや光学機器メーカーが集う、
が開催されます。
今回は日本インダストリアルイメージング協会(JIIA)主催の無料のセミナーもあるので、ご興味のある方は参加されてみては如何でしょうか?
http://www.optronics.co.jp/optworld/spring/event_jiia.php
OpenCVのプログラムを作っていると、OpenCV用のGUI(highgui)はかなり物足りなく、ウィンドウや表示まわりは.NETに任せたいので、OpenCVのIplImageから.NET(C++/CLI)のBitmapへ変換すると、簡単にウィンドウプログラムを作成する事が可能となります。
変換部分のサンプルプログラムは以下の通りです。
// IplImage の確保
IplImage *src_img = cvLoadImage("c:\\Sample.bmp");
// IplImageからBitmapの確保
Bitmap^ bmp = gcnew Bitmap(src_img->width, src_img->height, src_img->widthStep,
System::Drawing::Imaging::PixelFormat::Format24bppRgb, (IntPtr)src_img->imageData);
ただし、PixelFormatはIplImageで確保した画像データのdepthおよびnChannelsに合わせて設定して下さい。
PixelFormat | depth | nChannels |
Format8bppIndexed | 8 | 1 |
Format24bppRgb | 8 | 3 |
他の組合せも考えられますが、.NETやOpenCVの関数が対応していないものが多いので、実質的に、この2つの組合せになると思います。
モノクロ8Bit画像(Format8bppIndexed)の場合は、別途、カラーパレットを設定する必要があります。
カラーパレットの設定については、8Bitモノクロのカラーパレット設定方法のページを参照願います。
ここで作成したBitmapをPictureBoxに表示する場合、
pictureBox1->Image = bmp;
とすると
‘System.AccessViolationException’ のハンドルされていない例外が System.Drawing.dll で発生しました。
追加情報: 保護されているメモリに読み取りまたは書き込み操作を行おうとしました。他のメモリが壊れていることが考えられます。
というエラーメッセージが出てしまうので、DrawImageを使って
g->DrawImage(bmp, 0, 0, src_img->width, src_img->height);
というようにして、ピクチャボックスへ描画します。
ちなみに今回はIplImageからBitmapクラスへ変換する方法を紹介しましたが、BitmapクラスからIplImageへ変換する場合はBitmapクラスのポインタがガーベージコレクションにより移動してしまう可能性があるので、BitmapのポインタをLock~Unlockで取得し、IplImageのimageDataへコピーすればよいと思います。
また、OpenCV2.0からは画像データにC++用のcv::Matがありますが、cv::Matでは画像データのメモリの幅が4の倍数に調整されていないので、画像の幅が4の倍数でない場合はcv::Matと.NETのBitmapとではメモリのサイズが異なるため、そのままcv::MatのポインタをBitmapへ渡す事ができません。
そのため、cv::MatからBitmapへ変換する場合はcv::MatそのものをIplImageを確保してからcv:Matへ変換するか、画像の1行ごとにデータをコピーするなどの工夫が必要となります。
C++/CLIで簡単にできる画像の拡大縮小表示を紹介します。
今回、作成したプログラムはこんな感じ↓です。
このサンプルプログラムはこちら
SimpleZoomImage.zip
(VisualStudio2005 Express Edtionで作成)
作成手順は本当に簡単。
下図のようにフォームの上にMenuStrip、Panel、Panelの上にPictureBoxと配置していきます。
Panelをクリックすると、右上に三角マークが表示されるので、それをクリックし、
親コンテナにドッキングするをクリックします。
次にパネルとピクチャボックスのプロパティを以下のように設定します。
panel1->AutoScroll = true;
pictureBox1->SizeMode = System::Windows::Forms::PictureBoxSizeMode::Zoom;
■AutoScroll
trueに設定するとパネル上に配置されたコントロールコンテンツ(ピクチャボックスなど)がパネルの大きさより大きい場合は自動的にスクロールバーを表示し、コントロールをスクロールしてくれます。
■SizeMode
ピクチャボックスに設定されたイメージをどのように表示するかを設定します。
Normal | 画像をピクチャボックスの左上に合わせて表示します。 |
StretchImage | 画像をピクチャボックスの幅、高さに合わせて表示します。 画像をピクチャボックスの幅、高さに合わせて表示します。 |
AutoSize | ピクチャボックスのサイズを画像サイズに合わせて変更します。 |
CenterImage | ピクチャボックスの中心と画像の中心を合わせるようにして表示します。画像サイズがピクチャボックスより大きい場合は、画像がはみ出して表示されます。 |
Zoom | 画像全体をピクチャボックス全体に表示するように画像表示サイズを変更し、ピクチャボックスの中心に表示します。 画像の縦横比はくずれません。 |
画像の読み込みは、こんな感じ↓です。
private: System::Void mnuFileOpen_Click(System::Object^ sender, System::EventArgs^ e) {
//ファイルを開くダイアログの作成
OpenFileDialog^ dlg = gcnew OpenFileDialog;
//ファイルフィルタ
dlg->Filter = "画像ファイル(*.bmp,*.jpg,*.png,*.tif,*.ico)|*.bmp;*.jpg;*.png;*.tif;*.ico";
//ダイアログの表示
dlg->ShowDialog();
//ビットマップファイルから、Bitmapを作成
Bitmap^ bmp = gcnew Bitmap(dlg->FileName);
//ピクチャボックスをビットマップ画像サイズに合わせる
pictureBox1->Width = bmp->Width;
pictureBox1->Height = bmp->Height;
//ピクチャボックスのImageへ
pictureBox1->Image = bmp;
}
あとは、ピクチャボックスの大きさを拡大縮小したい倍率に合わせて変更すれば、勝手に画像は拡大縮小し、スクロールバーは自動的に調整してくれます。
private: System::Void mnuZoomEnlargement_Click(System::Object^ sender, System::EventArgs^ e) {
//拡大(2倍)
pictureBox1->Width *= 2;
pictureBox1->Height *= 2;
}
private: System::Void mnuZoomReduce_Click(System::Object^ sender, System::EventArgs^ e) {
//縮小(1/2倍)
pictureBox1->Width /= 2;
pictureBox1->Height /= 2;
}
と、これだけ!
本当に簡単にすごいことができちゃいます。
ただし、この方法では、大きい画像やモノクロの画像の上に線などの描画ができません。
その場合はDrawImageメソッドをお使い下さい。
モノクロ8Bitの画像を新規に作成する場合の、モノクロのカラーパレットは、以下のように作成します。
//8BitのBitmap作成
Bitmap^ bmp = gcnew Bitmap(pictureBox1->Width, pictureBox1->Height, Imaging::PixelFormat::Format8bppIndexed);
Imaging::ColorPalette^ pal = bmp->Palette;
for (i = 0; i < 256; i++){ pal->Entries[i] = Color::FromArgb(255, i, i, i);
}
bmp->Palette = pal;
この処理を使ったサンプルプログラムは、こちらです。
cppcli_colorpalette(Visual C++ 2005 Express Editionで作成 )
プログラムのイメージはこんな感じです。
アンマネージのC言語で作られたライブラリなどへ.NETのプログラム(マネージ)から文字列を渡す場合、System::String^からchar*へ変換する必要があります。
この場合、
System::Runtime::InteropServices::Marshal::StringToHGlobalAnsiメソッド
を使って変換します。
char*ポインタを使い終わったら
System::Runtime::InteropServices::Marshal::FreeHGlobalメソッド
でメモリを解放します。
以下、.NETのファイルを開くダイアログボックスを使ってファイル名を取得し、OpenCVの画像読込関数(cvLoadImage)へ渡す例を示します。
//ファイルを開くダイアログの作成
OpenFileDialog^ dlg = gcnew OpenFileDialog;
//ファイルフィルタ
dlg->Filter = "画像ファイル(*.bmp,*.jpg,*.png,*.tif,jp2)|*.bmp;*.jpg;*.png;*.tif;*.jp2";
//ダイアログの表示
dlg->ShowDialog();
//System::String^型のファイル名
System::String^ strFilename = dlg->FileName;
//System::String^からchar*へ変換
char* pStr = (char*)System::Runtime::InteropServices::Marshal::StringToHGlobalAnsi(strFilename).ToPointer();
//アンマネージ関数へchar*を渡す
IplImage *src_img = cvLoadImage(pStr, CV_LOAD_IMAGE_ANYDEPTH | CV_LOAD_IMAGE_ANYCOLOR);
//メモリの解放
System::Runtime::InteropServices::Marshal::FreeHGlobal((IntPtr)pStr);
例えば、Form1からForm2というフォームを表示する場合は、
表示したウィンドウしか操作できないウィンドウを開く
#include "Form2.h"
(中略)
Form2^ frm = gcnew Form2();
// モーダル表示
frm->ShowDialog(this);
表示したウィンドウと表示元のウィンドウの両方が操作できる
#include "Form2.h"
(中略)
Form2^ frm = gcnew Form2();
// モーダレス表示
frm->Show(this);
frm->Close();
のようにします。
ShowDialogおよびShowの引数で渡しているthisは無くてもよいのですが、このthisを渡す事で、
Form2からthis->Ownerを参照する事で、親のフォーム(どのフォームから呼ばれたのか?)が分かるので、thisを渡した方が何かと便利です。
また、
frm->Hide();
のようにすると、フォームそのものは破棄されずウィンドウを非表示にする事ができます。
そのため、再びウィンドウを表示したい場合は、再度
frm->Show();
を呼び出すと、Hide()する直前の状態のフォームが表示されます。
Visual Studioを使っていると、なぜか途中でツールバーのアイコンがこんな感じ↓
でおかしくなる場合があります。
でも、ちゃんと復帰方法もあります。
復帰方法
ツールボックスのウィンドウ上で右ボタン
↓
「ツールボックスのリセット」
↓
復帰完了!
マウスイベント(マウスをクリック、ダブルクリックなど)の処理を追加するには、イベント処理を行うオブジェクト(ピクチャボックスやボタンなど)を選択した状態でイベントのプロパティウィンドウを表示し、イベント処理の文字の部分をダブルクリックします。
すると、イベントハンドラが自動的に作成されます。
下記の例はMouseClickを追加した場合
this->pictureBox1->MouseClick += gcnew System::Windows::Forms::MouseEventHandler(this, &Form1::pictureBox1_MouseClick);
:
:
private: System::Void pictureBox1_MouseClick(System::Object^ sender, System::Windows::Forms::MouseEventArgs^ e) {
//この位置にイベント処理を追加する。
}
このイベントハンドラ内にイベント処理を追加していきます。
ここで注意しないといけないのが、pictureBox1_MouseClickという名前になっていますが、必ずしもpicutureBox1からのイベントとは限りません。
どのオブジェクトかは、最初の引数の System::Object^ sender を参照します。
sender のプロパティはそのまま参照できないので、下記のようにキャストします。
PictureBox^ pic = static_cast<PictureBox^>(sender);
また、マウスのどのボタンがクリックされ、どの位置でクリックされたか?などは2番目の引数の
System::Windows::Forms::MouseEventArgs^ e を参照します。
ボタンの判別例は以下の通りです。
if (e->Button == System::Windows::Forms::MouseButtons::Left){
//左ボタンクリック
MessageBox::Show("左ボタンがクリックされました。");
}else if (e->Button == System::Windows::Forms::MouseButtons::Right){
//右ボタンクリック
MessageBox::Show("右ボタンがクリックされました。");
}else if (e->Button == System::Windows::Forms::MouseButtons::Middle){
//中ボタンクリック
MessageBox::Show("中ボタンがクリックされました。");
}
クリックされた位置のX座標は e->X、Y座標は e->Y で取得できます。
また、マウスをクリックした時にShiftキー、Ctrlキー、Altキー、が押されているかどうかを判別するにはControl::ModifierKeysプロパティを参照すれば良いのですが、厳密にはマウスがクリックされたときのキーを取得するのはなく、Control::ModifierKeysプロパティが呼ばれた時のキーを取得するので、ご注意下さい。
//Shift,Ctrl,Altキーが押されているか?
Keys key = Control::ModifierKeys;
if ((key & Keys::Control) == Keys::Control){
//Controlボタンクリック
MessageBox::Show("Controlボタンがクリックされました。");
}else if ((key & Keys::Shift) == Keys::Shift){
//Shiftボタンクリック
MessageBox::Show("Shiftボタンがクリックされました。");
}else if ((key & Keys::Alt) == Keys::Alt){
//Altボタンクリック
MessageBox::Show("Altボタンがクリックされました。");
}
マウスイベントには以下のようなものがあります。
イベント | 意味 |
Click | クリックされたとき |
DoubleClick | ダブルクリックされたとき |
MouseCaptureChanged | マウスのキャプチャがなくなるとき(意味不明???) |
MouseClick | マウスでクリックされたとき |
MouseDoubleClick | マウスでダブルクリックされたとき |
MouseDown | マウスボタンが押されたとき |
MouseEnter | マウスポインタがコントロールの外側から内側に入ったとき |
MouseHover | マウスポインタがコントロール上に留まっているとき |
MouseLeave | マウスポインタがコントロールの内側から外側に出たとき |
MouseMove | マウスポインタがコントロール上を移動したとき |
MouseUp | マウスボタンが離されたとき |
MouseWheel | コントロールにフォーカスがあり、マウスホイールが動いたとき |
ClickイベントとMouseClickイベント、DoubleClickイベントとMouseDoubleClickイベントとは、ほぼ同じですが、2番目の引数が EventArgs^ e か MouseEventArgs^ e の違いがあります。
マウスの情報をより詳しく取得したい場合は Mouse××イベントをお使い下さい。
また、イベントの発生順番は
- MouseDown
- Click
- MouseClick
- MouseUp
- MouseDown
- DoubleClick
- MouseDoubleClick
- MouseUp
となります。
System::Drawing::GraphicsクラスのFromImageメソッドやSystem::Drawing::BitmapクラスのSetPixel、GetPixelメソッドで、モノクロの画像データを指定すると
追加情報: インデックス付きのピクセル形式をもつイメージからグラフィックス オブジェクトを作成することはできません。
というエラーが出る場合があります。
このインデックス付きのピクセル形式というのは、ビットマップフォーマットをご存じの方には、話が早いのですが、例えばモノクロ8Bitの画像データの場合は0~255の輝度値で画像データを指定しますが、モニタ上にモノクロの画像を表示するには、輝度値に対応したR、G、Bの値をカラーテーブルを使って指定する必要があります。
このカラーテーブルをもつファイル形式(例えば、1ビットの二値化画像、8ビットのモノクロ画像など)の画像データをインデックス付きのピクセル形式をもつイメージと言います。
画像処理ではモノクロ8Bitの画像データを使う機会が多いのですが、.NETのプログラムでは少し工夫しないといけない場合がありますが、この辺はおいおい紹介したいと思います。
新規プロジェクトの作成のページでは、何もしないだたのウィンドウ表示のプログラムを作成しましたが、このウィンドウにメニューを付けたいと思います。
と、その前に簡単にVisualStudioの操作説明をしたいと思います。
フォーム(ウィンドウ)上にメニューやボタンなどを配置するときは、ソリューションエクスプローラのタブを選択し、フォームのファイル(*.h)を選択し、デザイナの表示アイコンをクリックします。
デザイナの表示からイベント処理などのソースコードを書くときはコードの表示アイコンをクリックします。
オブジェクト(メニューやボタン、ピクチャボックスなど)の名前や大きさ、位置などの情報を設定するには右下のプロパティタブを選択し、プロパティアイコンをクリックします。
メニューをクリックしたときなどの処理(イベント処理)を表示するにはイベントアイコンをクリックします。
と、準備はここまで。
フォームファイル(*.h)を選択し、デザイナを表示します。
右側のツールボックスをタブを選択し、この中からMenuStripを選択し、フォームの上にドラッグ&ドロップします。
次にここへ入力と書かれた部分に表示するメニューの文字を記入していきます。
ここで、ファイルの文字の右側に(F)と表示されていますが、これはメニューをAltキーを使って操作するときの文字で、文字の入力時にはファイル(&F)のようにアルファベットの頭に&を付けます。
また、メニューの間に表示されている仕切り線(Separator)はここへ入力の部分に - (ハイフン)を入力します。
ここで、デフォルトではそれぞれのメニューの名前(Name)は開くOToolStripMenuItemなどとなっており、このままだとソースコードに日本語が入ってしまっていまいちなので、それぞれ下表のように名前を付けておきます。
Text | (Name) |
ファイル(&F) | mnuFile |
開く(&O) | mnuFileOpen |
終了(&X) | mnuFileExit |
この名前の付け方はVB6.0の時に使っていたプレフィックスを用いていますが、詳しく知りたい方は
このページ↓を参照すると良いと思います。
http://msdn.microsoft.com/en-us/library/aa263493(VS.60).aspx
次にメニューをクリックしたときの処理(イベント処理)を追加していきます。
メニューのイベント処理は開く(O)をダブルクリックすると、メニューをクリックした時の処理のソースコードが自動的に作成されます。このへんはVB6.0と同じです。
private: System::Void mnuFileOpen_Click(System::Object^ sender, System::EventArgs^ e) {
}
この{ } の間にメニューをクリックしたときの処理を書いていきます。
下記は開くメニューをクリックした時にファイルを開くダイアログボックスを表示し、ファイル名を眼セージボックスで表示するソースコードです。
private: System::Void mnuFileOpen_Click(System::Object^ sender, System::EventArgs^ e) {
//ファイル→ファイルを開く
//ファイルを開くダイアログの作成
OpenFileDialog^ dlg = gcnew OpenFileDialog;
//ファイルフィルタ
dlg->Filter = "画像ファイル(*.bmp,*.jpg,*.png,*.tif,*.ico)|*.bmp;*.jpg;*.png;*.tif;*.ico";
//ダイアログの表示 (Cancelボタンがクリックされた場合は何もしない)
if (dlg->ShowDialog() == System::Windows::Forms::DialogResult::Cancel) return;
//取得したファイル名の表示
MessageBox::Show(dlg->FileName);
}
終了メニューは下記の通りです。
private: System::Void mnuFileExit_Click(System::Object^ sender, System::EventArgs^ e) {
//ファイル→終了
this->Close();
}
VisualStudioで新規にC++/CLIのプログラムを作成する場合は以下の手順で行います。
VisualStudioを起動すると下図のようなウィンドウが開きます。
次に作成の文字の右側にあるプロジェクトの文字をクリックします。
すると下図ウィンドウが開きます。
ここで、
プロジェクトの種類:CLR
テンプレート:Windowsフォームアプリケーション
を選択し、プロジェクト名に適当な名前を入力し、OKボタンをクリックします。
(ソリューションのディレクトリを作成のチェックは入れておきます。)
すると下図のようなウィンドウが表示されます。
この状態で、ツールバーにある再生ボタン(デバッグ開始)をクリックすると、ビルドの確認ダイアログボックスが表示されるので、はいボタンをクリックします。
するとプログラムがビルドされ、これだけで空のウィンドウが表示されるだけのプログラムが実行されます。
ところで、ソリューションって何???と思う人もいるかと思いますが、今回はTestというプロジェクトを
作成しましたが、このとき作成されたファイルのディレクトリ構造は以下のようになっています。
Test ←ソリューションフォルダ
┣ Test ←プロジェクトフォルダ
┃ ┣ app.ico
┃ ┣ apprc
┃ ┣ AssemblyInfo.cpp
┃ ┣ Form1.h
┃ ┣ Form1.resX
┃ ┣ ReadMe.txt
┃ ┣ resource.h
┃ ┣ stdafx.cpp
┃ ┣ stdafx.cpp
┃ ┣ stdafx.h
┃ ┣ Test.cpp
┃ ┗ Test.vcproj ←プロジェクトファイル
┣ Test.ncb
┣ Test.sln ←ソリューションファイル
┗ Test.suo
となっています。
つまり、プロジェクトの1つ上の階層にソリューションが作成されているのですが、アプリケーションを開発するときに、メインのプロジェクト、ライブラリのプロジェクト、ユーザコントロールのプロジェクトなどを
同時進行で開発する場合など、各プロジェクトを取りまとめるのがソリューションの役目です。
このようにすることで、各プロジェクトをまたいでデバッグができるようになるので、うまく使うと便利な機能です。
もっとも、1つのプロジェクトしか使わない場合はソリューションは邪魔にしか思えないと思いますが、
ソリューションファイル(*.sln)は、必ず1つ作成されます。