算法|對話框管理器第二章:創建框架窗口

算法|對話框管理器第二章:創建框架窗口

文章圖片

算法|對話框管理器第二章:創建框架窗口

對話框模板包含了對話框外觀的描述 , 所以對話框管理器只是簡單地遍歷模板并按照模板的描述來創建對話框 。 這個過程十分簡單和直接 , 對話框管理器沒有太多自己的決策空間 , 它只是按照模板說的做而已 。
為了簡單起見 , 我會假設這里說的對話框管理器是一個擴展版本的對話框模板 。 它是經典的DLGTEMPLATE的超集 , 所以我講述起來就比較通用 。
另外 , 我還會跳過一些比較特殊的部分(例如WM_ENTERIDLE消息) , 因為它們和我要將的主要部分關系不大 。
同時 , 因為篇幅的原因 , 我也會將錯誤處理略過 。
最后 , 我假設你已經了解各種對話框模板的結構并忽略模板語句解析方面的問題 。
好了 , 可以開始了 。
首要任務是研究對話框樣式并將 DS_* 樣式轉換為 WS_* 和 WS_EX_* 樣式 , 如下圖所示:

問題:為什么定義了DS_CONTROL風格會導致去掉WS_CAPTION和WS_SYSMENU呢?
答案:通過簡單地添加一個樣式標志 , 使人們更容易將現有對話框轉換為 DS_CONTROL 子對話框 。
如果對話框模板中包含菜單 , 則從作為創建參數的一部分傳遞的實例句柄來加載菜單 。
hmenu = LoadMenu(hinst );
這是對話創建中的一個常用方法:傳遞給對話創建函數的實例句柄用于對話創建期間的所有資源相關活動 。
獲取對話框字體的算法如下:


請注意 , DS_SETFONT 優先于 DS_FIXEDFONT 。
一旦對話框管理器擁有字體 , 就會對其進行測量 , 以便可以使用其尺寸將對話框單元 (DLU) 轉換為像素 。對話框布局中的所有內容都在 DLU 中完成 。如果您忘記了將 DLU 轉換為像素的公式 , 這里有個提醒 。具體公式如下:
// 4 xdlu = 1 average character width
// 8 ydlu = 1 average character height
#define XDLU2Pix(xdlu) MulDiv(xdlu AveCharWidth 4)
#define YDLU2Pix(ydlu) MulDiv(ydlu AveCharHeight 8)
對話框大小來自模板:
cxDlg = XDLU2Pix(DialogTemplate.cx);
cyDlg = YDLU2Pix(DialogTemplate.cy);
模板中的對話框大小是客戶區的大小 , 所以我們也需要在非客戶區添加 。
RECT rcAdjust = { 0 0 cxDlg cyDlg ;
AdjustWindowRectEx(&rcAdjust dwStyle hmenu != NULL dwExStyle);
int cxDlg = rcAdjust.right – rcAdjust.left;
int cyDlg = rcAdjust.bottom – rcAdjust.top;
我怎么知道它是客戶區而不是包括非客戶區在內的整個窗口? 因為如果是全窗矩形 , 就不可能設計對話框! 模板設計者不知道最終用戶的系統將設置為哪些非客戶端指標 , 因此無法在設計時考慮它 。
(這是更一般規則的一個特例:如果你不確定某件事是否真實 , 問問自己 , “如果它是真實的 , 世界會是什么樣子?”如果你發現一個明顯錯誤的邏輯結果 ,那么你剛剛[通過矛盾
證明你所考慮的事情確實不是真的 。 這是一個重要的邏輯原理 , 我會一次又一次地回到 。 其實你幾天前就看到了 。 )
假設 DS_ABSALIGN 樣式未設置 , 對話框模板中給出的坐標是相對于對話框的父級的 。
POINT pt = { XDLU2Pix(DialogTemplate.x)YDLU2Pix(DialogTemplate.y) ;
ClientToScreen(hwndParent &pt);
但是如果調用者傳遞了 hwndParent = NULL 怎么辦? 在這種情況下 , 對話框位置相對于主屏幕的左上角 , 但不要這樣做 。

相關經驗推薦