3242438 (+0153)[+0282]
管理人のふたこと Tweet
模索中の話の続き……文字の小さい設定ダイアログを統一的かつなるべく自動的に拡大する方法
公開日:2019/11/19
文字の小さい設定ダイアログを統一的かつなるべく自動的に拡大する方法はないかと模索していた話ですが、Windows APIなネイティブなC++の範疇で、割と無理やり拡大する方法がうまくいったのでメモを残しておきます。

▲割と無理やり拡大したの図
ざっくりとした手順はこんな感じ。
- ダイアログの位置・サイズを取得し、その位置の属するモニタのワークエリア(タスクバー等を覗いたエリア)内でなるべく大きくなる倍率を計算する
- ダイアログのサイズを、その倍率で拡大する。
- ダイアログの中のすべてのコントロールをEnumChildWindows()で列挙する。この時、1つ目のコントロールからフォント情報を得て(WM_GETFONT)、フォントサイズに上記で得た倍率を掛け、太字にしたようなフォントを生成する。
- EnumChildWindows()で列挙されるすべてのコントロールに対して、上で生成したフォントを設定(WM_SETFONT)する。位置(x,y座標)にも、その倍率を掛けた座標に移動(SetWindowPos)する。
テスト用のコードはこんな感じ。(色々雑ですが)
■ダイアログサイズ強制拡大機能のクラスのヘッダファイル
#define ANCHOR_TOPLEFT 0 // ダイアログはワークエリアの左上に張り付く #define ANCHOR_TOPRIGHT 1 // ダイアログはワークエリアの右上に張り付く #define ANCHOR_BOTTOMLEFT 2 // ダイアログはワークエリアの左下に張り付く #define ANCHOR_BOTTOMRIGHT 3 // ダイアログはワークエリアの右下に張り付く #define ANCHOR_CENTER 5 // ダイアログはワークエリアの中央へ // 固定ダイアログを画面サイズまで拡大する class BIGDIALOG { private: HWND hDlg; // ダイアログのハンドル HFONT hOldFont; // 変更前のフォントを保存 HFONT hBFont; // 変更後のフォント double dBairitsu; // 倍率 public: BIGDIALOG() : // コンストラクタ hDlg(NULL), hOldFont(NULL), hBFont(NULL), dBairitsu(0) {} BIGDIALOG(HWND p_hDlg, int anchor) : // コンストラクタ+beBig hDlg(p_hDlg), hOldFont(NULL), hBFont(NULL), dBairitsu(0) {beBig(p_hDlg, anchor);} ~BIGDIALOG(); // デストラクタ bool beBig(HWND p_hDlg, int anchor); // ダイアログ拡大 bool release(); // 利用終了(デストラクタを呼び出す) friend BOOL CALLBACK cbEnumChildProc( HWND hCtrl, LPARAM lParam ); friend BOOL CALLBACK cbEnumChildProcOldFont( HWND hCtrl, LPARAM lParam ); };
■ダイアログサイズ強制拡大機能のクラスのソースファイル(上のヘッダファイルをincludeすること)
// ウィンドウ内の子要素のフォントを基に戻す BOOL CALLBACK cbEnumChildProcOldFont( HWND hCtrl, LPARAM lParam ) { BIGDIALOG *bd = (BIGDIALOG *)lParam; if (bd->hOldFont) { SendMessage(hCtrl, WM_SETFONT, (WPARAM)bd->hOldFont, MAKELPARAM(FALSE, 0)); } return TRUE; } bool BIGDIALOG::release() { if (hDlg) { if (hBFont) { // 子コントロールのフォントを元に戻す EnumChildWindows( hDlg, cbEnumChildProcOldFont, (LPARAM)this ); DeleteObject(hBFont); hBFont = NULL; } dBairitsu = 0; hDlg = NULL; hOldFont = NULL; return true; } return false; } BIGDIALOG::~BIGDIALOG() { release(); } // ウィンドウ内の子要素の位置・大きさをすべてdBairitsu倍する BOOL CALLBACK cbEnumChildProc( HWND hCtrl, LPARAM lParam ) { RECT rc; POINT po1; HWND hParent = GetParent(hCtrl); int newWidth, newHeight, newX, newY; BIGDIALOG *bd = (BIGDIALOG *)lParam; // 親ウィンドウがダイアログそのものの場合だけ調整 // (孫ウィンドウは子ウィンドウの位置とサイズを変えたときに自動で追随するようなので) if (hParent == bd->hDlg) { // 現在のダイアログのウィンドウサイズを取得し、dBairitsu倍する。 GetWindowRect(hCtrl, &rc); po1.x = rc.left; po1.y = rc.top; newWidth = (int)((rc.right - rc.left)*bd->dBairitsu); newHeight = (int)((rc.bottom - rc.top)*bd->dBairitsu); ScreenToClient(hParent, &po1); newX = (int)(po1.x * bd->dBairitsu); newY = (int)(po1.y * bd->dBairitsu); SetWindowPos(hCtrl, 0, newX, newY, newWidth, newHeight, SWP_NOZORDER); // フォントの変更。hBFontがまだ準備できていなければ、hOldFontの保存とhBFontの準備から行う if (bd->hBFont == NULL) { // 代表として1番目のコントロールのフォントハンドルを取得 bd->hOldFont = (HFONT)SendMessage(hCtrl, WM_GETFONT, 0, 0); LOGFONT lfont; // フォントハンドルからLOGFONTを取得 GetObject(bd->hOldFont, sizeof(LOGFONT), &lfont); // フォントサイズと太さのリサイズ lfont.lfHeight = (LONG)((double)lfont.lfHeight * bd->dBairitsu); lfont.lfWidth = (LONG)((double)lfont.lfWidth * bd->dBairitsu); lfont.lfWeight = bd->dBairitsu > 1 ? FW_SEMIBOLD : FW_NORMAL; // 新しいフォントの生成 bd->hBFont = CreateFontIndirect(&lfont); } if (bd->hBFont) { // 新しいフォントを設定 SendMessage(hCtrl, WM_SETFONT, (WPARAM)bd->hBFont, MAKELPARAM(FALSE, 0)); } } return TRUE; } bool BIGDIALOG::beBig(HWND p_hDlg, int anchor) { // p_hDlg がウィンドウハンドルでなければエラー if (p_hDlg == NULL || !IsWindow(p_hDlg)) { return false; } hDlg = p_hDlg; // ダイアログの座標を調べ、その座標のモニタハンドルを取得 HMONITOR hMonitor; MONITORINFOEX MonitorInfoEx; RECT rc; GetWindowRect(hDlg, &rc); POINT pt = { rc.left, rc.top }; // ダイアログの左上のモニタハンドルを取得 hMonitor = MonitorFromPoint(pt, MONITOR_DEFAULTTONEAREST); //モニター情報を取得する。MonitorInfoEx.rcWorkがワークエリア MonitorInfoEx.cbSize = sizeof(MonitorInfoEx); GetMonitorInfo(hMonitor, &MonitorInfoEx); // ワークエリアを縦に拡大するとした場合の倍率を求める double dBairitsuHeight = (double)(MonitorInfoEx.rcWork.bottom - MonitorInfoEx.rcWork.top) / (double)(rc.bottom - rc.top); // ワークエリアを横に拡大するとした場合の倍率を求める double dBairitsuWidth = (double)(MonitorInfoEx.rcWork.right - MonitorInfoEx.rcWork.left) / (double)(rc.right - rc.left); // 小さいほうの倍率を採用 dBairitsu = (dBairitsuHeight < dBairitsuWidth) ? dBairitsuHeight : dBairitsuWidth; // ダイアログのウィンドウを無理やり拡大 int newWidth = (int)((rc.right-rc.left)*dBairitsu); int newHeight = (int)((rc.bottom-rc.top)*dBairitsu); SetWindowPos(hDlg, NULL, 0, 0, newWidth, newHeight, SWP_NOMOVE | SWP_NOZORDER); // ダイアログ自身を親とする子コントロールのサイズ拡大 EnumChildWindows( hDlg, cbEnumChildProc, (LPARAM)this ); switch(anchor) { case ANCHOR_TOPLEFT: // ダイアログはワークエリアの左上に張り付く SetWindowPos(hDlg, NULL, MonitorInfoEx.rcWork.left, MonitorInfoEx.rcWork.top, 0, 0, SWP_NOSIZE | SWP_NOZORDER); break; case ANCHOR_TOPRIGHT: // ダイアログはワークエリアの右上に張り付く SetWindowPos(hDlg, NULL, MonitorInfoEx.rcWork.right-newWidth, MonitorInfoEx.rcWork.top, 0, 0, SWP_NOSIZE | SWP_NOZORDER); break; case ANCHOR_BOTTOMLEFT: // ダイアログはワークエリアの左下に張り付く SetWindowPos(hDlg, NULL, MonitorInfoEx.rcWork.left, MonitorInfoEx.rcWork.bottom-newHeight, 0, 0, SWP_NOSIZE | SWP_NOZORDER); break; case ANCHOR_BOTTOMRIGHT: // ダイアログはワークエリアの右下に張り付く SetWindowPos(hDlg, NULL, MonitorInfoEx.rcWork.right-newWidth, MonitorInfoEx.rcWork.bottom-newHeight, 0, 0, SWP_NOSIZE | SWP_NOZORDER); break; case ANCHOR_CENTER: // ダイアログはワークエリアの中央に移動する SetWindowPos(hDlg, NULL, (MonitorInfoEx.rcWork.right-MonitorInfoEx.rcWork.left)/2-newWidth/2 + MonitorInfoEx.rcWork.left, (MonitorInfoEx.rcWork.bottom-MonitorInfoEx.rcWork.top)/2-newHeight/2 + MonitorInfoEx.rcWork.top, 0, 0, SWP_NOSIZE | SWP_NOZORDER); break; default: // 何もしない break; } return true; }
■ダイアログサイズ強制拡大機能を利用したソースコード(上のヘッダファイルをincludeすること)
// ダイアログボックスのメッセージプロシージャ LRESULT CALLBACK DlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) { static BIGDIALOG bdialog; // 設定画面強制拡大 switch (message) { case WM_INITDIALOG: : : // ダイアログサイズ強制拡大 bdialog.beBig(hDlg, ANCHOR_BOTTOMRIGHT); : : return TRUE; case WM_DESTROY: : : // ダイアログサイズ強制拡大のリソース解放 bdialog.release(); : : return TRUE; } }
少し応用すると、プロパティシートについても拡大できます。プロパティシートについては、ダイアログが子ウィンドウになっていたり、WM_INITDIALOGされるタイミングがタブが選択されたときだったりと、いろいろ癖がありますが、工夫でどうにかなります。

▲割と無理やり拡大したの図2
もしかして、この作戦を応用したら、逆にダイアログを小さくすることはできないか? 具体的にはフリーソフトの「DF」がWindows 10で設定ダイアログが大きすぎて画面サイズをはみ出す件をソフトウェアの外部から制御できないか?とか、考えています。
本ページへは、自己責任の範囲内であれば自由にリンクしていただいて構いません。
本ページに掲載されている内容は、自由にお使いいただいて構いませんが、必ずしも筆者が内容を保証するものではありませんので、ご利用に際しては自己の責任においてお使いいただきますよう、お願いいたします。
このページのURLやアンカーは、サーバ運営・サイト運営・ページ運営・その他の都合により無告知で一時的あるいは永遠に消滅したり、変更したりする可能性がありますので、あらかじめご了承下さい。
本ページは、公開から1年半経過後の任意のタイミングで削除される予定です。本ページの内容は複製・公開していただいて構いません。