コモンダイアログについては、すでに
第38章で「フォントの指定」
ダイアログボックスを使いました。このダイアログボックスから
BMPファイルを選択して「開く」ボタンを押すと、・・・
親ウィンドウのクライアント領域にビットマップイメージが
表示されます。親ウィンドウの大きさはちょうどクライアント領域に
ビットマップがぴったり収まる大きさに調整されます。
また、親ウィンドウのタイトルバーにはフルパス付きのファイル名と イメージサイズが表示されていることに注意してください。
また、メニューのオプションでフルパス付きか、ファイル名のみかを
選択できるようにしてあります。
今までは、ビットマップはリソースになっていました。
外部のファイルからビットマップを読み込むにはどうしたらよいのでしょうか。
実はWindows95ではLoadImage関数という便利なものが用意されています。
ファイルよりビットマップをロードするには、 lpszNameにファイル名、イメージタイプにIMAGE_BITMAP、 ロードフラグにLR_LOADFROMFILEを指定します。 このとき幅、高さは無視してかまいません。 後は、リソースからBMPを読み込んだときと 同じ処理です。第26章を 参照してください。HANDLE LoadImage( HINSTANCE hinst, // インスタンスハンドル LPCTSTR lpszName, // イメージの名前 UINT uType, // イメージタイプ int cxDesired, // 幅 int cyDesired, // 高さ UINT fuLoad // ロードフラグ );
次に、「ファイルを開く」ダイアログの扱いですが 次のように行います。
たったこれだけです。でも、例によってOPENFILENAME構造体の メンバが山のようにあります。サンプルのプログラムで使用した メンバのみについて解説します。他のメンバについては各自で ヘルプで調べてみてください。1.OPENFILENAME構造体に必要な事項をセットする 2.GetOpenFileName関数の呼び出し
lStructSizeはこの構造体の大きさです。typedef struct tagOFN { // ofn DWORD lStructSize; HWND hwndOwner; HINSTANCE hInstance; LPCTSTR lpstrFilter; LPTSTR lpstrCustomFilter; DWORD nMaxCustFilter; DWORD nFilterIndex; LPTSTR lpstrFile; DWORD nMaxFile; LPTSTR lpstrFileTitle; DWORD nMaxFileTitle; LPCTSTR lpstrInitialDir; LPCTSTR lpstrTitle; DWORD Flags; WORD nFileOffset; WORD nFileExtension; LPCTSTR lpstrDefExt; DWORD lCustData; LPOFNHOOKPROC lpfnHook; LPCTSTR lpTemplateName; } OPENFILENAME;
hwndOwnerはこのダイアログの親ウィンドウのハンドルです。
hInstanceは、lpTemplateNameを指定しない場合は無視します。
lpstrFilterは、その名の通りフィルタです。
"テキストファイル(*.txt)\0*.txt\0すべて(*.*)\0*.*\0\0"
などのように使います。文字列の区切りにはヌル文字(\0)を
入れます。最後はヌル文字2つ(\0\0)を使います。実際使って
みるとすぐにわかります。
lpstrFileには、GetOpenFileName関数が呼ばれるとここに フルパス付きのファイル名が格納されます。
nMaxFileは、lpstrFileの大きさです。なおMAX_PATHはwindef.hで 260と定義されています。
lpstrFileTitleには、選択されたファイル名が格納されます。
nMaxFileTitleは、lpstrFileTitleの大きさです。
Flagsには、ダイアログを作成時の細かい指定です。 ここでは、OFN_FILEMUSTEXISTを使っています。 これは、実際に存在しないファイル名をユーザーが無理矢理 入力したとき、警告が表示されます。
必要なメンバだけセットしていくわけですが、最初に memset関数でメンバを全部0で埋めておくと安全です。
次に、GetOpenFileName関数です。
引数は、先ほど定義した構造体のアドレスだけです。BOOL GetOpenFileName( LPOPENFILENAME lpofn
では、サンプルのプログラムを見てみましょう。
リソーススクリプトです。いつも書いていますが自分で作る人は windows.hと自作ヘッダーファイル(IDM_OPENなどを定義)を インクルードしてください。// imgview.rcの一部 // 自前で作る人はwindows.hと自作のヘッダをインクルード ///////////////////////////////////////////////////////////// // // Menu // MYMENU MENU DISCARDABLE BEGIN POPUP "ファイル(&F)" BEGIN MENUITEM "開く(&O)", IDM_OPEN MENUITEM SEPARATOR MENUITEM "終了(&X)", IDM_END END POPUP "オプション(&O)" BEGIN MENUITEM "フルバス付き(&P)", IDM_FULL END POPUP "ヘルプ(&H)" BEGIN MENUITEM "..について(&A)", IDM_ABOUT END END
いつもとたいして変わりませんが、enumに注意してください。オプションで使います。// imgview.cpp #define STRICT #include <windows.h> #include "resource.h" LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); BOOL InitApp(HINSTANCE); BOOL InitInstance(HINSTANCE, int); void OpenImgFiles(HWND); char szClassName[] = "imgview"; //ウィンドウクラス HINSTANCE hInst; HMENU hMenu; int show = 0; int dx, dy; //ウィンドウサイズとクライアントサイズの差 char FileName[MAX_PATH], FileTitle[64]; int filename_type; //タイトルバーに表示するファイル名の種別 //0:フルパス付きファイル名 1:ファイル名のみ enum {full, title}; int WINAPI WinMain(HINSTANCE hCurInst, HINSTANCE hPrevInst, LPSTR lpsCmdLine, int nCmdShow) { MSG msg; if (!hPrevInst) { if (!InitApp(hCurInst)) return FALSE; } if (!InitInstance(hCurInst, nCmdShow)) { return FALSE; } while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return msg.wParam; }
これは、いつもと同じです。//ウィンドウ・クラスの登録 BOOL InitApp(HINSTANCE hInst) { WNDCLASS wc; wc.style = CS_HREDRAW | CS_VREDRAW; wc.lpfnWndProc = WndProc; //プロシージャ名 wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = hInst; //インスタンス wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); wc.lpszMenuName = "MYMENU"; //メニュー名 wc.lpszClassName = (LPCSTR)szClassName; return (RegisterClass(&wc)); }
インスタンスハンドルをグローバル変数にコピーしています。 こうすると後でインスタンスハンドルを使うときに便利です。//ウィンドウの生成 BOOL InitInstance(HINSTANCE hInstance, int nCmdShow) { HWND hWnd; RECT rc0, rc1; hInst = hInstance; //インスタンスハンドルの保存 hWnd = CreateWindow(szClassName, "イメージビューア", //タイトルバーにこの名前が表示されます WS_OVERLAPPEDWINDOW, //ウィンドウの種類 CW_USEDEFAULT, //X座標 CW_USEDEFAULT, //Y座標 CW_USEDEFAULT, //幅 CW_USEDEFAULT, //高さ NULL, //親ウィンドウのハンドル、親を作るときはNULL NULL, //メニューハンドル、クラスメニューを使うときはNULL hInstance, //インスタンスハンドル NULL); if (!hWnd) return FALSE; ShowWindow(hWnd, nCmdShow); UpdateWindow(hWnd); GetWindowRect(hWnd, &rc0); GetClientRect(hWnd, &rc1); dx = (rc0.right - rc0.left) - (rc1.right - rc1.left); dy = (rc0.bottom - rc0.top) - (rc1.bottom - rc1.top); return TRUE; }
それと、後で使うのですがクライアント領域の大きさを 特定の大きさにするのは結構面倒です。
AdjustWindowRect関数はこれからウィンドウを作るときには つかえてもすでにできてしまったウィンドウの調整には使えません。
そこで、筆者は次のように考えました。
WM_PAINTの部分を注意深く見てください。 GetObject関数でビットマップの大きさを取得したら MoveWindow関数でちょうどよい大きさにウィンドウサイズを 変更しています。また、タイトルバーにファイル名を 表示しています。このとき、オプションの種類によって フルパス付きか、ファイル名のみかを区別しています。//ウィンドウプロシージャ LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp) { int id, wx, wy; HDC hdc, hdc_mem; HBITMAP hBitmap; BITMAP bmp_info; PAINTSTRUCT ps; RECT rc; char str[32], str_title[128]; char *str_org = " %d * %d"; switch (msg) { case WM_CREATE: hMenu = GetMenu(hWnd); filename_type = title; // 表示するファイル名をタイトルのみにする break; case WM_COMMAND: switch (LOWORD(wp)) { case IDM_END: SendMessage(hWnd, WM_CLOSE, 0, 0); break; case IDM_OPEN: OpenImgFiles(hWnd); break; case IDM_FULL: switch (filename_type) { case full: CheckMenuItem(hMenu, IDM_FULL, MF_BYCOMMAND | MF_UNCHECKED); filename_type = title; break; case title: CheckMenuItem(hMenu, IDM_FULL, MF_BYCOMMAND | MF_CHECKED); filename_type = full; break; } break; case IDM_ABOUT: MessageBox(hWnd, "イメージビューア\n(c)1997 Y.Kumei", "..について", MB_OK | MB_ICONINFORMATION); break; } break; case WM_PAINT: if (show == 1) { hdc = BeginPaint(hWnd, &ps); hBitmap = (HBITMAP)LoadImage(hInst, FileName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE); if (hBitmap == NULL) MessageBox(hWnd, "失敗です", "ok", MB_OK); GetObject(hBitmap, sizeof(BITMAP), &bmp_info); wx = bmp_info.bmWidth; wy = bmp_info.bmHeight; GetWindowRect(hWnd, &rc); MoveWindow(hWnd, rc.left, rc.top, wx+dx, wy+dy, TRUE); hdc_mem = CreateCompatibleDC(hdc); SelectObject(hdc_mem, hBitmap); BitBlt(hdc,0, 0, wx, wy, hdc_mem, 0, 0, SRCCOPY); DeleteDC(hdc_mem); DeleteObject(hBitmap); EndPaint(hWnd, &ps); memset(str_title, '\0', sizeof(str)); wsprintf(str, str_org, wx, wy); if (filename_type == full) strcpy(str_title, FileName); if (filename_type == title) strcpy(str_title, FileTitle); strcat(str_title, str); SetWindowText(hWnd, str_title); } else { return (DefWindowProc(hWnd, msg, wp, lp)); } break; case WM_CLOSE: id = MessageBox(hWnd, (LPCSTR)"終了してもよいですか", (LPCSTR)"終了確認", MB_YESNO | MB_ICONQUESTION); if (id == IDYES) { DestroyWindow(hWnd); } break; case WM_DESTROY: PostQuitMessage(0); break; default: return (DefWindowProc(hWnd, msg, wp, lp)); } return 0L; }
これは、あまり説明はいりませんね。ユーザーが ファイルを選択し終わったらクライアント領域を再描画 させています。変数のshowは、ウィンドウが表示されて まだ、BMPが表示されていないときのみ0で、一度この関数が 実行されてからはずっと1です。というのは、ウィンドウが できたばかりの時は、WM_PAINTメッセージが発行されても 表示するものがなくてエラーになるのを防ぐためです。void OpenImgFiles(HWND hWnd) { OPENFILENAME ofn; memset(&ofn, 0, sizeof(OPENFILENAME)); ofn.lStructSize = sizeof(OPENFILENAME); ofn.hwndOwner = hWnd; ofn.lpstrFilter = "BMP files(*.bmp)\0*.bmp\0All Files(*.*)\0*.*\0\0"; ofn.lpstrFile = FileName; ofn.nMaxFile = MAX_PATH; ofn.Flags = OFN_FILEMUSTEXIST; ofn.lpstrDefExt = "bmp"; ofn.nMaxFileTitle = 64; ofn.lpstrFileTitle = FileTitle; ofn.lpstrTitle = "粂井康孝制作・ファイルを開く!"; if (GetOpenFileName(&ofn)) { show = 1; InvalidateRect(hWnd, NULL, TRUE); } return; }
このプログラムを少し改良して、小さなビットマップを 表示するときにウィンドウがある一定の大きさより小さくならない ようにしてください。
また、アイコンを表示するように作り替えてみてください。
[SDK Index] [総合Index] [Previous Chapter] [Next Chapter]
Update Aug/28/1997 By Y.Kumei