Windows编程系列:通用对话框
打开文件对话框
打开文件对话框,主要用到GetOpenFileName函数,参数为指向OPENFILENAME结构体的指针
1 BOOL GetOpenFileName( 2 LPOPENFILENAME Arg1 3 );
使用方法如下:
1 #include <windows.h> 2 #include<iostream> 3 4 int main() 5 { 6 OPENFILENAME ofn{}; 7 TCHAR szFile[260]{}; //文件名缓冲区 8 9 ofn.lStructSize = sizeof(ofn); 10 ofn.hwndOwner = NULL; //设置对话框拥有者句柄 11 ofn.lpstrFile = szFile; //设置文件名缓冲区 12 ofn.lpstrFile[0] = '\0'; 13 ofn.nMaxFile = sizeof(szFile); //设置所选文件路径缓冲区最大长度 14 ofn.lpstrFilter = L"全部文件\0*.*\0文本文件\0*.txt\0"; //指定文件类型 15 ofn.nFilterIndex = 1; //文件类型选中的索引 从1开始 16 ofn.lpstrFileTitle = NULL; //选中的文件名(不包含路径,包含扩展名) 17 ofn.nMaxFileTitle = 0; //选中的文件名缓冲区最大长度 18 ofn.lpstrInitialDir = NULL; //初始路径 19 ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST; //用于初始化对话框的标志位 20 21 22 if (GetOpenFileName(&ofn) == TRUE) 23 std::wcout << ofn.lpstrFile << std::endl; 24 }
保存文件对话框
保存文件对话框和打开文件对话框的使用差不多,只需要把GetOpenFileName换成GetSaveFileName即可。
1 #include <windows.h> 2 #include<iostream> 3 4 int main() 5 { 6 OPENFILENAME ofn{}; 7 TCHAR szFile[260]{}; //文件名缓冲区 8 9 ofn.lStructSize = sizeof(ofn); 10 ofn.hwndOwner = NULL; //设置对话框拥有者句柄 11 ofn.lpstrFile = szFile; //设置文件名缓冲区 12 ofn.lpstrFile[0] = '\0'; 13 ofn.nMaxFile = sizeof(szFile); //设置所选文件路径缓冲区最大长度 14 ofn.lpstrFilter = L"全部文件\0*.*\0文本文件\0*.txt\0"; //指定文件类型 15 ofn.nFilterIndex = 1; //文件类型选中的索引 从1开始 16 ofn.lpstrFileTitle = NULL; //选中的文件名(不包含路径,包含扩展名) 17 ofn.nMaxFileTitle = 0; //选中的文件名缓冲区最大长度 18 ofn.lpstrInitialDir = NULL; //初始路径 19 ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST; //用于初始化对话框的标志位 20 21 22 if (GetSaveFileName(&ofn) == TRUE) 23 std::wcout << ofn.lpstrFile << std::endl; 24 }
选择文件夹对话框
1 #include <iostream> 2 #include<Windows.h> 3 #include<shlobj_core.h> 4 5 int main() 6 { 7 TCHAR szBuffer[MAX_PATH]{}; 8 BROWSEINFO bi{}; 9 bi.hwndOwner = ::GetForegroundWindow(); //对话框的拥有者名柄 10 bi.pszDisplayName = szBuffer; //设置选中路径缓冲区 11 bi.pidlRoot = NULL; //默认选中的根路径 12 bi.lpszTitle = L"标题"; //标题 13 bi.ulFlags = BIF_NEWDIALOGSTYLE; //对话框的选项 14 15 LPITEMIDLIST idl = SHBrowseForFolder(&bi); //显示对话框,并获取快捷方式 16 17 if (NULL == idl) 18 { 19 return 0; 20 } 21 22 SHGetPathFromIDList(idl, szBuffer); //从快捷方式获取路径 23 std::wcout << szBuffer << std::endl; 24 25 return 0; 26 }
字体选择对话框
选择字体对话框主要用到ChooseFont函数,参数为指向CHOOSEFONTA结构体的指针。
1 BOOL WINAPI ChooseFont( 2 _Inout_ LPCHOOSEFONT lpcf 3 );
使用方法如下:
1 CHOOSEFONT cf{}; 2 static LOGFONT lf{}; // 逻辑字体结构 3 static DWORD rgbCurrent = 0; // 当前字体颜色 4 HFONT hfont; 5 6 cf.lStructSize = sizeof(cf); 7 cf.hwndOwner = NULL; 8 cf.lpLogFont = &lf; //指向一个逻辑 字体 9 cf.rgbColors = rgbCurrent; 10 cf.Flags = CF_SCREENFONTS | CF_EFFECTS; //设置字体对话框选项 11 12 if (ChooseFont(&cf) == TRUE) 13 { 14 hfont = CreateFontIndirect(cf.lpLogFont); //创建逻辑字体 15 rgbCurrent = cf.rgbColors; //保存字体颜色 16 }
颜色选择对话框
颜色选择对话框主要用到ChooseColor函数,参数为指向CHOOSECOLOR 结构体的指针。
1 BOOL WINAPI ChooseColor( 2 _Inout_ LPCHOOSECOLOR lpcc 3 );
使用方法如下:
1 #include <windows.h> 2 #include<iostream> 3 4 int main() 5 { 6 CHOOSECOLOR cc{}; 7 static COLORREF acrCustClr[16]; //颜色对话框下面的16个自定义颜色存储空间 8 HBRUSH hbrush; 9 static COLORREF rgbCurrent = 0; 10 11 cc.lStructSize = sizeof(cc); 12 cc.hwndOwner = NULL; 13 cc.lpCustColors = (LPDWORD)acrCustClr; 14 cc.rgbResult = rgbCurrent; 15 cc.Flags = CC_FULLOPEN | CC_RGBINIT; 16 17 if (ChooseColor(&cc) == TRUE) 18 { 19 hbrush = CreateSolidBrush(cc.rgbResult); 20 rgbCurrent = cc.rgbResult; 21 } 22 }
打印对话框
打印对话框主要用到PrintDlg函数,参数为指向PRINTDLG结构体的指针。
1 BOOL WINAPI PrintDlg( 2 _Inout_ LPPRINTDLG lppd 3 );
使用方法如下:
1 PRINTDLG pd{}; 2 3 pd.lStructSize = sizeof(pd); 4 pd.hwndOwner = NULL; 5 pd.hDevMode = NULL; 6 pd.hDevNames = NULL; 7 pd.Flags = PD_USEDEVMODECOPIESANDCOLLATE || PD_RETURNDC; 8 pd.nCopies = 1; //设置打印份数 9 pd.nFromPage = 0xFFFF; //设置打印起始页号 10 pd.nToPage = 0xFFFF; //设置打印结束页号 11 pd.nMinPage = 1; 12 pd.nMaxPage = 0xFFFF; 13 14 if (PrintDlg(&pd) == TRUE) 15 { 16 //打印操作 17 18 //释放dc 19 DeleteDC(pd.hDC); 20 }
打印属性页对话框(打印对话框增强版)
主要用到PrintDlgEx函数,参数为指向PRINTDLGEX结构体的指针。
1 HRESULT WINAPI PrintDlgEx( 2 _Inout_ LPPRINTDLGEX lppd 3 );
使用方法:
1 #include <windows.h> 2 #include<iostream> 3 4 int main() 5 { 6 HRESULT hResult; 7 PRINTDLGEX pdx = { 0 }; 8 LPPRINTPAGERANGE pPageRanges = NULL; 9 10 //分配PRINTPAGERANGE结构体数组内存 11 pPageRanges = (LPPRINTPAGERANGE)GlobalAlloc(GPTR, 10 * sizeof(PRINTPAGERANGE)); 12 13 if (!pPageRanges) 14 return 0; 15 16 // 初始化PRINTDLGEX结构体 17 pdx.lStructSize = sizeof(PRINTDLGEX); 18 pdx.hwndOwner = GetConsoleWindow(); //拥有者窗体句柄 不能为NULL (这里使用控制台程序测试,所以传入控制台窗体句柄) 19 pdx.hDevMode = NULL; 20 pdx.hDevNames = NULL; 21 pdx.hDC = NULL; //设备上下文句柄 22 pdx.Flags = PD_RETURNDC | PD_COLLATE; 23 pdx.Flags2 = 0; 24 pdx.ExclusionFlags = 0; 25 pdx.nPageRanges = 0; 26 pdx.nMaxPageRanges = 10; 27 pdx.lpPageRanges = pPageRanges; 28 pdx.nMinPage = 1; 29 pdx.nMaxPage = 1000; 30 pdx.nCopies = 1; 31 pdx.hInstance = 0; 32 pdx.lpPrintTemplateName = NULL; 33 pdx.lpCallback = NULL; 34 pdx.nPropertyPages = 0; 35 pdx.lphPropertyPages = NULL; 36 pdx.nStartPage = START_PAGE_GENERAL; 37 pdx.dwResultAction = 0; 38 39 hResult = PrintDlgEx(&pdx); 40 41 if ((hResult == S_OK) && pdx.dwResultAction == PD_RESULT_PRINT) 42 { 43 //打印操作 44 } 45 46 //资源释放 47 if (pdx.hDevMode != NULL) 48 GlobalFree(pdx.hDevMode); 49 if (pdx.hDevNames != NULL) 50 GlobalFree(pdx.hDevNames); 51 if (pdx.lpPageRanges != NULL) 52 GlobalFree(pPageRanges); 53 54 //删除dc 55 if (pdx.hDC != NULL) 56 DeleteDC(pdx.hDC); 57 58 return 0; 59 }
页面设置对话框
页面设置对话框主要用到PageSetupDlg函数,参数为指向PAGESETUPDLG结构体的指针。
1 BOOL WINAPI PageSetupDlg( 2 _Inout_ LPPAGESETUPDLG lppsd 3 );
这里在进行页面预览时,需要设置一个钩子函数,用于对页面进行绘制。如下:
1 UINT_PTR CALLBACK PaintHook(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) 2 { 3 LPRECT lprc; 4 COLORREF crMargRect; 5 HDC hdc, hdcOld; 6 7 switch (uMsg) 8 { 9 case WM_PSD_MARGINRECT: 10 hdc = (HDC)wParam; 11 lprc = (LPRECT)lParam; 12 13 // 获取系统High light颜色 14 crMargRect = GetSysColor(COLOR_HIGHLIGHT); 15 16 //创建系统突出显示颜色的点划线笔,然后将其选择到示例页面的DC中。 17 hdcOld = (HDC)SelectObject(hdc, CreatePen(PS_DASHDOT, 1, crMargRect)); 18 19 //绘制Rectangle 20 Rectangle(hdc, lprc->left, lprc->top, lprc->right, lprc->bottom); 21 22 //还原dc 23 SelectObject(hdc, hdcOld); 24 return TRUE; 25 26 default: 27 return FALSE; 28 } 29 return TRUE; 30 }
使用方法:
1 PAGESETUPDLG psd{}; 2 3 psd.lStructSize = sizeof(psd); 4 psd.hwndOwner = GetConsoleWindow(); // 演示使用控制台窗体 5 psd.hDevMode = NULL; 6 psd.hDevNames = NULL; 7 psd.Flags = PSD_INTHOUSANDTHSOFINCHES | PSD_MARGINS | 8 PSD_ENABLEPAGEPAINTHOOK; 9 psd.rtMargin.top = 1000; 10 psd.rtMargin.left = 1250; 11 psd.rtMargin.right = 1250; 12 psd.rtMargin.bottom = 1000; 13 psd.lpfnPagePaintHook = PaintHook; 14 15 if (PageSetupDlg(&psd) == TRUE) 16 { 17 //获取纸张尺寸和边距值。 18 19 }
有趣的是,按照MSDN文档上的说明,上述部分函数已经不推荐使用了。但在最新的.NET 5中,依旧使用了这些函数。
推荐阅读:
GetOpenFileName API
https://docs.microsoft.com/en-us/windows/win32/api/commdlg/nf-commdlg-getopenfilenamea
SHBrowseForFolder
https://docs.microsoft.com/en-us/windows/win32/api/shlobj_core/nf-shlobj_core-shbrowseforfoldera
ChooseFont API
https://docs.microsoft.com/en-us/previous-versions/windows/desktop/legacy/ms646914(v=vs.85)
ChooseColor API
https://docs.microsoft.com/en-us/previous-versions/windows/desktop/legacy/ms646912(v=vs.85)
PrintDlgEx API
https://docs.microsoft.com/en-us/previous-versions/windows/desktop/legacy/ms646942(v=vs.85)