一般情况下,我们是用不带边框的对话框来充当Tab Control的每个选项卡的内容的。
例如,主对话框IDD_TABBOX上有一个Tab Control,上面有两个选项卡,第一个选项卡用的是IDD_DIALOG1充当内容,第二个用的则是IDD_DIALOG2。IDD_DIALOG1和IDD_DIALOG2的Border属性设为None,Style属性设为Child,如下三张图所示。
但是用对话框充当选项卡内容会有烦人的深色对话框背景,和选项卡的样式并不搭,看起来也不好看,如下图所示。
其实,要去掉这个深色对话框背景很简单,只需要在每个选项卡的对话框的消息处理函数的WM_INITDIALOG里面加一句EnableThemeDialogTexture(hdlg, ETDT_ENABLETAB);就行了。有多少个选项卡,就加多少句这句话。
使用EnableThemeDialogTexture函数前,需要包含头文件Uxtheme.h和库文件Uxtheme.lib。该函数支持Windows XP系统。
#include <Uxtheme.h>
#pragma comment(lib, "Uxtheme.lib")
注意:使用这个函数后,如果程序处理了Static控件的WM_CTLCOLORSTATIC消息,直接return 0就可以用渐变背景了。不需要返回HBRUSH背景刷,SetTextColor设置的文字颜色就能生效。
运行之后是这样的:
深色的对话框背景就成功去除掉了。话说XP系统的选项卡界面真的挺好看的,是一个渐变色。
【完整C语言代码】
main.c:
/* 参考资料:https://stackoverflow.com/questions/43768291/how-to-get-correct-background-and-control-colors-in-property-pages */
#include <stdio.h>
#include <tchar.h>
#include <Windows.h>
#include <WindowsX.h>
#include <CommCtrl.h>
#include <Uxtheme.h>
#include "resource.h"
#pragma comment(lib, "comctl32.lib")
#pragma comment(linker, "\"/manifestdependency:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' language='*' publicKeyToken='6595b64144ccf1df'\"")
#pragma comment(lib, "Uxtheme.lib")
#define COLOR_BLUE RGB(0, 0, 255)
#define COLOR_RED RGB(255, 0, 0)
#define COLOR_ORANGE RGB(255, 201, 14)
#define COLOR_GREEN RGB(0, 255, 0)
COLORREF msg_color = COLOR_RED;
HFONT big_font;
HINSTANCE main_instance;
HWND main_dlg;
/* 获取指定选项卡的对话框窗口 */
HWND get_tab_page(int index)
{
HWND tabctrl;
TCITEM item;
tabctrl = GetDlgItem(main_dlg, IDC_TAB1);
item.mask = TCIF_PARAM;
TabCtrl_GetItem(tabctrl, index, &item);
return (HWND)item.lParam;
}
/* 修改文本内容和颜色 */
void change_static2_text(const char *str, COLORREF color)
{
HWND page;
page = get_tab_page(0);
msg_color = color;
SetDlgItemTextA(page, IDC_STATIC2, str);
}
/* 在下拉菜单框中显示串口列表 */
int display_comm_ports(HWND combobox)
{
char name[50];
char value[50];
DWORD index, type, namelen, valuelen;
HKEY key;
LSTATUS status;
ComboBox_ResetContent(combobox);
status = RegOpenKeyExA(HKEY_LOCAL_MACHINE, "HARDWARE\\DEVICEMAP\\SERIALCOMM", 0, KEY_READ, &key);
if (status != ERROR_SUCCESS)
return -1;
index = 0;
while (1)
{
namelen = sizeof(name);
valuelen = sizeof(value);
status = RegEnumValueA(key, index, name, &namelen, NULL, &type, (LPBYTE)value, &valuelen);
if (status != ERROR_SUCCESS)
break;
SendMessageA(combobox, CB_ADDSTRING, 0, (LPARAM)value);
index++;
}
if (index != 0)
ComboBox_SetCurSel(combobox, 0);
RegCloseKey(key);
return 0;
}
/* 选项卡1消息响应函数 */
INT_PTR CALLBACK page1_proc(HWND hdlg, UINT umsg, WPARAM wparam, LPARAM lparam)
{
int id;
switch (umsg)
{
case WM_COMMAND:
id = LOWORD(wparam);
switch (id)
{
case IDOK:
// 点击“连接”按钮后执行的代码
EnableWindow((HWND)lparam, FALSE);
SetWindowTextA((HWND)lparam, "已连接");
EnableWindow(GetDlgItem(hdlg, IDC_COMBO1), FALSE);
EnableWindow(GetDlgItem(hdlg, IDC_RADIO1), FALSE);
EnableWindow(GetDlgItem(hdlg, IDC_RADIO2), FALSE);
change_static2_text("连接成功", COLOR_GREEN);
break;
case IDC_CHECK1:
if (Button_GetCheck((HWND)lparam))
EnableWindow(GetDlgItem(hdlg, IDC_EDIT1), TRUE);
else
EnableWindow(GetDlgItem(hdlg, IDC_EDIT1), FALSE);
break;
case IDC_CHECK2:
if (Button_GetCheck((HWND)lparam))
{
EnableWindow(GetDlgItem(hdlg, IDC_EDIT2), TRUE);
EnableWindow(GetDlgItem(hdlg, IDC_SPIN1), TRUE);
}
else
{
EnableWindow(GetDlgItem(hdlg, IDC_EDIT2), FALSE);
EnableWindow(GetDlgItem(hdlg, IDC_SPIN1), FALSE);
}
break;
}
break;
case WM_CTLCOLORSTATIC:
// 设置文本颜色
id = GetDlgCtrlID((HWND)lparam);
if (id == IDC_STATIC2)
SetTextColor((HDC)wparam, msg_color);
break;
case WM_INITDIALOG:
EnableThemeDialogTexture(hdlg, ETDT_ENABLETAB); // 删除选项卡灰色背景
display_comm_ports(GetDlgItem(hdlg, IDC_COMBO1)); // 在下拉菜单框中显示串口列表
Button_SetCheck(GetDlgItem(hdlg, IDC_RADIO1), BST_CHECKED); // 选中第一个单选框
SendDlgItemMessage(hdlg, IDC_STATIC2, WM_SETFONT, (WPARAM)big_font, TRUE); // 设置文本字体
SendDlgItemMessage(hdlg, IDC_SPIN1, UDM_SETRANGE, 0, MAKELPARAM(100, 0)); // 设置数值上下调整框的调整范围
SendDlgItemMessage(hdlg, IDC_SPIN1, UDM_SETPOS, 0, 25); // 设置数值上下调整框的当前值为25 (倒数第二个参数没有作用, 一般设置为0)
break;
}
return 0;
}
/* 选项卡2消息响应函数 */
INT_PTR CALLBACK page2_proc(HWND hdlg, UINT umsg, WPARAM wparam, LPARAM lparam)
{
char name[50];
int i;
HICON icon;
HIMAGELIST imglist;
HWND list_wnd;
LVITEMA lvi;
switch (umsg)
{
case WM_INITDIALOG:
EnableThemeDialogTexture(hdlg, ETDT_ENABLETAB); // 删除选项卡灰色背景
list_wnd = GetDlgItem(hdlg, IDC_LIST1);
ListView_SetExtendedListViewStyle(list_wnd, LVS_EX_DOUBLEBUFFER); // 使用蓝色半透明选择框
// 创建图像列表
imglist = ImageList_Create(32, 32, ILC_COLOR24, 1, 1);
ListView_SetImageList(list_wnd, imglist, LVSIL_NORMAL); // 绑定图像列表(绑定后,关闭窗口时会自动删除图像列表)
icon = LoadImage(main_instance, MAKEINTRESOURCE(IDI_ICON1), IMAGE_ICON, GetSystemMetrics(SM_CXICON), GetSystemMetrics(SM_CYICON), LR_DEFAULTCOLOR); // 加载图标
ImageList_AddIcon(imglist, icon); // 将图标复制到图像列表中(注意是复制,不是绑定)
DestroyIcon(icon); // 复制完后要删除刚才加载的图标
// 添加显示项目
for (i = 0; i < 50; i++)
{
lvi.mask = LVIF_IMAGE | LVIF_TEXT;
lvi.iItem = i; // 行号
lvi.iSubItem = 0; // 列号
_snprintf_s(name, sizeof(name), sizeof(name) - 1, "测试项目%02d", i + 1); // 第三个参数MaxCount是字符数,不包括'\0',所以必须减1
lvi.pszText = name; // 名称
lvi.iImage = 0; // 使用的图标序号
lvi.iItem = (int)SendMessage(list_wnd, LVM_INSERTITEMA, 0, (LPARAM)&lvi); // 返回值为排序后的行号 (若启用了自动排序功能的话)
}
break;
}
return 0;
}
/* 加载字体 */
void create_font()
{
LOGFONTA font = {0};
font.lfCharSet = DEFAULT_CHARSET;
strcpy_s(font.lfFaceName, sizeof(font.lfFaceName), "Times New Roman");
font.lfHeight = 24;
font.lfWeight = FW_BOLD;
big_font = CreateFontIndirectA(&font);
}
/* 删除字体 */
void delete_font()
{
DeleteObject(big_font);
}
/* 初始化选项卡控件 */
void init_tabs()
{
char *titles[2] = {"第一个选项卡", "第二个选项卡"};
int pages[2] = {IDD_DIALOG1, IDD_DIALOG2};
DLGPROC procs[2] = {page1_proc, page2_proc};
int i;
HWND tabctrl, page;
RECT rect;
TCITEMA item;
UINT flags;
tabctrl = GetDlgItem(main_dlg, IDC_TAB1);
for (i = 0; i < _countof(pages); i++)
{
item.mask = TCIF_TEXT;
item.pszText = titles[i];
SendMessage(tabctrl, TCM_INSERTITEMA, i, (LPARAM)&item);
}
GetClientRect(tabctrl, &rect);
TabCtrl_AdjustRect(tabctrl, FALSE, &rect);
for (i = 0; i < _countof(pages); i++)
{
page = CreateDialog(main_instance, MAKEINTRESOURCE(pages[i]), tabctrl, procs[i]);
flags = (i == 0) ? SWP_SHOWWINDOW : 0;
SetWindowPos(page, NULL, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, flags);
item.mask = TCIF_PARAM;
item.lParam = (LPARAM)page;
TabCtrl_SetItem(tabctrl, i, &item);
}
}
/* 切换选项卡 */
void switch_tab()
{
int i, n, curr;
HWND tabctrl;
TCITEM item;
tabctrl = GetDlgItem(main_dlg, IDC_TAB1);
n = TabCtrl_GetItemCount(tabctrl);
curr = TabCtrl_GetCurSel(tabctrl);
for (i = 0; i < n; i++)
{
item.mask = TCIF_PARAM;
TabCtrl_GetItem(tabctrl, i, &item);
if (i == curr)
ShowWindow((HWND)item.lParam, SW_SHOW);
else
ShowWindow((HWND)item.lParam, SW_HIDE);
}
}
/* 主对话框(仅包含选项卡控件)消息响应函数 */
INT_PTR CALLBACK tabbox_proc(HWND hdlg, UINT umsg, WPARAM wparam, LPARAM lparam)
{
int id;
LPNMHDR lpnmhdr;
switch (umsg)
{
case WM_COMMAND:
id = LOWORD(wparam);
switch (id)
{
case IDCANCEL:
EndDialog(hdlg, 0);
main_dlg = NULL;
delete_font();
break;
}
break;
case WM_INITDIALOG:
main_dlg = hdlg;
lparam = (LPARAM)LoadImage(main_instance, MAKEINTRESOURCE(IDI_ICON1), IMAGE_ICON, GetSystemMetrics(SM_CXICON), GetSystemMetrics(SM_CYICON), LR_DEFAULTCOLOR);
SendMessage(hdlg, WM_SETICON, ICON_BIG, lparam);
lparam = (LPARAM)LoadImage(main_instance, MAKEINTRESOURCE(IDI_ICON1), IMAGE_ICON, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), LR_DEFAULTCOLOR);
SendMessage(hdlg, WM_SETICON, ICON_SMALL, lparam);
create_font();
init_tabs();
break;
case WM_NOTIFY:
lpnmhdr = (LPNMHDR)lparam;
switch (lpnmhdr->code)
{
case TCN_SELCHANGE:
switch_tab();
break;
}
break;
}
return 0;
}
/* 主函数 */
int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)
{
LRESULT ret;
main_instance = hInstance;
InitCommonControls();
ret = DialogBox(hInstance, MAKEINTRESOURCE(IDD_TABBOX), NULL, tabbox_proc);
return (int)ret;
}
resource.h:
//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ generated include file.
// Used by remove_tab_page_background.rc
//
#define IDI_ICON1 101
#define IDD_DIALOG1 102
#define IDD_DIALOG2 103
#define IDD_TABBOX 104
#define IDC_COMBO1 1001
#define IDC_RADIO1 1002
#define IDC_RADIO2 1003
#define IDC_STATIC2 1004
#define IDC_CHECK1 1005
#define IDC_EDIT1 1006
#define IDC_CHECK2 1007
#define IDC_EDIT2 1008
#define IDC_SPIN1 1009
#define IDC_LIST1 1010
#define IDC_TAB1 1011
// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE 105
#define _APS_NEXT_COMMAND_VALUE 40001
#define _APS_NEXT_CONTROL_VALUE 1012
#define _APS_NEXT_SYMED_VALUE 101
#endif
#endif
remove_tab_page_background.rc:
// Microsoft Visual C++ generated resource script.
//
#include "resource.h"
#define APSTUDIO_READONLY_SYMBOLS
/
//
// Generated from the TEXTINCLUDE 2 resource.
//
#include "afxres.h"
/
#undef APSTUDIO_READONLY_SYMBOLS
/
// 中文(简体,中国) resources
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_CHS)
LANGUAGE LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED
#ifdef APSTUDIO_INVOKED
/
//
// TEXTINCLUDE
//
1 TEXTINCLUDE
BEGIN
"resource.h\0"
END
2 TEXTINCLUDE
BEGIN
"#include ""afxres.h""\r\n"
"\0"
END
3 TEXTINCLUDE
BEGIN
"\r\n"
"\0"
END
#endif // APSTUDIO_INVOKED
/
//
// Icon
//
// Icon with lowest ID value placed first to ensure application icon
// remains consistent on all systems.
IDI_ICON1 ICON "icon1.ico"
/
//
// Dialog
//
IDD_DIALOG1 DIALOGEX 0, 0, 354, 138
STYLE DS_SETFONT | WS_CHILD | WS_SYSMENU
FONT 9, "新宋体", 400, 0, 0x0
BEGIN
LTEXT "串口号: ",IDC_STATIC,14,18,33,8
COMBOBOX IDC_COMBO1,52,15,81,30,CBS_DROPDOWN | CBS_SORT | WS_VSCROLL | WS_TABSTOP
DEFPUSHBUTTON "连接",IDOK,137,15,50,14
CTEXT "未连接串口",IDC_STATIC2,197,14,139,37,SS_CENTERIMAGE | SS_SUNKEN
CONTROL "模式1",IDC_RADIO1,"Button",BS_AUTORADIOBUTTON,52,39,36,10
CONTROL "模式2",IDC_RADIO2,"Button",BS_AUTORADIOBUTTON,105,39,36,10
CONTROL "",IDC_STATIC,"Static",SS_ETCHEDHORZ,7,63,340,1
CONTROL "电池电压",IDC_CHECK1,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,115,81,48,10
EDITTEXT IDC_EDIT1,170,79,67,14,ES_AUTOHSCROLL | WS_DISABLED
LTEXT "V",IDC_STATIC,242,82,8,8
CONTROL "温度",IDC_CHECK2,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,115,105,32,10
EDITTEXT IDC_EDIT2,170,103,40,14,ES_AUTOHSCROLL | WS_DISABLED
CONTROL "",IDC_SPIN1,"msctls_updown32",UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_AUTOBUDDY | UDS_ARROWKEYS | UDS_NOTHOUSANDS | WS_DISABLED,263,103,11,14
LTEXT "℃",IDC_STATIC,214,106,15,8
END
IDD_DIALOG2 DIALOGEX 0, 0, 354, 138
STYLE DS_SETFONT | WS_CHILD | WS_SYSMENU
FONT 9, "新宋体", 400, 0, 0x0
BEGIN
CONTROL "",IDC_LIST1,"SysListView32",WS_BORDER | WS_TABSTOP,7,7,338,121
END
IDD_TABBOX DIALOGEX 0, 0, 374, 166
STYLE DS_SETFONT | DS_MODALFRAME | DS_CENTER | WS_MINIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "移除选项卡灰色背景"
FONT 9, "新宋体", 400, 0, 0x0
BEGIN
CONTROL "",IDC_TAB1,"SysTabControl32",0x0,7,7,360,152
END
/
//
// DESIGNINFO
//
#ifdef APSTUDIO_INVOKED
GUIDELINES DESIGNINFO
BEGIN
IDD_DIALOG1, DIALOG
BEGIN
LEFTMARGIN, 7
RIGHTMARGIN, 347
TOPMARGIN, 7
END
IDD_DIALOG2, DIALOG
BEGIN
LEFTMARGIN, 7
RIGHTMARGIN, 345
TOPMARGIN, 7
END
IDD_TABBOX, DIALOG
BEGIN
LEFTMARGIN, 7
RIGHTMARGIN, 309
TOPMARGIN, 7
END
END
#endif // APSTUDIO_INVOKED
#endif // 中文(简体,中国) resources
/
#ifndef APSTUDIO_INVOKED
/
//
// Generated from the TEXTINCLUDE 3 resource.
//
/
#endif // not APSTUDIO_INVOKED