モーダルダイアログを作る
今まで、ウィンドウを使ったプログラムを作成してきました。でもちょっとした情報の表示や設定を行いたいだけの場合には、ダイアログが便利です。
また、ダイアログの場合は、VisualC++の「リソースエディタ」を用いて、WYSIWYGにダイアログを作成することが可能ですので、ちょっとしたアプリケーションの作成に向いています。
ダイアログリソースを作成する
まずダイアログリソースの作成を行います。
[ソリューション エクスプローラ]の中の、[リソースファイル]を右クリックします。
メニューから[追加(D)]-[リソース(R)...]を選択します。
[リソースの追加]ウィンドウが開くのでDialogを選択し[新規作成(N)]ボタンをクリックします。
OKとキャンセルのボタンがデフォルトで表示されています。ここにCheck Box、Static Text、Radio Button、Edit Controlを貼り付けてみます。
実行してみましょう。ダイアログの生成方法は、Windowsの場合と同じです。
モーダルダイアログ(ダイアログボックスを閉じるまで、他の操作が出来ない)で表示するソースです。
// SampleDialog001.cpp
// モーダルダイアログを作成する
#include <windows.h>
#include "resource.h"
BOOL DlgProc(HWND hDlg,UINT uMsg, WPARAM wParam,LPARAM lParam);
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine,int nShowCmd)
{
DialogBox(hInstance,MAKEINTRESOURCE(IDD_DIALOG1),NULL,DlgProc);
}
// ダイアログプロシージャ
BOOL DlgProc(HWND hDlg,UINT uMsg, WPARAM wParam,LPARAM lParam)
{
switch (uMsg){
case WM_COMMAND:
switch( LOWORD(wParam) ) {
case IDOK:
EndDialog(hDlg,IDOK);
break;
case IDCANCEL:
EndDialog(hDlg,IDCANCEL);
break;
}
break;
case WM_CLOSE:
DestroyWindow(hDlg);
break;
}
return FALSE;
}
リソースエディタでの表示と、WindowsMobile上での表示が異なっていますね。チェックボックスとラジオボタンの上下か切れてしまっています。
これを修正するには、ダイアログのフォントをWindowsMobileにデフォルトで導入されているフォントに変更します。
では、フォントをTahomaにかえてサイズを9ポイントに設定して実行してみましょう。
このように、正しく表示されました。
なぜダイアログではこのような事が起こるのでしょう。実はダイアログではフォントのサイズを基にして、ダイアログの幅、高さ、オブジェクトの幅、高さを決めているのです。ダイアログにデフォルトで設定されているフォント "MS Shell Dlg"がWindowsMobileには存在しないため、ダイアログやオブジェクトの幅が正しく設定されず、文字が切れたり、チェックボックスやラジオボタンの上下が切れてしまったのです。
ダイアログボックスには、今回作成したモーダルダイアログボックスの他に、モードレスダイアログボックスというものがあります。
モーダルダイアログボックス | ダイアログボックスを閉じるまで、他の操作が出来ません。通常のアプリケーションではよくこれを使いますね。 |
モードレスダイアログボックス | ダイアログボックスを開いている間にも他の操作が出来ます。ダイアログボックスを開いたまま、ブラウザを開いたり、電卓を開いたりすることができます。Excelの検索ダイアログがこれにあたりますね。Excelでは検索ダイアログを開いたまま操作ができますよね。 |
では、今回のプログラムの説明です。
モーダルダイアログを作るには、DialogBox関数を呼び出します。ダイアログボックスを閉じるときにはEndDialog関数を呼び出します。
DialogBox関数
モーダルダイアログを表示します。
int DialogBox(
HINSTANCE hInstance, // インスタンスハンドル
LPCTSTR lpTemplate, // ダイアログボックステンプレート
HWND hWndParent, // 親ウィンドウのハンドル
DLGPROC lpDialogFunc // ダイアログプロシージャのポインタ
);
EndDialog関数
モーダルダイアログボックスを破棄し、そのダイアログボックスに関係するすべての処理を終了します。
BOOL EndDialog(
HWND hDlg, // ダイアログボックスのハンドル
INT_PTR nResult // 返したい値
);
WinMain関数を見てください。今までのプログラムとは違い、メッセージループがありません。これは、DialogBox関数の処理はEndDialog関数が呼び出されるまで終了しないためです。
DialogBox関数のlpTemplateにはダイアログリソースを指定します。リソース識別子を指定する場合、上位ワードに 0 を、下位ワードに識別子を指定しなければなりません。MAKEINTRESOURCEマクロを使用すれはリソース識別子を作成できます。
lpDialogFuncにはダイアログプロシージャを指定します。
ダイアログプロシージャDlgProcを見てみましょう。ダイアログでは、ボタンが押されるなどのイベントが発生すると、WM_COMMANDメッセージが届きます。これだけでは、どのボタンが押されたのかわかりませんね。追加の情報がwParamに格納されています。どのボタンか判断するにはLOWORD(wParam)とします。これで押されたボタンのIDがわかります。
LOWORDは特定の値の下位ワードを取り出すマクロです。つまりwParamの下位ワードに押されたボタンのIDが格納されているのです。
サンプルでは、IDOKとIDCANCELの処理を別々に書いていますが、ダイアログプロシージャの中での処理が特にない場合はまとめて下のように書きます。EndDialogの第2引数にLOWORD(wParam)を指定するのがポイントです。
switch( LOWORD(wParam) ) {
case IDOK:
case IDCANCEL:
EndDialog(hDlg,LOWORD(wParam));
break;
}
今日はここまでです。
テーマ:Windows Mobile - ジャンル:コンピュータ
WindowsMobile特有のウィンドウ作成
今まで、通常のWindowsと同じようにメインウィンドウを作成してきました。単純にウィンドウを表示すると、下のメニューバーの部分が切れているのがわかると思います。
そこでSIP(Software Input Panel)の高さを考慮したウィンドウサイズを作成し、メニューバーを作成したいと思います。
SIPを考慮したウィンドウサイズの設定
SIPの情報を得るにはSHSipInfo関数を使用します。SIP領域を除いた有効なウィンドウサイズを取得し、MoveWindow関数でウィンドウサイズを変更しています。
SHSipInfo関数 SIPの状態を取得・設定します。
BOOL WINAPI SHSipInfo(
UINT uiAction,
UINT uiParam,
PVOID pvParam,
UINT fWinIni
);
MoveWindow関数 指定されたウィンドウの位置とサイズを変更します。
BOOL MoveWindow(
HWND hWnd, // ウィンドウのハンドル
int X, // 横方向の位置
int Y, // 縦方向の位置
int nWidth, // 幅
int nHeight, // 高さ
BOOL bRepaint // 再描画オプション
);
SHSipInfo関数はaygshell.hに含まれています。
#include <aygshell.h>
これだけだとビルド時にエラーとなってしまいます。そこで必要であればaygshell.libをリンクするようにリンカに指示を行います。
#pragma comment( lib, "aygshell.lib" )
SHSipInfo関数のほかに、SipGetInfo関数を使用することで同じようにSIPの情報を得ることが出来ますが、今回は使用しません。
BOOL SipGetInfo(
SIPINFO* pSipInfo
);
SIPの状態が変化したときは、トップレベルウィンドウに対してWM_SETTINGCHANGEイベントが通知され、ウィンドウプロシージャの第3引数 WPARAM に、SPI_SETSIPINFOがセットされます。
このイベントが通知されたら、ShShipInfo関数でSIP領域を除いた有効なウィンドウサイズを取得し、MoveWindow関数でウィンドウサイズを変更しています。
case WM_SETTINGCHANGE:
switch( wp )
{
case SPI_SETSIPINFO:
memset( &si, 0, sizeof( si ) );
si.cbSize = sizeof( si );
if( SHSipInfo( SPI_GETSIPINFO, 0, &si, 0 ) )
{
MoveWindow(
hWnd,
si.rcVisibleDesktop.left,
si.rcVisibleDesktop.top,
si.rcVisibleDesktop.right -
si.rcVisibleDesktop.left,
si.rcVisibleDesktop.bottom -
si.rcVisibleDesktop.top,
TRUE );
}
break;
}
break;
メニューバーの作成
次にメニューバーを作成します。作成にはSHCreateMenuBar関数を使用します。
BOOL SHCreateMenuBar(
SHMENUBARINFO * pmb
);
SHMENUBARINFO構造体に、メニューの情報を格納してSHCreateMenuBar関数に渡します。サンプルの例ではSHCMBF_EMPTYBARを指定することで、空のメニューバーを作成しています。
SHMENUBARINFO smi = {sizeof(SHMENUBARINFO),hWnd,SHCMBF_EMPTYBAR};
SHCreateMenuBar(&smi);
メニューの作成については、また別の機会に説明しますが、ここでは単純に見た目を整えるための空のメニューを作成して表示しています。
では、ソースファイルです。
// SampleWindow001.cpp
#include <aygshell.h>
#pragma comment( lib, "aygshell.lib" ) // 必要であれば aygshell.lib をリンクするよう、リンカに指示をする。
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
ATOM InitApp(HINSTANCE);
BOOL InitInstance(HINSTANCE, int);
WCHAR szClassName[] = L"SampleWindow001"; // ウィンドウクラス。UNICODEとしての文字列定数
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine,int nShowCmd)
{
MSG msg;
BOOL bRet;
if (!InitApp(hInstance))
return FALSE;
if (!InitInstance(hInstance,nShowCmd))
return FALSE;
while((bRet = GetMessage(&msg, NULL, 0, 0)) != 0) {
if (bRet == -1){
break;
} else {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
return (int)msg.wParam;
}
// ウィンドウクラスの登録
ATOM InitApp(HINSTANCE hInst)
{
WNDCLASSW wc;
wc.style = CS_HREDRAW|CS_VREDRAW;
wc.lpfnWndProc = WndProc; // プロシージャ名
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInst;
wc.hIcon = NULL; // 未サポート
wc.hCursor = NULL; // 未サポート
wc.hbrBackground= (HBRUSH) COLOR_WINDOW;
wc.lpszMenuName = NULL; // 未サポート
wc.lpszClassName=(LPCWSTR) szClassName;
return (RegisterClassW(&wc));
}
// ウィンドウの生成
BOOL InitInstance(HINSTANCE hInst, int nShowCmd)
{
HWND hWnd;
hWnd = CreateWindowW(szClassName,L"SampleWindow",
WS_CLIPCHILDREN, // ウィンドウの種類
CW_USEDEFAULT, // x座標
CW_USEDEFAULT, // y座標
CW_USEDEFAULT, // 幅
CW_USEDEFAULT, // 高さ
NULL, // 親ウィンドウのハンドル。親を作るのでNULL
NULL, // メニューハンドルまたは子ウィンドウID
hInst, // インスタンスハンドル
NULL);
if (!hWnd)
return FALSE;
ShowWindow(hWnd, nShowCmd);
UpdateWindow(hWnd);
return TRUE;
}
// ウィンドウプロシージャ
LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp)
{
SIPINFO si;
SHMENUBARINFO smi = {sizeof(SHMENUBARINFO),hWnd,SHCMBF_EMPTYBAR};// 空のメニューバー
switch (msg){
case WM_CREATE:
// メニューバーを作成する。
SHCreateMenuBar(&smi);
break;
case WM_SETTINGCHANGE:
switch( wp )
{
case SPI_SETSIPINFO:
// SIP領域を除いた有効なウィンドウサイズを取得し、
// MoveWindow関数でウィンドウサイズを変更する。
memset( &si, 0, sizeof( si ) );
si.cbSize = sizeof( si );
if( SHSipInfo( SPI_GETSIPINFO, 0, &si, 0 ) )
{
MoveWindow(
hWnd,
si.rcVisibleDesktop.left,
si.rcVisibleDesktop.top,
si.rcVisibleDesktop.right -
si.rcVisibleDesktop.left,
si.rcVisibleDesktop.bottom -
si.rcVisibleDesktop.top,
TRUE );
}
break;
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return (DefWindowProc(hWnd, msg, wp, lp));
}
return 0;
}
今日はここまでです。
テーマ:Windows Mobile - ジャンル:コンピュータ
リージョンの結合
WindowsMobileでは長方形のリージョンしか作成できません。そのため複雑な図形の描画には適していませんが、リージョンの結合はサポートされています。二つの長方形を組み合わせた図形をリージョンとすることが出来ます。
CombineRgn関数
int CombineRgn(
HRGN hrgnDest, // 結合先リージョンのハンドル
HRGN hrgnSrc1, // 結合元リージョンのハンドル
HRGN hrgnSrc2, // 結合元リージョンのハンドル
int fnCombineMode // リージョンの結合モード
);
hrgnDest このリージョンに、他の 2 つのリージョンを結合した結果が格納されます。結合先リージョンはどのようなものでもかまわないため、サンプルプログラムではCreateRectRgn(0,0,1,1)としています。
hrgnSrc1 1番目の結合元リージョンのハンドルを指定します。
hrgnSrc2 2番目の結合元リージョンのハンドルを指定します。
fnCombineMode 2 つのリージョンの結合方法を指定します。
fnCombineModeは次の値を指定できます。
値 | 説明 |
RGN_AND | 2 つのリージョンの重なり合う領域 |
RGN_COPY | hrgnSrc1 で識別されているリージョンのコピー |
RGN_DIFF | hrgnSrc1 リージョンのうち、hrgnSrc2 リージョンの一部ではない領域 |
RGN_OR | 両方のリージョンを結合 |
RGN_XOR | 両方のリージョンが重なり合う領域を除いた領域 |
では早速、RGN_XORで結合したリージョンでクリッピングしてみましょう。
ソースは前回「リージョン」と同じですので、ウインドウプロシージャ「WndProc関数」のみ記載します。
// ウィンドウプロシージャ
LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp)
{
PAINTSTRUCT ps;
HDC hdc;
HGDIOBJ hPen,hOldPen,hBrush,hOldBrush;
HRGN hRgnDest;
HRGN hRgn1;
HRGN hRgn2;
switch (msg){
case WM_PAINT:
hdc = BeginPaint(hWnd,&ps); // 描画処理を開始します。
hPen = CreatePen(PS_SOLID, 1, RGB(255, 0, 0)); // 赤色のペンを作成
hOldPen = SelectObject(hdc, hPen); // 作成したペンを選択する
hBrush = CreateSolidBrush(RGB(0, 255, 0)); // 緑色のブラシを作成
hOldBrush = SelectObject(hdc, hBrush); // 作成したペンを選択する
hRgn1 = CreateRectRgn(0,0,60,60); // リージョン1の作成
hRgn2 = CreateRectRgn(40,40,100,100); // リージョン2の作成
hRgnDest = CreateRectRgn(0,0,1,1); // 結合先リージョンの作成
CombineRgn(hRgnDest,hRgn1,hRgn2,RGN_XOR); // リージョン1と2を結合してhRgnDestへ格納
SelectClipRgn(hdc,hRgnDest); // デバイスコンテキストにリージョンを設定
Ellipse(hdc, 0, 0, 100, 100); // 円を描く
SelectObject(hdc, hOldPen); // ペンを元に戻す
SelectObject(hdc, hOldBrush); // ブラシを元に戻す
DeleteObject(hPen); // 作成したペンを削除
DeleteObject(hBrush); // 作成したブラシを削除
DeleteObject(hRgn1); // 作成したリージョンを削除
DeleteObject(hRgn2); // 作成したリージョンを削除
DeleteObject(hRgnDest); // 作成したリージョンを削除
EndPaint(hWnd,&ps); // 描画処理を終了します。
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return (DefWindowProc(hWnd, msg, wp, lp));
}
return 0;
}
きょうはここまでです。
テーマ:Windows Mobile - ジャンル:コンピュータ
リージョン
regionとは
地方、地域、領域などと訳されますが、WindowsAPIでは、多角形で定義された領域を表す図形要素を表します。
日本語では、図形領域、とでも訳せばわかりやすいでしょうか。
リージョンを作成するにはCreateRectRgn関数を使用します。
HRGN CreateRectRgn(
int nLeftRect, // 左上隅のX座標
int nTopRect, // 左上隅のY座標
int nRightRect, // 右下隅のX座標
int nBottomRect // 右下隅のY座標
);
リージョンは領域を表す情報なので、リージョンを作っただけでは画面には何も描画されません。これだけでは利用価値がありませんが、通常はクリッピングリージョンとして利用します。
リージョンをデバイスコンテキストに設定すると、そのリージョンの範囲内のみが描画可能な領域となるのです。このように画面の一部分だけを描画可能にすることをクリッピングといいます。リージョンをデバイスコンテキストに割り当てるにはSelectClipRgn関数を使用します。
int SelectClipRgn(
HDC hdc, // デバイスコンテキストのハンドル
HRGN hrgn // リージョンのハンドル
);
作成したリージョンは、他のオブジェクト同様、DeleteObject関数で破棄します。
さて、ではリージョンを使った描画をおこなってみましょう。
// gdi008.cpp
//
// リージョンによるクリッピング
#include <windows.h>
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
ATOM InitApp(HINSTANCE);
HWND InitInstance(HINSTANCE, int);
WCHAR szClassName[] = _T("gdi008"); // ウィンドウクラス。UNICODEとしての文字列定数
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine,int nShowCmd)
{
MSG msg;
BOOL bRet;
HWND hWnd;
if (!InitApp(hInstance))
return FALSE;
if (!(hWnd = InitInstance(hInstance,nShowCmd)))
return FALSE;
while((bRet = GetMessage(&msg, NULL, 0, 0)) != 0) {
if (bRet == -1){
break;
} else {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
return (int)msg.wParam;
}
// ウィンドウクラスの登録
ATOM 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 = NULL; // 未サポート
wc.hCursor = NULL; // 未サポート
wc.hbrBackground= (HBRUSH) COLOR_WINDOW;
wc.lpszMenuName = NULL; // 未サポート
wc.lpszClassName=(LPCTSTR) szClassName;
return (RegisterClass(&wc));
}
// ウィンドウの生成
HWND InitInstance(HINSTANCE hInst, int nShowCmd)
{
HWND hWnd;
hWnd = CreateWindow(szClassName,_T("Window Title"),
WS_CLIPCHILDREN, // ウィンドウの種類
CW_USEDEFAULT, // x座標
CW_USEDEFAULT, // y座標
CW_USEDEFAULT, // 幅
CW_USEDEFAULT, // 高さ
NULL, // 親ウィンドウのハンドル。親を作るのでNULL
NULL, // メニューハンドルまたは子ウィンドウID
hInst, // インスタンスハンドル
NULL);
if (!hWnd)
return FALSE;
ShowWindow(hWnd, nShowCmd);
UpdateWindow(hWnd);
return hWnd;
}
// ウィンドウプロシージャ
LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp)
{
PAINTSTRUCT ps;
HDC hdc;
HGDIOBJ hPen,hOldPen,hBrush,hOldBrush;
HRGN hRgn;
switch (msg){
case WM_PAINT:
hdc = BeginPaint(hWnd,&ps); // 描画処理を開始します。
hPen = CreatePen(PS_SOLID, 1, RGB(255, 0, 0)); // 赤色のペンを作成
hOldPen = SelectObject(hdc, hPen); // 作成したペンを選択する
hBrush = CreateSolidBrush(RGB(0, 255, 0)); // 緑色のブラシを作成
hOldBrush = SelectObject(hdc, hBrush); // 作成したペンを選択する
hRgn = CreateRectRgn(0,20,100,80); // リージョンの作成
SelectClipRgn(hdc,hRgn); // デバイスコンテキストにリージョンを設定
Ellipse(hdc, 0, 0, 100, 100); // 円を描く
SelectObject(hdc, hOldPen); // ペンを元に戻す
SelectObject(hdc, hOldBrush); // ブラシを元に戻す
DeleteObject(hPen); // 作成したペンを削除
DeleteObject(hBrush); // 作成したブラシを削除
DeleteObject(hRgn); // 作成したリージョンを削除
EndPaint(hWnd,&ps); // 描画処理を終了します。
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return (DefWindowProc(hWnd, msg, wp, lp));
}
return 0;
}
描画領域が 横長の長方形リージョンに限定することで、円の上下がリージョンからはみ出るために描画されず切り取られました。
リージョンは、XPやVistaなどのデスクトップ用Windowsであれば長方形・多角形・円形などのリージョンを駆使して、複雑な図形を表現するのに使われるもので、高度な描画処理には欠かせません。しかし、WindowsMobileの場合、長方形のリージョンしか扱うことが出来ません。
円や、多角形を用いたリージョンは用意されていなのです。
そのため、リージョンを用いた複雑な描画は期待できませんね。
またWindowsには、リージョンとよく似た機能でありリージョンと同時に説明されることの多い「パス」という機能があります。
パスは直線と曲線を表すもので、複雑な図形をあらわことができます。このパスをリージョンに変換することができるため、複雑な描画処理に使われます。
しかしながらWindowsMobileではパスは一切使用できないようです。
まとめると、WindowsMobileにおいては、リージョンを用いた複雑な図形の描画は出来ない、ということになりますね。
今日はここまでです。
テーマ:Windows Mobile - ジャンル:コンピュータ
日 | 月 | 火 | 水 | 木 | 金 | 土 |
---|---|---|---|---|---|---|
- | - | - | 1 | 2 | 3 | 4 |
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 | - |