文章目录
- 概要
- 实例代码
- 技术细节
概要
在 Windows 应用程序开发中,创建自定义对话框是一个常见需求。本文提供一份创建模态对话框的代码,供大家参考。
实例代码
下面是真实项目中使用的代码,实现的效果是,双击一个窗口,将窗口变成全屏(实际上这个全屏窗口是新建的一个以原窗口为父窗口的模态对话框窗口):
HINSTANCE g_hInstance; // 实例句柄
bool fakeMessageBox(HINSTANCE hInst, HWND hwnd, DLGPROC lpDglPrc, void* pApi) {
bool bSuccess = false;
HDC hdc = GetDC(nullptr);
if (hdc) {
NONCLIENTMETRICSW ncm = { sizeof(ncm) };
if (SystemParametersInfoW(SPI_GETNONCLIENTMETRICS, 0, &ncm, 0)) {
DialogTemplate tmp;
// Write out the extended dialog template header
tmp.Write<WORD>(1); // dialog version
tmp.Write<WORD>(0xFFFF); // extended dialog template
tmp.Write<DWORD>(0); // help ID
tmp.Write<DWORD>(0); // extended style
tmp.Write<DWORD>(DS_SETFONT | DS_FIXEDSYS | WS_POPUP | WS_SYSMENU | WS_CLIPCHILDREN);
tmp.Write<WORD>(0); // number of controls
tmp.Write<WORD>(0); // X
tmp.Write<WORD>(0); // Y
tmp.Write<WORD>(439); // width
tmp.Write<WORD>(275); // height
tmp.WriteString(L""); // no menu
tmp.WriteString(L""); // default dialog class
tmp.WriteString(L""); // title
// Next comes the font description.
// See text for discussion of fancy formula.
if (ncm.lfMessageFont.lfHeight < 0) {
ncm.lfMessageFont.lfHeight = -MulDiv(ncm.lfMessageFont.lfHeight, 72, GetDeviceCaps(hdc, LOGPIXELSY));
}
tmp.Write<WORD>((WORD)ncm.lfMessageFont.lfHeight); // point
tmp.Write<WORD>((WORD)ncm.lfMessageFont.lfWeight); // weight
tmp.Write<BYTE>(ncm.lfMessageFont.lfItalic); // Italic
tmp.Write<BYTE>(ncm.lfMessageFont.lfCharSet); // CharSet
tmp.WriteString(ncm.lfMessageFont.lfFaceName);
// Template is ready - go display it.
bSuccess = DialogBoxIndirectParam(hInst, tmp.Template(), hwnd, lpDglPrc, (LPARAM)pApi) >= 0;
}
ReleaseDC(nullptr, hdc); // fixed 11 May
}
return bSuccess;
}
// 模态对话框的消息响应函数,hwndDlg是窗口句柄。在WM_INITDIALOG消息中再创建一个子窗口hwnd
INT_PTR dlgFullScreenPrc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) {
static CPlayWnd *pPlayWnd = nullptr;
HWND hwnd = nullptr;
PAINTSTRUCT ps;
HDC hdc;
switch (uMsg) {
case WM_INITDIALOG: {
pPlayWnd = (CPlayWnd *)lParam;
const int SCREEN_WIDTH = GetSystemMetrics(SM_CXSCREEN), SCREEN_HEIGHT = GetSystemMetrics(SM_CYSCREEN);
HINSTANCE hinstance = (HINSTANCE)GetWindowLong(hwndDlg, GWL_HINSTANCE);
hwnd = CreateWindowEx(0, TEXT("STATIC"), nullptr,
/* WS_CHILD |*/WS_POPUP | WS_VISIBLE,
0,
0,
SCREEN_WIDTH,
SCREEN_HEIGHT,
hwndDlg, nullptr, hinstance, nullptr);
long lWindowStyle = GetWindowLong(hwnd, GWL_STYLE);
lWindowStyle &= ~WS_SYSMENU; // 去掉系统菜单
lWindowStyle &= ~WS_CAPTION; // 去掉标题栏
SetWindowLong(hwnd, GWL_STYLE, lWindowStyle);
} break;
case WM_LBUTTONDBLCLK: {
SendMessage(hwndDlg, WM_COMMAND, WM_DESTROY, NULL);
} break;
case WM_COMMAND: {
pPlayWnd = nullptr;
EndDialog(hwndDlg, 0);
} break;
case WM_PAINT: {
hdc = BeginPaint(hwndDlg, &ps);
RECT rect;
rect.left = 0;
rect.top = 0;
rect.right = GetSystemMetrics(SM_CXSCREEN);
rect.bottom = GetSystemMetrics(SM_CYSCREEN);
FillRect(hdc, &rect, CreateSolidBrush(RGB(0, 0, 0)));
EndPaint(hwndDlg, &ps);
} break;
case WM_ERASEBKGND: {
return true;
}
default:
return false;
}
return true;
}
void threadFullScreenProc(CPlayWnd *obj) {
// 进入全屏
fakeMessageBox(g_hInstance, obj->m_hPWnd, (DLGPROC)dlgFullScreenPrc, obj);
// 全屏退出,下面做其他操作
// ......
}
// 父窗口m_hPWnd的响应函数
LRESULT CALLBACK WndProc(HWND hWnd, UINT uMessage, WPARAM wParam, LPARAM lParam) {
// ......
case WM_LBUTTONDBLCLK: {
// 收到双击事件,通知到CPlayWnd的FullScreen()
} break;
// ......
}
class CPlayWnd {
// ......
public:
int FullScreen() {
std::thread thread = std::thread(threadFullScreenProc, this);
thread.detach();
return 0;
}
public:
HWND m_hPWnd; // 通过CreateWindowEx创建的父窗口
// ......
};
int main() {
// ......
g_hInstance = ::GetModuleHandle(NULL);
CPlayWnd wndObj;
// ......
return 0;
}
技术细节
上述代码中,m_hPWnd
是CPlayWnd
类创建的一个窗口,当它接收到鼠标双击事件后,会调用其FullScreen()方法,创建一个以它为父窗口的模态对话框窗口。这个模态对话框窗口也在其消息响应函数里创建了一个子窗口,子窗口的大小是全屏窗口。这样就实现了双击窗口,将窗口变成全屏的效果。