今回は、メニューコントロールや「ファイルを開く」ダイアログボックス、ビットマップ用のAPI関数を活用し、ビットマップビューワの作成にチャレンジしてみます。


プロジェクトの作成

ノーマル ウィンドウ ベースのプロジェクト(プロジェクト名を "BmpViewer" とします)を作成し、メニューの作成(IDは "MainMenu")、メインウィンドウへのメニューの貼り付けを行っておきましょう。
"MainWnd" のプロパティ設定

まずは、ベースとなる "MainWnd" を開き、ウィンドウ プロパティの設定を行います。
背景色のボタンをクリックし、"NULL" に設定しておきます。
メニューを "なし" から "MainMenu" に変更し、"サイズ変更枠" を "細枠" に変更しておきます。今回作るビットマップビューワは、最大化ボタンの使いみちがないため、「最大化ボタン」のチェックもはずしておきましょう。

メニューの編集

"MainMenu" を開き、下のような構成になるよう、メニューの追加を行っていきます。メニュー項目を挿入する場合は、挿入したい場所の1つ上の項目を選択し、「挿入」ボタンをクリックしていくことを思い出しましょう。メニューの編集方法は、ステップ6、ステップ7で解説しています。


キャプション ID 備考
ファイル(&F) - ポップアップ
開く(&O) IDM_OPEN -
  • - セパレータ
終了(&X) IDM_EXIT -
ヘルプ(&H) - ポップアップ
バージョン情報(&A) IDM_ABOUT -



メニュー デザインが完了したら、それぞれのメニューに対するイベント コーディングを行います。

イベント コーディング前の準備

プロジェクト ビューのFileタブから、"MainWnd.sbp" を開き、ファイルの先頭部(「ここから」、「ここまで」の間の部分)に以下の定義文を記述しておきます。これらの変数はグローバル変数として管理され、プログラム内のあらゆるプロシージャが参照することができます。

' ----------------------------------ここから----------------------------------

'ビットマップのファイル名
Dim FileName As String

'「開く」ダイアログの拡張子フィルター
Dim BitmapFileFilter As String
BitmapFileFilter = "ビットマップ ファイル(*.bmp)" + Chr$(0) + "*.bmp" + Chr$(0) + _
                  "すべてのファイル(*.*)" + Chr$(0) + "*" + Chr$(0)

' ----------------------------------ここまで----------------------------------



「開く」メニュー イベント

OPENFILENAME構造体を定義、初期化したのち、GetOpenFileName関数を呼び出しています。ユーザーが「キャンセル」ボタンを押したときはプロシージャから抜け出し(Exit Sub)、「開く」ボタンをクリックしたときは、グローバル文字列変数のFileNameにパスをコピーし、InvalidateRect関数で、hMainWndに対して再描画を要求します。このInvalidateRect関数は、後ほどコーディングするPaintイベントを呼び出し、このPaintイベント内で実際にビットマップの描画が行われる形になります。

Sub MainWnd_IDM_OPEN_MenuClick()
   Dim ofn As OPENFILENAME
   Dim buffer As String

   'ファイル名を取得
   ofn.lStructSize=Len(ofn)
   ofn.hwndOwner=hMainWnd
   ofn.lpstrFilter=StrPtr(BitmapFileFilter)
   ofn.nFilterIndex=1
   buffer=ZeroString(MAX_PATH)
   ofn.lpstrFile=StrPtr(buffer)
   ofn.nMaxFile=MAX_PATH
   If GetOpenFileName(ofn)=0 Then Exit Sub

   FileName=ofn.lpstrFile

   '再描画要求を出す
   InvalidateRect(hMainWnd,ByVal 0,1)
End Sub



「終了」メニュー イベント

終了メニューがクリックされると、WM_CLOSEメッセージを送るようにします。WM_CLOSEメッセージは、ウィンドウの「閉じる(×)」ボタンが押されたときに呼び出されるメッセージです。

