您現在的位置是:首頁 > 藝術

對話方塊管理器第二章:建立框架視窗

由 漫漫開發路 發表于 藝術2022-08-02
簡介如果對話方塊模板中包含選單,則從作為建立引數的一部分傳遞的例項控制代碼來載入選單

如何測量字型的尺寸

對話方塊模板包含了對話方塊外觀的描述,所以對話方塊管理器只是簡單地遍歷模板並按照模板的描述來建立對話方塊。這個過程十分簡單和直接,對話方塊管理器沒有太多自己的決策空間,它只是按照模板說的做而已。

為了簡單起見,我會假設這裡說的對話方塊管理器是一個擴充套件版本的對話方塊模板。它是經典的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 怎麼辦? 在這種情況下,對話方塊位置相對於主螢幕的左上角,但不要這樣做。

> 在多顯示器系統上,它會將對話方塊放在主顯示器上,即使你的程式正在輔助顯示器上執行。

> 使用者可能將他們的工作列停靠在螢幕的頂部或左側邊緣,這將覆蓋你的對話方塊。

> 即使在單顯示器系統上,你的程式也可能在螢幕的右下角執行。 將對話放在左上角不會在兩者之間建立有意義的聯絡。

> 如果你的程式的兩個副本正在執行,它們的對話方塊將精確地相互覆蓋。 我們在之前的文章中看到了這種情況的危險。

故事的寓意:始終傳遞一個hwndParent視窗,以便對話框出現在相對於程式的其餘部分有意義的位置。 (也不要只抓住 GetDesktopWindow!)

好的,我們現在已經準備好建立對話方塊:我們有它的類、它的字型、它的選單、它的大小和位置等。

哦等等,我們必須處理前面討論過的對話方塊建立的微妙之處:對話方塊總是在最初隱藏建立的。

BOOL fWasVisible = dwStyle & WS_VISIBLE;

dwStyle &= ~WS_VISIBLE;

對話方塊類和標題來自模板。 幾乎每個人都只使用預設對話方塊類,儘管我在之前的文章中解釋瞭如何使用自定義對話方塊類。

好的,現在我們有了建立視窗所需的資訊。

HWND hdlg = CreateWindowEx(dwExStyle, pszClass,

pszCaption, dwStyle & 0xFFFF0000, pt。x, pt。y,

cxDlg, cyDlg, hwndParent, hmenu, hinst, NULL);

請注意,我們過濾掉了所有低樣式位(每個類),因為我們已經將 DS_* 樣式轉換為“真實”樣式。

這就是為什麼你的對話過程沒有得到像 WM_CREATE 這樣的視窗建立訊息的原因。 在建立框架時,對話過程還沒有進入畫面。 只有在框架建立之後,對話管理器才能附加對話過程。

// Set the dialog procedure

SetWindowLongPtr(hdlg, DWLP_DLGPROC, (LPARAM)lpDlgProc);

對話方塊管理器在這一點上做了更多的擺弄,基於對話方塊模板樣式。 模板可能要求提供視窗上下文幫助 ID。 如果模板沒有指定允許調整大小、最大化或最小化的視窗樣式,則相關的選單項將從對話方塊的系統選單中刪除。

接下來是設定字型:

SetWindowFont(hdlg, hf, FALSE);

這就是為什麼你的對話過程收到的第一條訊息恰好是 WM_SETFONT:它是在設定 DWLP_DLGPROC 之後傳送的第一條訊息。 當然,這種行為將來可能會改變; 你不應該依賴訊息排序。

好的,對話方塊架現在已經建立完成了。 下一步:建立控制元件。

總結

拖拖控制元件它確實簡單,但是作業系統實現它的底層原理還真是不那麼簡單。

你可以:

> 忽略這些複雜的,繁瑣的東西,繼續”接著奏樂接著舞”。

> 苦其心志,深入研究這些細枝末節的東西。

好了,選擇權給到你這邊了。

最後

Raymond Chen的《The Old New Thing》是我非常喜歡的部落格之一,裡面有很多關於Windows的小知識,對於廣大Windows平臺開發者來說,確實十分有幫助。

本文來自:《The dialog manager, part 2: Creating the frame window》

對話方塊管理器第二章:建立框架視窗

推薦文章