スケルトン
ATL/WTL AppWizardによって生成されたスケルトンには、次のような特徴があります。
ATL7.0未満の場合は「atlimpl.cpp」がインクルードされる
以下に示すのは、ATL/WTL AppWizardによって生成された「stdafx.cpp」ファイルです。
#include "stdafx.h" #if (_ATL_VER < 0x0700) #include <atlimpl.cpp> #endif //(_ATL_VER < 0x0700)
ATLのバージョンが7.0未満の場合は「atlimpl.cpp」をインクルードするようになっています。
「atlimpl.cpp」では、_ATL_MIN_CRT
が定義されている場合に独自のWinMainCRTStartup()
などを呼び出します。これにより、ATLは可能な限りCランタイムライブラリ(CRT)を使用しなくなり、プログラムのファイルサイズが小さくなります。
_ATL_MIN_CRT
が定義されています。これは、できる限りCランタイムライブラリを使用しないということを意味します。ただし、
_ATL_MIN_CRT
が定義されていると、一部のCランタイムライブラリ関数が使用できないことやC++例外処理ができないなどの制限があるため、本稿では_ATL_MIN_CRT
を使用しません。このため、プロジェクトの設定で_ATL_MIN_CRT
を削除します。_tWinMain()にCOM初期化やMSLU用のコードが追加される
ATL/WTL AppWizardによって生成された_tWinMain()
では、COM用に::CoInitialize()/::CoUninitialize()
の呼び出しやMSLU(Microsoft Layer for Unicode)用に::DefWindowProc()
の呼び出しが追加されています。
ところで、ATL/WTL AppWizard によって生成されたリソースはデフォルトで英語版です。これを日本語にするためには、リソースファイル(拡張子.rc)をテキストモードで開き、以下の部分を修正します。
///////////////////////////////////////////////////////////////////// // English (U.S.) resources #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) #ifdef _WIN32 LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US #pragma code_page(1252) #endif //_WIN32
///////////////////////////////////////////////////////////////////// // 日本語 resources #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_JPN) #ifdef _WIN32 LANGUAGE LANG_JAPANESE, SUBLANG_DEFAULT #pragma code_page(932) #endif //_WIN32
ソースコード
以下に、このサンプルプログラムのソースコードを示します。
#include <atlbase.h> #include <atlapp.h> extern CAppModule _Module; #include <atlwin.h> #include <atlcrack.h> #include <atlmisc.h> #include <atlctrls.h> #include <atlctrlx.h> // CSortListViewCtrlを使用するため #include <atlframe.h> // CDialogResizeを使用するため
class CMainDlg : public CDialogImpl<CMainDlg>, public CDialogResize<CMainDlg> { public: enum { IDD = IDD_MAINDLG }; CEdit m_edit_width; CEdit m_edit_height; CSortListViewCtrl m_list_window; // ソートリストビュー // ダイアログリサイズマップ BEGIN_DLGRESIZE_MAP(CMainDlg) DLGRESIZE_CONTROL(IDC_STATIC_WIDTH, DLSZ_MOVE_X) DLGRESIZE_CONTROL(IDC_EDIT_WIDTH, DLSZ_MOVE_X) DLGRESIZE_CONTROL(IDC_STATIC_HEIGHT, DLSZ_MOVE_X) DLGRESIZE_CONTROL(IDC_EDIT_HEIGHT, DLSZ_MOVE_X) DLGRESIZE_CONTROL(IDC_BUTTON_APPLY, DLSZ_MOVE_X) DLGRESIZE_CONTROL(IDC_BUTTON_UPDATE, DLSZ_MOVE_X) DLGRESIZE_CONTROL(IDC_LIST_WINDOW, DLSZ_SIZE_X | DLSZ_SIZE_Y) END_DLGRESIZE_MAP() // メッセージマップ BEGIN_MSG_MAP_EX(CMainDlg) MSG_WM_INITDIALOG(OnInitDialog) COMMAND_ID_HANDLER_EX(IDC_BUTTON_APPLY, OnButtonApply) COMMAND_ID_HANDLER_EX(IDC_BUTTON_UPDATE, OnButtonUpdate) COMMAND_ID_HANDLER_EX(IDOK, OnOK) COMMAND_ID_HANDLER_EX(IDCANCEL, OnCancel) // CDialogResizeクラスへのチェーン CHAIN_MSG_MAP(CDialogResize<CMainDlg>) END_MSG_MAP() LRESULT OnInitDialog(HWND hWnd, LPARAM lParam){ // スクリーンの中央に配置 CenterWindow(); // 大きいアイコン設定 HICON hIcon = AtlLoadIconImage( IDR_MAINFRAME, LR_DEFAULTCOLOR, ::GetSystemMetrics(SM_CXICON), ::GetSystemMetrics(SM_CYICON)); SetIcon(hIcon, TRUE); // 小さいアイコン設定 HICON hIconSmall = AtlLoadIconImage( IDR_MAINFRAME, LR_DEFAULTCOLOR, ::GetSystemMetrics(SM_CXSMICON), ::GetSystemMetrics(SM_CYSMICON)); SetIcon(hIconSmall, FALSE); // コントロール設定 m_edit_width = GetDlgItem(IDC_EDIT_WIDTH); m_edit_height = GetDlgItem(IDC_EDIT_HEIGHT); m_list_window.SubclassWindow(GetDlgItem(IDC_LIST_WINDOW)); m_list_window.SetExtendedListViewStyle( LVS_EX_CHECKBOXES | LVS_EX_FULLROWSELECT | LVS_EX_INFOTIP); // ソートリストビューにカラム設定 CRect rcList; m_list_window.GetWindowRect(rcList); int nScrollWidth = GetSystemMetrics(SM_CXVSCROLL); int n3DEdge = GetSystemMetrics(SM_CXEDGE); m_list_window.InsertColumn( 0, _T("タイトル"), LVCFMT_LEFT, rcList.Width() - 100 - nScrollWidth - n3DEdge * 2, -1); m_list_window.InsertColumn( 1, _T("幅"), LVCFMT_RIGHT, 50, -1); m_list_window.InsertColumn( 2, _T("高さ"), LVCFMT_RIGHT, 50, -1); // ソートタイプ設定 m_list_window.SetColumnSortType(0, LVCOLSORT_TEXT); m_list_window.SetColumnSortType(1, LVCOLSORT_LONG); m_list_window.SetColumnSortType(2, LVCOLSORT_LONG); ::EnumWindows((WNDENUMPROC)SetWindowListFunc, (LPARAM)&m_list_window); // ダイアログリサイズ初期化 DlgResize_Init(true, true, WS_THICKFRAME | WS_MAXIMIZEBOX | WS_CLIPCHILDREN); return TRUE; } static BOOL CALLBACK SetWindowListFunc(HWND hWnd, LPARAM lParam){ // ウィンドウクラス名取得 TCHAR szClassName[256]; ::GetClassName(hWnd, szClassName, sizeof(szClassName)/sizeof(TCHAR)); if(lstrcmp(szClassName, _T("IEFrame")) == 0){ CSortListViewCtrl* pList = (CSortListViewCtrl*)lParam; int index = pList->GetItemCount(); // ウィンドウのタイトル名をリストに設定 TCHAR szTitle[256]; ::GetWindowText(hWnd, szTitle, sizeof(szTitle)/sizeof(TCHAR)); pList->InsertItem(index, szTitle); // ウィンドウの幅と高さをリストに設定 CRect rcWindow; ::GetWindowRect(hWnd, rcWindow); CString tmp; tmp.Format(_T("%d"), rcWindow.Width()); pList->SetItemText(index, 1, tmp); tmp.Format(_T("%d"), rcWindow.Height()); pList->SetItemText(index, 2, tmp); // ウィンドウハンドルをリストアイテムに設定 pList->SetItemData(index, (DWORD)hWnd); } return TRUE; } void OnButtonApply(UINT uNotifyCode, int nID, HWND hWndCtl){ int width = GetDlgItemInt(IDC_EDIT_WIDTH, NULL, FALSE); int height = GetDlgItemInt(IDC_EDIT_HEIGHT, NULL, FALSE); for(int i=0; i<m_list_window.GetItemCount(); i++){ if(m_list_window.GetCheckState(i)){ HWND hWnd = (HWND)m_list_window.GetItemData(i); ::SetWindowPos(hWnd, NULL, 0, 0, width, height, SWP_NOMOVE | SWP_NOZORDER | SWP_SHOWWINDOW); } } } void OnButtonUpdate(UINT uNotifyCode, int nID, HWND hWndCtl){ m_list_window.DeleteAllItems(); ::EnumWindows((WNDENUMPROC)SetWindowListFunc, (LPARAM)&m_list_window); } void OnOK(UINT uNotifyCode, int nID, HWND hWndCtl){ EndDialog(nID); } void OnCancel(UINT uNotifyCode, int nID, HWND hWndCtl){ EndDialog(nID); } };
#include "stdafx.h" #include "resource.h" #include "maindlg.h" CAppModule _Module; int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE /*hPrevInstance*/, LPTSTR lpstrCmdLine, int nCmdShow) { HRESULT hRes = ::CoInitialize(NULL); ATLASSERT(SUCCEEDED(hRes)); ::DefWindowProc(NULL, 0, 0, 0L); AtlInitCommonControls(ICC_COOL_CLASSES | ICC_BAR_CLASSES); hRes = _Module.Init(NULL, hInstance); ATLASSERT(SUCCEEDED(hRes)); int nRet = 0; { CMainDlg dlgMain; nRet = dlgMain.DoModal(); } _Module.Term(); ::CoUninitialize(); return nRet; }
まず、ダイアログリソースに以下のコントロールを配置し、IDを次のように設定します。なお、2つのエディットコントロールはプロパティで[番号]にチェックを入れ、リストビューコントロールはプロパティで表示を[レポート]に設定します。
コントロール名 | リソースID | 説明 |
リストビュー | IDC_LIST_WINDOW | IEタイトル名リストビュー |
スタティック | IDC_STATIC_WIDTH | [幅]ラベル |
スタティック | IDC_STATIC_HEIGHT | [高さ]ラベル |
エディット | IDC_EDIT_WIDTH | [幅]エディット |
エディット | IDC_EDIT_HEIGHT | [高さ]エディット |
ボタン | IDC_BUTTON_APPLY | [適用]ボタン |
ボタン | IDC_BUTTON_UPDATE | [更新]ボタン |
次に、「stdafx.h」で「atlctrlx.h」と「atlframe.h」をインクルードします。これらはソートリストビューとダイアログリサイズを使用するために必要です。
CMainDlg
クラスでは、まず基底クラスにCDialogResize
を追加し、メッセージマップにCDialogResize
へのチェーンを追加します。CDialogResize
は内部にWM_SIZE
メッセージハンドラを用意しており、ダイアログウィンドウサイズが変更されてWM_SIZE
メッセージが送られてきたときに、ダイアログ上のコントロールの位置やサイズを調整します。
コントロールをどのように調整するかは、ダイアログリサイズマップで指定します。
BEGIN_DLGRESIZE_MAP(CMainDlg) DLGRESIZE_CONTROL(IDC_STATIC_WIDTH, DLSZ_MOVE_X) DLGRESIZE_CONTROL(IDC_EDIT_WIDTH, DLSZ_MOVE_X) DLGRESIZE_CONTROL(IDC_STATIC_HEIGHT, DLSZ_MOVE_X) DLGRESIZE_CONTROL(IDC_EDIT_HEIGHT, DLSZ_MOVE_X) DLGRESIZE_CONTROL(IDC_BUTTON_APPLY, DLSZ_MOVE_X) DLGRESIZE_CONTROL(IDC_BUTTON_UPDATE, DLSZ_MOVE_X) DLGRESIZE_CONTROL(IDC_LIST_WINDOW, DLSZ_SIZE_X | DLSZ_SIZE_Y) END_DLGRESIZE_MAP()
このマップにDLGRESIZE_CONTROL
マクロでエントリを追加することで、特定のコントロールのサイズまたは位置、または両方を変更させることができます。
DLGRESIZE_CONTROL
マクロの第1引数にはコントロールIDを指定し、第2引数にはフラグを指定します。フラグには次のような種類があります。
DLSZ_SIZE_X | ダイアログの幅が変更された時にコントロールの幅を変更 |
DLSZ_SIZE_Y | ダイアログの高さが変更された時にコントロールの高さを変更 |
DLSZ_MOVE_X | ダイアログの幅が変更された時にコントロールの位置を水平方向に移動 |
DLSZ_MOVE_Y | ダイアログの高さが変更された時にコントロールの位置を垂直方向に移動 |
DLSZ_REPAINT | コントロールのサイズや位置が変更された時にコントロールを再描画 |
ダイアログリサイズ用マクロにはコントロールをグループ化するマクロが用意されています。例えばダイアログに次のようにボタンが配置されていたとします。
それぞれのボタンにはIDC_BUTTON1
~IDC_BUTTON4
というIDを設定します。次に、ダイアログリサイズマクロに次のようなエントリを追加します。
BEGIN_DLGRESIZE_MAP(CMainDlg)
// 4つのボタンをグループ化
BEGIN_DLGRESIZE_GROUP()
DLGRESIZE_CONTROL(IDC_BUTTON1, DLSZ_SIZE_X)
DLGRESIZE_CONTROL(IDC_BUTTON2, DLSZ_SIZE_X)
DLGRESIZE_CONTROL(IDC_BUTTON3, DLSZ_SIZE_X)
DLGRESIZE_CONTROL(IDC_BUTTON4, DLSZ_SIZE_X)
END_DLGRESIZE_GROUP()
END_DLGRESIZE_MAP()
こうすると、ダイアログのサイズを変更した時に、グループ化された各ボタンのサイズと位置が、自動的に均等に配置されるようになります。
次に、WM_INITDIALOG
メッセージハンドラでDlgResize_Init()
を呼び出します。第1引数はダイアログの右下にサイズ変更用のグリップを表示するかどうかを示すbool
値、第2引数はダイアログが変更できる最小サイズをダイアログリソースのサイズに固定するかどうかを示すbool
値、第3引数はダイアログに追加するウィンドウスタイルを指定します。リサイズ可能なダイアログを作成する場合はWS_THICKFRAME
スタイルを追加する必要があります。すべての引数は省略可能です。DlgResize_Init()
の定義では、各引数のデフォルト引数は次のようになっています。
void DlgResize_Init(bool bAddGripper = true, bool bUseMinTrackSize = true, DWORD dwForceStyle = WS_CLIPCHILDREN) { ... ...
WM_INITDIALOG
メッセージハンドラではソートリストビューも設定しています。
まず、ダイアログ上のリストビューコントロールのハンドルを取得し、SubclassWindow()
を呼び出してサブクラス化します。次に拡張リストビュースタイルを設定し、カラムを追加してから各カラムにソートタイプを設定します。
ソートリストビューでは以下のソートタイプを設定することができます。
ソートタイプ | 意味 | オーバーライド可能な比較関数 |
LVCOLSORT_NONE | ソートしない | - |
LVCOLSORT_TEXT | テキスト値としてソート(デフォルト) | LVCompareText() |
LVCOLSORT_TEXTNOCASE | 大文字と小文字を区別しないテキスト値としてソート | LVCompareTextNoCase() |
LVCOLSORT_LONG | long 値としてソート | LVCompareLong() |
LVCOLSORT_DOUBLE | double 値としてソート | LVCompareDouble() |
LVCOLSORT_DECIMAL | 小数値としてソート | LVCompareDecimal() |
LVCOLSORT_DATETIME | 日時としてソート | LVCompareDouble() |
LVCOLSORT_DATE | 日付としてソート | LVCompareDouble() |
LVCOLSORT_TIME | 時間としてソート | LVCompareDouble() |
LVCOLSORT_CUSTOM | 独自のデータとしてソート | LVCompareCustom() |
カラムにソートタイプを設定しなければ、デフォルトでLVCOLSORT_TEXT
が設定されます。
ソートリストビューでは、カラムをクリックすると比較関数が呼び出されます。比較関数はオーバーライド可能で、ユーザ独自の比較コードを書くことができます。なお、ソートタイプにLVCOLSORT_CUSTOM
を設定した場合は、比較関数であるLVCompareCustom()
の内部でCompareItemsCustom()
が呼び出されるのでCompareItemsCustom()
をオーバーライドします。
ソートリストビュー設定後はEnumWindows()
を呼び出し、リストに現在表示中のIEのタイトル名、幅、高さを追加します。さらにリストアイテムにはIEのウィンドウハンドルを関連付けておきます。
[適用]ボタンを押したときのハンドラ関数OnButtonApply()
では、ソートリストビュー内のチェックされたタイトル名のウィンドウサイズを変更します。
[更新]ボタンを押したときのハンドラ関数OnButtonUpdate()
では、リストアイテムをすべて削除してから再度IEウィンドウのタイトル名を追加します。
まとめ
このように、CDialogResize
を使用すると簡単にコントロールの自動調整機能が付いたダイアログを作成することができます。なお、CDialogResize
はダイアログウィンドウだけではなく、フォームビューや通常のウィンドウにも利用できます。
最後に
本稿では、ダイアログリサイズとソートリストビューコントロールを使用しました。次回はスプリッタウィンドウやペインコンテナを扱いたいと思います。