Sub MainWnd_IDM_EXIT_MenuClick()
   SendMessage(hMainWnd,WM_CLOSE,0,0)
End Sub



「バージョン情報」メニュー イベント

ここは単純に、プログラム名とバージョン情報をメッセージボックスを利用して表示します。

Sub MainWnd_IDM_ABOUT_MenuClick()
   MessageBox(hMainWnd, "ビットマップ ビューワ Ver1.00","バージョン情報",MB_OK or MB_ICONINFORMATION)
End Sub



"MainWnd" のPaintイベント

"MainWnd" のRAD画面を開き、ウィンドウ内の適当な場所を右クリックしたのち、「イベント コード」をクリックして「イベント処理」ダイアログを開きます。ここでPaintイベントを選択し、「コーディング」ボタンをクリックします。下のコードがPaintイベント プロシージャになります。

まずは、LoadImage関数でFileNameが示すパスのビットマップ ファイルをロードします。
GetObject関数でビットマップの高さ、幅を取得、計算した後、ウィンドウ サイズをビットマップのサイズに変更します。
いったんメモリ デバイス コンテキスト内にビットマップを展開し、BitBlt関数でビットマップを描画します。
最後に、利用したデバイス コンテキスト、オブジェクト(ビットマップ)を破棄して終わりです。

この一連の動作が、再描画要求が出されるたびに行われることになります。再描画要求というと、ちょっとピンとこないかもしれませんが、他のウィンドウに隠れている、画面の外にはみ出しているときなどに、ウィンドウを移動したり、最前面に表示したりすると、隠れていた部分に対して再描画が要求されることになります。また、「開く」メニューのイベント プロシージャからInvalidateRect関数が呼び出されたときも、このイベントが実行されます。

Sub MainWnd_Paint(hDC As Long)
   Dim hBmp As Long              'ビットマップ ハンドル
   Dim hMemDC As Long            'メモリ内 デバイスコンテキストのハンドル
   Dim BitmapReport As BITMAP    'ビットマップ情報を格納するための構造体
   Dim dx As Long, dy As Long    'ウィンドウサイズとクライアント領域サイズの差

   'ファイルが開かれていない場合は抜け出す
   If FileName="" Then Exit Sub

   'ビットマップをロード
   hBmp=LoadImage(GetWindowLong(hMainWnd, GWL_HINSTANCE), FileName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE)
   If hBmp=0 Then
       MessageBox(hMainWnd, "ビットマップのオープンに失敗", "BmpViewer", MB_OK)
       Exit Sub
   End If

   'ビットマップのサイズを取得
   GetObject(hBmp, Len(BitmapReport), BitmapReport)

   'ウィンドウサイズとクライアント領域サイズの差を計算
   Dim rc_Window As RECT, rc_Client As RECT
   GetWindowRect(hMainWnd, rc_Window)
   GetClientRect(hMainWnd, rc_Client)
   dx = (rc_Window.right - rc_Window.left) - (rc_Client.right - rc_Client.left)
   dy = (rc_Window.bottom - rc_Window.top) - (rc_Client.bottom - rc_Client.top)

   'ウィンドウサイズをビットマップのサイズにあわせる
   MoveWindow(hMainWnd, rc_Window.left, rc_Window.top, BitmapReport.bmWidth+dx ,BitmapReport.bmHeight+dy, 1)

   hMemDC=CreateCompatibleDC(hDC)  'メモリ内にデバイスコンテキストを作成する
   SelectObject(hMemDC, hBmp)      'ビットマップを選択

   'ビットマップを描画
   BitBlt(hDC, 0, 0, BitmapReport.bmWidth, BitmapReport.bmHeight, hMemDC, 0, 0, SRCCOPY)

   DeleteDC(hMemDC)    'メモリ内のデバイスコンテキストを解放する
   DeleteObject(hBmp)  'ビットマップ ハンドルを破棄する
End Sub



これですべての作業が完了です。コンパイル、実行をしてみて、ビットマップがしっかりと表示されるかどうか、試してみて下さい。