本文介绍了为什么我的程序的选项卡控件以块状方式呈现其背景,而标准的“窗口"对话框却没有?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

tl; dr;适合那些阅读旧问题的人:新情况使我看上去更深入,我发现这会单独影响裸露的Tab控件;我已对问题进行了调整以补偿.如果我应该完全删除旧的问题文本,请告诉我.

这是我正在测试的包装程序库的屏幕快照:

如果您仔细观察,则右侧的窗口看起来很块状,而左侧的窗口(标准Windows资源管理器文件属性属性表)看起来很平滑.起初我以为这只是,但其他人也看到了,甚至可以调整图像颜色以显示出块状效果:

绘制代码的本质很简单:

 //不是实际的代码,只是用于证明我正在*思考*的算法dc = BeginPaint(hwnd,& ps);父= GetAncestor(hwnd,GA_PARENT);GetClientRect(父母,& r);cdc = CreateCompatibleDC(dc);位图= CreateCompatibleBitmap(dc,r.right-r.left,r.bottom-r.top);SelectObject(cdc,pd->位图);SendMessageW(父级,WM_PRINTCLIENT,(WPARAM)cdc,PRF_CLIENT);updateRect = ps.rcPaint;parentRect = updateRect;MapWindowRect(hwnd,parent,& parentRect);BitBlt(dc,updateRect.left,updateRect.top,updateRect.right-updateRect.left,updateRect.bottom-updateRect.top,cdc,parentRect.left,parentRect.top,SRCCOPY); 

除非看不到兼容位图有关的内容,否则我在这里看不到任何会影响绘制图像质量的东西?有人可以解释吗?我真的不知道该怎么办,除了重新为每个可能的容器父级重新绘制图形并希望对您有所帮助.

谢谢.

这是容器的完整代码:

 //2015年4月26日#include"uipriv_windows.h"#define containerClass L"libui_uiContainerClass"HWND initialParent;结构容器{HWND hwnd;uiContainer *父级;隐藏HBRUSH刷;};静态HWND realParent(HWND hwnd){HWND父母;int类父= hwnd;为了 (;;) {父母= GetAncestor(父母,GA_PARENT);//跳过组框;它们(应该是透明的)//跳过uiContainers;他们什么都没画class = windowClassOf(parent,L"button",containerClass,NULL);if(class!= 0&& class!= 1)休息;}返回父母}struct parentDraw {HDC直流电;HBITMAP位图;HBITMAP prevbitmap;};静态void parentDraw(HDC dc,HWND parent,struct parentDraw * pd){RECT r;如果(GetClientRect(parent,& r)== 0)logLastError(在parentDraw()中获取父项的客户端时出错");pd-> cdc = CreateCompatibleDC(dc);如果(pd-> cdc == NULL)logLastError(在parentDraw()中创建兼容的DC时出错");pd->位图= CreateCompatibleBitmap(dc,r.right-r.left,r.bottom-r.top);if(pd->位图== NULL)logLastError(在parentDraw()中创建兼容位图时出错");pd-> prevbitmap = SelectObject(pd-> cdc,pd->位图);如果(pd-> prevbitmap == NULL)logLastError(将位图选择到parentDraw()的兼容DC中时出错");SendMessageW(父级,WM_PRINTCLIENT,(WPARAM)(pd-> cdc),PRF_CLIENT);}静态void endParentDraw(struct parentDraw * pd){如果(SelectObject(pd-> cdc,pd-> prevbitmap)!= pd-> bitmap)logLastError(将先前的位图选择回到endParentDraw()中的兼容DC时出错");如果(DeleteObject(pd-> bitmap)== 0)logLastError(删除endParentDraw()中的兼容位图时出错");如果(DeleteDC(pd-> cdc)== 0)logLastError(在endParentDraw()中删除兼容的DC时出错");}//参见http://www.codeproject.com/Articles/5978/Correctly-drawn-themed-dialogs-in-WinXP静态HBRUSH getControlBackgroundBrush(HWND hwnd,HDC dc){HWND父母;RECT hwndScreenRect;struct parentDraw pd;HBRUSH刷;父= realParent(hwnd);parentDraw(dc,parent,& pd);brush = CreatePatternBrush(pd.bitmap);如果(画笔== NULL)logLastError(在getControlBackgroundBrush()中创建模式画笔时发生错误");endParentDraw(& pd);//现在找出控件相对于父控件的位置,以便我们正确对齐画笔如果(GetWindowRect(hwnd,& hwndScreenRect)== 0)logLastError(在getControlBackgroundBrush()中获取控制窗口错误时发生错误");//这将在屏幕坐标中;转换为父坐标mapWindowRect(NULL,parent,&hwndScreenRect);如果(SetBrushOrgEx(dc,-hwndScreenRect.left,-hwndScreenRect.top,NULL)== 0)logLastError(在getControlBackgroundBrush()中错误设置画笔原点");回程刷}静态无效的paintContainerBackground(HWND hwnd,HDC dc,RECT * paintRect){HWND父母;RECT paintRectParent;struct parentDraw pd;父= realParent(hwnd);parentDraw(dc,parent,& pd);paintRectParent = * paintRect;mapWindowRect(hwnd,parent,& paintRectParent);如果(BitBlt(dc,paintRect-> left,paintRect-> top,paintRect-> right-paintRect-> left,paintRect-> bottom-paintRect-> top,pd.cdc,paintRectParent.left,paintRectParent.top,SRCCOPY)== 0)logLastError(在paintContainerBackground()中在uiContainer上绘制父背景时出错,");endParentDraw(& pd);}//来自https://msdn.microsoft.com/zh-CN/library/windows/desktop/dn742486.aspx#sizingandspacing和https://msdn.microsoft.com/en-us/library/windows/desktop/bb226818%28v = vs.85%29.aspx//这个X值实际上仅用于按钮,但是我看不到更好的一个:/#define winXP添加4#define winYPadding 4//如果某些操作失败并且中止调整大小,那么我们就没有进行调整大小所需的内容静态HRESULT调整大小(uiContainer * cc,RECT * r){结构容器* c =(结构容器*)(uiControl(cc)->内部);调整d;uiSizingSys sys;HDC直流;HFONT prevfont;TEXTMETRICW tm;SIZE大小;dc = GetDC(c-> hwnd);如果(dc == NULL)return logLastError(在resize()中获取DC时出错");prevfont =(HFONT)SelectObject(dc,hMessageFont);如果(prevfont == NULL)返回logLastError(将控件字体加载到resize()中的设备上下文中时出错");ZeroMemory(& tm,sizeof(TEXTMETRICW));如果(GetTextMetricsW(dc,& tm)== 0)返回logLastError(在resize()中获取文本指标时出错");如果(GetTextExtentPoint32W(dc,L"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz",52,& size)== 0)返回logLastError(在resize()中获取文本范围点时出错");sys.baseX =(int)((size.cx/26 +1)/2);sys.baseY =(int)tm.tmHeight;sys.internalLeading = tm.tmInternalLeading;如果(SelectObject(dc,prevfont)!= hMessageFont)return logLastError(将先前的字体还原到resize()中的设备上下文时出错");如果(ReleaseDC(c-> hwnd,dc)== 0)返回logLastError(释放resi​​ze()中的DC时出错");d.xPadding = uiDlgUnitsToX(winXPadding,sys.baseX);d.yPadding = uiDlgUnitsToY(winYPadding,sys.baseY);d.sys =& sys;uiContainerResizeChildren(cc,r-> left,r-> top,r-> right-r-> left,r-> bottom-r-> top,& d);返回S_OK;}静态LRESULT CALLBACK containerWndProc(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam){uiContainer * cc;struct容器* c;CREATESTRUCTW * cs =(CREATESTRUCTW *)lParam;HWND控制;NMHDR * nm =(NMHDR *)lParam;WINDOWPOS * wp =(WINDOWPOS *)lParam;RECT r;HDC直流;PAINTSTRUCT ps;cc = uiContainer(GetWindowLongPtrW(hwnd,GWLP_USERDATA));如果(cc == NULL)如果(uMsg == WM_NCCREATE)SetWindowLongPtrW(hwnd,GWLP_USERDATA,(LONG_PTR)(cs-> lpCreateParams));//不要在这里返回DEFWINDOWPROC()//查看下一个注释块,为什么//相反,我们只是稍后再次检查c == NULL开关(uMsg){//这些必须始终运行,即使在初始父级上也是如此//为什么?http://blogs.msdn.com/b/oldnewthing/archive/2010/03/16/9979112.aspx案例WM_COMMAND://跳回到有问题的控件//除非是初始父级,否则在这种情况下,就像忽略了该消息一样控制=(HWND)lParam;if(控制项!= NULL&& IsChild(ini​​tialParent,控制项)== 0)返回SendMessageW(control,msgCOMMAND,wParam,lParam);休息;//落入DefWindowProcW()案例WM_NOTIFY://与WM_COMMAND相同控制= nm-> hwndFrom;if(控制项!= NULL&& IsChild(ini​​tialParent,控制项)== 0)返回SendMessageW(control,msgNOTIFY,wParam,lParam);休息;//这些仅在c不为NULL时运行案例WM_CTLCOLORSTATIC:案例WM_CTLCOLORBTN:如果(cc == NULL)休息;c =(结构容器*)(uiControl(cc)->内部);如果(c-> brush!= NULL)如果(DeleteObject(c-> brush)== 0)logLastError(删除containerWndProc()中的旧背景画笔时出错");/* TODO//只读的TextField和Textbox被免除//这是因为只读编辑控件的计数在WM_CTLCOLORSTATIC下如果(windowClassOf(((HWND)lParam,L"edit",NULL)== 0)如果(textfieldReadOnly((HWND)lParam))返回DefWindowProcW(hwnd,uMsg,wParam,lParam);*/如果(SetBkMode((HDC)wParam,TRANSPARENT)== 0)logLastError(将透明背景模式设置为containerWndProc()中的控件时出错");c->画笔= getControlBackgroundBrush(((HWND)lParam,(HDC)wParam);return(LRESULT)(c-> brush);情况WM_PAINT:如果(cc == NULL)休息;c =(结构容器*)(uiControl(cc)->内部);dc = BeginPaint(c-> hwnd,& ps);如果(dc == NULL)logLastError(在容器WndProc()中开始绘制容器油漆时出错");r = ps.rcPaint;paintContainerBackground(c-> hwnd,dc,& r);EndPaint(c-> hwnd,& ps);返回0;//标签控件使用它来绘制标签区域的背景案例WM_PRINTCLIENT:如果(cc == NULL)休息;c =(结构容器*)(uiControl(cc)->内部);如果(GetClientRect(c-> hwnd,& r)== 0)logLastError(在containerWndProc()中获取客户端错误时出错");paintContainerBackground(c-> hwnd,(HDC)wParam,& r);返回0;案例WM_ERASEBKGND://避免闪烁//无论如何我们都会绘制整个更新区域返回1;案例WM_WINDOWPOSCHANGED:如果((wp-> flags& SWP_NOSIZE)!= 0)休息;//跌倒情况msgUpdateChild:如果(cc == NULL)休息;c =(结构容器*)(uiControl(cc)->内部);如果(GetClientRect(c-> hwnd,& r)== 0)logLastError(获取客户端rect在containerWndProc()中调整大小时出错");调整大小(cc,& r);返回0;}返回DefWindowProcW(hwnd,uMsg,wParam,lParam);}const char * initContainer(HICON hDefaultIcon,HCURSOR hDefaultCursor){WNDCLASSW wc;ZeroMemory(& wc,sizeof(WNDCLASSW));wc.lpszClassName = containerClass;wc.lpfnWndProc = containerWndProc;wc.hInstance = hInstance;wc.hIcon = hDefaultIcon;wc.hCursor = hDefaultCursor;wc.hbrBackground =(HBRUSH)(COLOR_BTNFACE + 1);如果(RegisterClassW(& wc)== 0)返回注册uiContainer窗口类";initialParent = CreateWindowExW(0,containerClass,L",WS_OVERLAPPEDWINDOW,0、0,100、100,NULL,NULL,hInstance,NULL);如果(initialParent == NULL)返回创建初始父窗口";//为安全起见,请禁用初始父对象​​,以免意外交互//如果这会导致我们的控件出现问题,我们可以将其删除EnableWindow(initialParent,FALSE);返回NULL;}//子类重写此方法,并在销毁所有子级时在此处调用静态void containerDestroy(uiControl * cc){结构容器* c =(结构容器*)(cc->内部);如果(c-> parent!= NULL)抱怨("cc拥有父级时试图销毁uiContainer%p",cc);如果(DestroyWindow(c-> hwnd)== 0)logLastError(销毁containerDestroy()中的uiContainer窗口时出错");uiFree(c);}静态uintptr_t containerHandle(uiControl * cc){结构容器* c =(结构容器*)(cc->内部);返回(uintptr_t)(c-> hwnd);}静态void containerSetParent(uiControl * cc,uiContainer * parent){结构容器* c =(结构容器*)(cc->内部);uiContainer *老父母;HWND newparent;oldparent = c-> parent;c->父母=父母;newparent = initialParent;如果(c-> parent!= NULL)newparent =(HWND)uiControlHandle(uiControl(c-> parent));如果(SetParent(c-> hwnd,newparent)== 0)logLastError(更改containerSetParent()中的uiContainer父级时发生错误");如果(oldparent!= NULL)uiContainerUpdate(oldparent);如果(c-> parent!= NULL)uiContainerUpdate(c-> parent);}静态void containerResize(uiControl * cc,intmax_t x,intmax_t y,intmax_t宽度,intmax_t高度,uiSizing * d){结构容器* c =(结构容器*)(cc->内部);如果(MoveWindow(c-> hwnd,x,y,width,height,TRUE)== 0)logLastError(在containerResize()中调整uiContainer的大小时发生错误");}静态int containerVisible(uiControl * cc){结构容器* c =(结构容器*)(cc->内部);返回!c->隐藏;}静态void containerShow(uiControl * cc){结构容器* c =(结构容器*)(cc->内部);ShowWindow(c-> hwnd,SW_SHOW);//隐藏的控件不计入框和网格c->隐藏= 0;如果(c-> parent!= NULL)uiContainerUpdate(c-> parent);}静态void containerHide(uiControl * cc){结构容器* c =(结构容器*)(cc->内部);ShowWindow(c-> hwnd,SW_HIDE);c->隐藏= 1;如果(c-> parent!= NULL)uiContainerUpdate(c-> parent);}静态void containerEnable(uiControl * cc){结构容器* c =(结构容器*)(cc->内部);uiControlSysFuncParams p;EnableWindow(c-> hwnd,TRUE);p.Func = uiWindowsSysFuncContainerEnable;uiControlSysFunc(cc,& p);}静态void containerDisable(uiControl * cc){结构容器* c =(结构容器*)(cc->内部);uiControlSysFuncParams p;EnableWindow(c-> hwnd,FALSE);p.Func = uiWindowsSysFuncContainerDisable;uiControlSysFunc(cc,& p);}静态void containerUpdate(uiContainer * cc){结构容器* c =(结构容器*)(uiControl(cc)->内部);SendMessageW(c-> hwnd,msgUpdateChild,0,0);}无效的uiMakeContainer(uiContainer * cc){struct容器* c;c = uiNew(结构容器);c-> hwnd = CreateWindowExW(WS_EX_CONTROLPARENT,containerClass,L",WS_CHILD |WS_VISIBLE,0、0,100、100,initialParent,NULL,hInstance,cc);如果(c-> hwnd == NULL)logLastError(在uiMakeContainer()中创建uiContainer窗口时出错");uiControl(cc)-> Internal = c;uiControl(cc)-> Destroy = containerDestroy;uiControl(cc)-> Handle = containerHandle;uiControl(cc)-> SetParent = containerSetParent;//PreferredSize()由子类提供uiControl(cc)->调整大小= containerResize;uiControl(cc)-> Visible = containerVisible;uiControl(cc)-> Show = containerShow;uiControl(cc)-> Hide = containerHide;uiControl(cc)-> Enable = containerEnable;uiControl(cc)-> Disable = containerDisable;//ResizeChildren()由子类提供uiContainer(cc)-> Update = containerUpdate;} 
解决方案

好吧,我想我主要是知道了.

问题在于这里有两个不同的主题部分,分别是 TABP_PANE TABP_BODY . TABP_PANE 是选项卡控件本身绘制的背景,而 TABP_BODY 是我假设的实际标签背景.

比较:

我正在猜测Microsoft希望您执行的操作以及属性表控件所执行的操作,是使每个选项卡页面成为 WC_DIALOG 对话框,并且您将调用 EnableThemeDialogTexture()函数在顶部绘制 TABP_BODY 纹理.

但是,如果您仔细查看原始问题中Explorer资源表的屏幕截图,我们的 TABP_BODY 仍然不太一样.这样就留下两个问题:

  1. 如何在 TABP_PANE 顶部绘制 TABP_BODY ?它只是画在顶部吗?它以某种方式融合吗?

  2. 如何绘制主题对话框纹理?它是否以最小宽度 TABP_BODY 进行平铺?无论如何,那就是我的样子……

因此,至少这是部分答案.我不确定要何时进行调查.


图中所示程序的来源:

 //2015年5月20日//基于wintabprintclient.c 2015年5月18日//基于wintabparentwinebug.c 2015年5月3日#定义UNICODE#定义_UNICODE#define STRICT#定义STRICT_TYPED_ITEMIDS#定义界面//获得正确的Windows版本;现在Windows XP#定义WINVER 0x0501#define _WIN32_WINNT 0x0501#define _WIN32_WINDOWS 0x0501//*根据微软的winperf.h */#define _WIN32_IE 0x0600/*根据Microsoft的sdkddkver.h */#根据微软的sdkddkver.h定义NTDDI_VERSION 0x05010000/* */#include< windows.h>#include< commctrl.h>#include< stdint.h>#include< uxtheme.h>#include< string.h>#include< wchar.h>#include< windowsx.h>#include< vsstyle.h>#include< vssym32.h>#include< stdarg.h>#include< oleacc.h>#include< stdio.h>无效死亡(char * s){//去做}HWND mainwin;#定义BGCOLOR RGB(0x0A,0x24,0x6A)#定义PCOLOR RGB(0x6A,0x24,0x0A)LRESULT回调wndProc(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam){NMHDR * nm =(NMHDR *)lParam;PAINTSTRUCT ps;HDC直流;要点pt;RECT r;HTHEME主题;开关(uMsg){案例WM_CLOSE:PostQuitMessage(0);返回0;情况WM_PAINT:dc = BeginPaint(hwnd,& ps);#定义X 20#定义Y 20#定义X2 240#定义YT 40#定义宽度200#定义HEIGHT 440主题= OpenThemeData(hwnd,L"tab");r.left = X;r.top = Y;r.right = r.left + WIDTH;r.bottom = YT-5;DrawTextW(dc,L"TABP_PANE",-1,& r,DT_LEFT | DT_TOP);r.left = X;r.top = YT;r.right = r.left + WIDTH;r.bottom = r.top +高度;DrawThemeBackground(theme,dc,TABP_PANE,0,& r,NULL);r.left = X2;r.top = Y;r.right = r.left + WIDTH;r.bottom = YT-5;DrawTextW(dc,L"TABP_BODY",-1,& r,DT_LEFT | DT_TOP);r.left = X2;r.top = YT;r.right = r.left + WIDTH;r.bottom = r.top +高度;DrawThemeBackground(theme,dc,TABP_BODY,0,& r,NULL);CloseThemeData(theme);EndPaint(hwnd,& ps);返回0;}返回DefWindowProcW(hwnd,uMsg,wParam,lParam);}静态void makeWindows(void){mainwin = CreateWindowExW(0,L"mainwin",L全窗",WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,CW_USEDEFAULT,500、500,NULL,NULL,GetModuleHandle(NULL),NULL);}int main(int argc,char * argv []){WNDCLASSW wc;味精味精;HBRUSH b;ZeroMemory(& wc,sizeof(WNDCLASSW));wc.lpszClassName = L"mainwin";wc.lpfnWndProc = wndProc;wc.hInstance = GetModuleHandle(NULL);wc.hIcon = LoadIconW(NULL,IDI_APPLICATION);wc.hCursor = LoadCursorW(NULL,IDC_ARROW);//如果打印客户端不打印标签背景,则该颜色会流血b = CreateSolidBrush(BGCOLOR);wc.hbrBackground = b;RegisterClassW(& wc);makeWindows();ShowWindow(mainwin,SW_SHOWDEFAULT);UpdateWindow(mainwin);而(GetMessageW(& msg,NULL,0,0)> 0){TranslateMessage(& msg);DispatchMessage(& msg);}返回0;} 

tl;dr for those who read the old question: new circumstances have caused me to look a little deeper and I've found that this affects bare Tab controls on their own; I've adjusted the question to compensate. If I should remove the old question text entirely, please let me know.

Here is a screenshot from a program I'm working on to test a wrapper library I'm also working on:

If you look closely, the window on the right appears blocky, while the window on the left (the standard Windows Explorer file properties property sheet) looks smooth. At first I thought this was just my video card, but other people see it too, even going so far as to adjust the image colors to demonstrate the blockiness:

Recent bugs have led me to produce another program to test tab controls on their own. In the following screenshot, the picture on the left is a real tab control, and the picture on the right is the tab control rendered with WM_PRINTCLIENT (the red is from where the tab control called my own WM_PRINTCLIENT):

Both of these render blockily:

Why is the tab control in my own software behaving like this but not Windows's own tabs?

This is Windows XP because it has the gradient to test. I need to target XP and newer, though I might drop XP soon anyway. I don't want to hide a bug in my code or setup behind a minimum system requirement.

This test program is below. Run it passing comctl6 as an argument. Note that in the interest of brevity and being a little program to test things it does no error checking.

Thanks!

// 18 may 2015
// based on wintabparentwinebug.c 3 may 2015
#define UNICODE
#define _UNICODE
#define STRICT
#define STRICT_TYPED_ITEMIDS
#define CINTERFACE
// get Windows version right; right now Windows XP
#define WINVER 0x0501
#define _WIN32_WINNT 0x0501
#define _WIN32_WINDOWS 0x0501       /* according to Microsoft's winperf.h */
#define _WIN32_IE 0x0600            /* according to Microsoft's sdkddkver.h */
#define NTDDI_VERSION 0x05010000    /* according to Microsoft's sdkddkver.h */
#include <windows.h>
#include <commctrl.h>
#include <stdint.h>
#include <uxtheme.h>
#include <string.h>
#include <wchar.h>
#include <windowsx.h>
#include <vsstyle.h>
#include <vssym32.h>
#include <stdarg.h>
#include <oleacc.h>
#include <stdio.h>

void die(char *s)
{
    // TODO
}

void initCommonControls(BOOL);

HWND mainwin;
HWND tab;

#define BGCOLOR RGB(0x0A, 0x24, 0x6A)
#define PCOLOR RGB(0x6A, 0x24, 0x0A)

LRESULT CALLBACK wndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    NMHDR *nm = (NMHDR *) lParam;
    PAINTSTRUCT ps;
    HDC dc;
    POINT prev;
    RECT r;
    HBRUSH b;

    switch (uMsg) {
    case WM_CLOSE:
        PostQuitMessage(0);
        return 0;
    case WM_PAINT:
        dc = BeginPaint(hwnd, &ps);
        SetWindowOrgEx(dc, -240, -20, &prev);
        SendMessage(tab, WM_PRINTCLIENT, (WPARAM) dc, PRF_CLIENT);
        SetWindowOrgEx(dc, prev.x, prev.y, NULL);
        EndPaint(hwnd, &ps);
{COLORREF r;
r=GetSysColor(COLOR_ACTIVECAPTION);
printf("%I32X\n", r);}
        return 0;
    case WM_PRINTCLIENT:
        // the tab control sends this to draw the background of the area where the tab buttons are
        b = CreateSolidBrush(PCOLOR);
        GetClientRect(hwnd, &r);
        FillRect((HDC) wParam, &r, b);
        DeleteObject(b);
        return 0;
    }
    return DefWindowProcW(hwnd, uMsg, wParam, lParam);
}

static void makeWindows(void)
{
    mainwin = CreateWindowExW(0,
        L"mainwin", L"Full Window",
        WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT, CW_USEDEFAULT,
        500, 500,
        NULL, NULL, GetModuleHandle(NULL), NULL);

    // create the tab as a child of the empty window...
    tab = CreateWindowExW(0,
        WC_TABCONTROLW, L"",
        TCS_TOOLTIPS | WS_TABSTOP | WS_CHILD | WS_VISIBLE,
        20, 20, 200, 440,
        mainwin, (HMENU) 100, GetModuleHandle(NULL), NULL);
}

void addTab(WCHAR *name)
{
    TCITEMW item;
    LRESULT n;

    n = SendMessageW(tab, TCM_GETITEMCOUNT, 0, 0);
    ZeroMemory(&item, sizeof (TCITEMW));
    item.mask = TCIF_TEXT;
    item.pszText = name;
    SendMessageW(tab, TCM_INSERTITEM, (WPARAM) n, (LPARAM) (&item));
}

int main(int argc, char *argv[])
{
    WNDCLASSW wc;
    MSG msg;
    HBRUSH b;

    initCommonControls(argc > 1 && strcmp(argv[1], "comctl6") == 0);

    ZeroMemory(&wc, sizeof (WNDCLASSW));
    wc.lpszClassName = L"mainwin";
    wc.lpfnWndProc = wndProc;
    wc.hInstance = GetModuleHandle(NULL);
    wc.hIcon = LoadIconW(NULL, IDI_APPLICATION);
    wc.hCursor = LoadCursorW(NULL, IDC_ARROW);
    // if printing client doesn't print the tab background, this color will bleed through instead
    b = CreateSolidBrush(BGCOLOR);
    wc.hbrBackground = b;
    RegisterClassW(&wc);

    makeWindows();
    addTab(L"Page 1");
    addTab(L"Page 2");

    ShowWindow(mainwin, SW_SHOWDEFAULT);
    UpdateWindow(mainwin);

    while (GetMessageW(&msg, NULL, 0, 0) > 0) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    return 0;
}

static const char manifest[] = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n<assembly xmlns=\"urn:schemas-microsoft-com:asm.v1\" manifestVersion=\"1.0\">\n<assemblyIdentity\n    version=\"1.0.0.0\"\n    processorArchitecture=\"*\"\n    name=\"CompanyName.ProductName.YourApplication\"\n    type=\"win32\"\n/>\n<description>Your application description here.</description>\n<dependency>\n    <dependentAssembly>\n        <assemblyIdentity\n            type=\"win32\"\n            name=\"Microsoft.Windows.Common-Controls\"\n            version=\"6.0.0.0\"\n            processorArchitecture=\"*\"\n            publicKeyToken=\"6595b64144ccf1df\"\n            language=\"*\"\n        />\n    </dependentAssembly>\n</dependency>\n</assembly>\n";

static ULONG_PTR comctlManifestCookie;
static HMODULE comctl32;

void initCommonControls(BOOL comctl6)
{
    WCHAR temppath[MAX_PATH + 1];
    WCHAR filename[MAX_PATH + 1];
    HANDLE file;
    DWORD nExpected, nGot;
    ACTCTX actctx;
    HANDLE ac;
    INITCOMMONCONTROLSEX icc;
    FARPROC f;
    // this is listed as WINAPI in both Microsoft's and MinGW's headers, but not on MSDN for some reason
    BOOL (*WINAPI ficc)(const LPINITCOMMONCONTROLSEX);

    if (comctl6) {
        if (GetTempPathW(MAX_PATH + 1, temppath) == 0)
            die("getting temporary path for writing manifest file");
        if (GetTempFileNameW(temppath, L"manifest", 0, filename) == 0)
            die("getting temporary filename for writing manifest file");
        file = CreateFileW(filename, GENERIC_WRITE,
            0,          // don't share while writing
            NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
        if (file == NULL)
            die("creating manifest file");
        nExpected = (sizeof manifest / sizeof manifest[0]) - 1;     // - 1 to omit the terminating null character)
        if (WriteFile(file, manifest, nExpected, &nGot, NULL) == 0)
            die("writing manifest file");
        if (nGot != nExpected)
            die("short write to manifest file");
        if (CloseHandle(file) == 0)
            die("closing manifest file (this IS an error here because not doing so will prevent Windows from being able to use the manifest file in an activation context)");

        ZeroMemory(&actctx, sizeof (ACTCTX));
        actctx.cbSize = sizeof (ACTCTX);
        actctx.dwFlags = ACTCTX_FLAG_SET_PROCESS_DEFAULT;
        actctx.lpSource = filename;
        ac = CreateActCtx(&actctx);
        if (ac == INVALID_HANDLE_VALUE)
            die("creating activation context for synthesized manifest file");
        if (ActivateActCtx(ac, &comctlManifestCookie) == FALSE)
            die("activating activation context for synthesized manifest file");
    }

    ZeroMemory(&icc, sizeof (INITCOMMONCONTROLSEX));
    icc.dwSize = sizeof (INITCOMMONCONTROLSEX);
    icc.dwICC = ICC_TAB_CLASSES;

    comctl32 = LoadLibraryW(L"comctl32.dll");
    if (comctl32 == NULL)
        die("loading comctl32.dll");
    f = GetProcAddress(comctl32, "InitCommonControlsEx");
    if (f == NULL)
        die("loading InitCommonControlsEx()");
    ficc = (BOOL (*WINAPI)(const LPINITCOMMONCONTROLSEX)) f;
    if ((*ficc)(&icc) == FALSE)
        die("initializing Common Controls (comctl32.dll)");
}


Original question:

Title: Why are CreateCompatibleDC(), CreateCompatibleBitmap(), and WM_PRINTCLIENT rendering the parent tab control background in a blocky way?

My container window clss draws, in its WM_PAINT, whatever WM_PRINTCLIENT draws for its parent (that isn't itself a container). So for instance, if its background is a themed tab control, it will draw the themed tab control background as its painted content. The window on the right in the following image is an example:

If you look closely, the window on the right appears blocky, while the window on the left (the standard Windows Explorer file properties property sheet) looks smooth. At first I thought this was just my video card, but other people see it too, even going so far as to adjust the image colors to demonstrate the blockiness:

The essence of the code that paints is simple:

// not actual code, just algorithm to demonstrate what I'm *thinking* is supposed to happen
dc = BeginPaint(hwnd, &ps);
parent = GetAncestor(hwnd, GA_PARENT);
GetClientRect(parent, &r);
cdc = CreateCompatibleDC(dc);
bitmap = CreateCompatibleBitmap(dc, r.right - r.left, r.bottom - r.top);
SelectObject(cdc, pd->bitmap);
SendMessageW(parent, WM_PRINTCLIENT, (WPARAM) cdc, PRF_CLIENT);
updateRect = ps.rcPaint;
parentRect = updateRect;
MapWindowRect(hwnd, parent, &parentRect);
BitBlt(dc, updateRect.left, updateRect.top, updateRect.right - updateRect.left, updateRect.bottom - updateRect.top,
    cdc, parentRect.left, parentRect.top,
    SRCCOPY);

I don't see anything here that would affect the quality of the painted image, unless there's something about compatible bitmaps that I'm missing? Can anyone else explain? I really don't know what to do about this, other than reimplement the drawing for every possible parent of a container and hope that helps.

Thanks.

Here is the full code for the container:

// 26 april 2015
#include "uipriv_windows.h"

#define containerClass L"libui_uiContainerClass"

HWND initialParent;

struct container {
    HWND hwnd;
    uiContainer *parent;
    int hidden;
    HBRUSH brush;
};

static HWND realParent(HWND hwnd)
{
    HWND parent;
    int class;

    parent = hwnd;
    for (;;) {
        parent = GetAncestor(parent, GA_PARENT);
        // skip groupboxes; they're (supposed to be) transparent
        // skip uiContainers; they don't draw anything
        class = windowClassOf(parent, L"button", containerClass, NULL);
        if (class != 0 && class != 1)
            break;
    }
    return parent;
}

struct parentDraw {
    HDC cdc;
    HBITMAP bitmap;
    HBITMAP prevbitmap;
};

static void parentDraw(HDC dc, HWND parent, struct parentDraw *pd)
{
    RECT r;

    if (GetClientRect(parent, &r) == 0)
        logLastError("error getting parent's client rect in parentDraw()");
    pd->cdc = CreateCompatibleDC(dc);
    if (pd->cdc == NULL)
        logLastError("error creating compatible DC in parentDraw()");
    pd->bitmap = CreateCompatibleBitmap(dc, r.right - r.left, r.bottom - r.top);
    if (pd->bitmap == NULL)
        logLastError("error creating compatible bitmap in parentDraw()");
    pd->prevbitmap = SelectObject(pd->cdc, pd->bitmap);
    if (pd->prevbitmap == NULL)
        logLastError("error selecting bitmap into compatible DC in parentDraw()");
    SendMessageW(parent, WM_PRINTCLIENT, (WPARAM) (pd->cdc), PRF_CLIENT);
}

static void endParentDraw(struct parentDraw *pd)
{
    if (SelectObject(pd->cdc, pd->prevbitmap) != pd->bitmap)
        logLastError("error selecting previous bitmap back into compatible DC in endParentDraw()");
    if (DeleteObject(pd->bitmap) == 0)
        logLastError("error deleting compatible bitmap in endParentDraw()");
    if (DeleteDC(pd->cdc) == 0)
        logLastError("error deleting compatible DC in endParentDraw()");
}

// see http://www.codeproject.com/Articles/5978/Correctly-drawn-themed-dialogs-in-WinXP
static HBRUSH getControlBackgroundBrush(HWND hwnd, HDC dc)
{
    HWND parent;
    RECT hwndScreenRect;
    struct parentDraw pd;
    HBRUSH brush;

    parent = realParent(hwnd);

    parentDraw(dc, parent, &pd);
    brush = CreatePatternBrush(pd.bitmap);
    if (brush == NULL)
        logLastError("error creating pattern brush in getControlBackgroundBrush()");
    endParentDraw(&pd);

    // now figure out where the control is relative to the parent so we can align the brush properly
    if (GetWindowRect(hwnd, &hwndScreenRect) == 0)
        logLastError("error getting control window rect in getControlBackgroundBrush()");
    // this will be in screen coordinates; convert to parent coordinates
    mapWindowRect(NULL, parent, &hwndScreenRect);
    if (SetBrushOrgEx(dc, -hwndScreenRect.left, -hwndScreenRect.top, NULL) == 0)
        logLastError("error setting brush origin in getControlBackgroundBrush()");

    return brush;
}

static void paintContainerBackground(HWND hwnd, HDC dc, RECT *paintRect)
{
    HWND parent;
    RECT paintRectParent;
    struct parentDraw pd;

    parent = realParent(hwnd);
    parentDraw(dc, parent, &pd);

    paintRectParent = *paintRect;
    mapWindowRect(hwnd, parent, &paintRectParent);
    if (BitBlt(dc, paintRect->left, paintRect->top, paintRect->right - paintRect->left, paintRect->bottom - paintRect->top,
        pd.cdc, paintRectParent.left, paintRectParent.top,
        SRCCOPY) == 0)
        logLastError("error drawing parent background over uiContainer in paintContainerBackground()");

    endParentDraw(&pd);
}

// from https://msdn.microsoft.com/en-us/library/windows/desktop/dn742486.aspx#sizingandspacing and https://msdn.microsoft.com/en-us/library/windows/desktop/bb226818%28v=vs.85%29.aspx
// this X value is really only for buttons but I don't see a better one :/
#define winXPadding 4
#define winYPadding 4

// abort the resize if something fails and we don't have what we need to do a resize
static HRESULT resize(uiContainer *cc, RECT *r)
{
    struct container *c = (struct container *) (uiControl(cc)->Internal);
    uiSizing d;
    uiSizingSys sys;
    HDC dc;
    HFONT prevfont;
    TEXTMETRICW tm;
    SIZE size;

    dc = GetDC(c->hwnd);
    if (dc == NULL)
        return logLastError("error getting DC in resize()");
    prevfont = (HFONT) SelectObject(dc, hMessageFont);
    if (prevfont == NULL)
        return logLastError("error loading control font into device context in resize()");

    ZeroMemory(&tm, sizeof (TEXTMETRICW));
    if (GetTextMetricsW(dc, &tm) == 0)
        return logLastError("error getting text metrics in resize()");
    if (GetTextExtentPoint32W(dc, L"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", 52, &size) == 0)
        return logLastError("error getting text extent point in resize()");

    sys.baseX = (int) ((size.cx / 26 + 1) / 2);
    sys.baseY = (int) tm.tmHeight;
    sys.internalLeading = tm.tmInternalLeading;

    if (SelectObject(dc, prevfont) != hMessageFont)
        return logLastError("error restoring previous font into device context in resize()");
    if (ReleaseDC(c->hwnd, dc) == 0)
        return logLastError("error releasing DC in resize()");

    d.xPadding = uiDlgUnitsToX(winXPadding, sys.baseX);
    d.yPadding = uiDlgUnitsToY(winYPadding, sys.baseY);
    d.sys = &sys;
    uiContainerResizeChildren(cc, r->left, r->top, r->right - r->left, r->bottom - r->top, &d);
    return S_OK;
}

static LRESULT CALLBACK containerWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    uiContainer *cc;
    struct container *c;
    CREATESTRUCTW *cs = (CREATESTRUCTW *) lParam;
    HWND control;
    NMHDR *nm = (NMHDR *) lParam;
    WINDOWPOS *wp = (WINDOWPOS *) lParam;
    RECT r;
    HDC dc;
    PAINTSTRUCT ps;

    cc = uiContainer(GetWindowLongPtrW(hwnd, GWLP_USERDATA));
    if (cc == NULL)
        if (uMsg == WM_NCCREATE)
            SetWindowLongPtrW(hwnd, GWLP_USERDATA, (LONG_PTR) (cs->lpCreateParams));
        // DO NOT RETURN DEFWINDOWPROC() HERE
        // see the next block of comments as to why
        // instead, we simply check if c == NULL again later

    switch (uMsg) {
    // these must always be run, even on the initial parent
    // why? http://blogs.msdn.com/b/oldnewthing/archive/2010/03/16/9979112.aspx
    case WM_COMMAND:
        // bounce back to the control in question
        // except if to the initial parent, in which case act as if the message was ignored
        control = (HWND) lParam;
        if (control != NULL && IsChild(initialParent, control) == 0)
            return SendMessageW(control, msgCOMMAND, wParam, lParam);
        break;          // fall through to DefWindowProcW()
    case WM_NOTIFY:
        // same as WM_COMMAND
        control = nm->hwndFrom;
        if (control != NULL && IsChild(initialParent, control) == 0)
            return SendMessageW(control, msgNOTIFY, wParam, lParam);
        break;

    // these are only run if c is not NULL
    case WM_CTLCOLORSTATIC:
    case WM_CTLCOLORBTN:
        if (cc == NULL)
            break;
        c = (struct container *) (uiControl(cc)->Internal);
        if (c->brush != NULL)
            if (DeleteObject(c->brush) == 0)
                logLastError("error deleting old background brush in containerWndProc()");
/*TODO      // read-only TextFields and Textboxes are exempt
        // this is because read-only edit controls count under WM_CTLCOLORSTATIC
        if (windowClassOf((HWND) lParam, L"edit", NULL) == 0)
            if (textfieldReadOnly((HWND) lParam))
                return DefWindowProcW(hwnd, uMsg, wParam, lParam);
*/      if (SetBkMode((HDC) wParam, TRANSPARENT) == 0)
            logLastError("error setting transparent background mode to controls in containerWndProc()");
        c->brush = getControlBackgroundBrush((HWND) lParam, (HDC) wParam);
        return (LRESULT) (c->brush);
    case WM_PAINT:
        if (cc == NULL)
            break;
        c = (struct container *) (uiControl(cc)->Internal);
        dc = BeginPaint(c->hwnd, &ps);
        if (dc == NULL)
            logLastError("error beginning container paint in containerWndProc()");
        r = ps.rcPaint;
        paintContainerBackground(c->hwnd, dc, &r);
        EndPaint(c->hwnd, &ps);
        return 0;
    // tab controls use this to draw the background of the tab area
    case WM_PRINTCLIENT:
        if (cc == NULL)
            break;
        c = (struct container *) (uiControl(cc)->Internal);
        if (GetClientRect(c->hwnd, &r) == 0)
            logLastError("error getting client rect in containerWndProc()");
        paintContainerBackground(c->hwnd, (HDC) wParam, &r);
        return 0;
    case WM_ERASEBKGND:
        // avoid some flicker
        // we draw the whole update area anyway
        return 1;
    case WM_WINDOWPOSCHANGED:
        if ((wp->flags & SWP_NOSIZE) != 0)
            break;
        // fall through
    case msgUpdateChild:
        if (cc == NULL)
            break;
        c = (struct container *) (uiControl(cc)->Internal);
        if (GetClientRect(c->hwnd, &r) == 0)
            logLastError("error getting client rect for resize in containerWndProc()");
        resize(cc, &r);
        return 0;
    }

    return DefWindowProcW(hwnd, uMsg, wParam, lParam);
}

const char *initContainer(HICON hDefaultIcon, HCURSOR hDefaultCursor)
{
    WNDCLASSW wc;

    ZeroMemory(&wc, sizeof (WNDCLASSW));
    wc.lpszClassName = containerClass;
    wc.lpfnWndProc = containerWndProc;
    wc.hInstance = hInstance;
    wc.hIcon = hDefaultIcon;
    wc.hCursor = hDefaultCursor;
    wc.hbrBackground = (HBRUSH) (COLOR_BTNFACE + 1);
    if (RegisterClassW(&wc) == 0)
        return "registering uiContainer window class";

    initialParent = CreateWindowExW(0,
        containerClass, L"",
        WS_OVERLAPPEDWINDOW,
        0, 0,
        100, 100,
        NULL, NULL, hInstance, NULL);
    if (initialParent == NULL)
        return "creating initial parent window";

    // just to be safe, disable the initial parent so it can't be interacted with accidentally
    // if this causes issues for our controls, we can remove it
    EnableWindow(initialParent, FALSE);
    return NULL;
}

// subclasses override this and call back here when all children are destroyed
static void containerDestroy(uiControl *cc)
{
    struct container *c = (struct container *) (cc->Internal);

    if (c->parent != NULL)
        complain("attempt to destroy uiContainer %p while it has a parent", cc);
    if (DestroyWindow(c->hwnd) == 0)
        logLastError("error destroying uiContainer window in containerDestroy()");
    uiFree(c);
}

static uintptr_t containerHandle(uiControl *cc)
{
    struct container *c = (struct container *) (cc->Internal);

    return (uintptr_t) (c->hwnd);
}

static void containerSetParent(uiControl *cc, uiContainer *parent)
{
    struct container *c = (struct container *) (cc->Internal);
    uiContainer *oldparent;
    HWND newparent;

    oldparent = c->parent;
    c->parent = parent;
    newparent = initialParent;
    if (c->parent != NULL)
        newparent = (HWND) uiControlHandle(uiControl(c->parent));
    if (SetParent(c->hwnd, newparent) == 0)
        logLastError("error changing uiContainer parent in containerSetParent()");
    if (oldparent != NULL)
        uiContainerUpdate(oldparent);
    if (c->parent != NULL)
        uiContainerUpdate(c->parent);
}

static void containerResize(uiControl *cc, intmax_t x, intmax_t y, intmax_t width, intmax_t height, uiSizing *d)
{
    struct container *c = (struct container *) (cc->Internal);

    if (MoveWindow(c->hwnd, x, y, width, height, TRUE) == 0)
        logLastError("error resizing uiContainer in containerResize()");
}

static int containerVisible(uiControl *cc)
{
    struct container *c = (struct container *) (cc->Internal);

    return !c->hidden;
}

static void containerShow(uiControl *cc)
{
    struct container *c = (struct container *) (cc->Internal);

    ShowWindow(c->hwnd, SW_SHOW);
    // hidden controls don't count in boxes and grids
    c->hidden = 0;
    if (c->parent != NULL)
        uiContainerUpdate(c->parent);
}

static void containerHide(uiControl *cc)
{
    struct container *c = (struct container *) (cc->Internal);

    ShowWindow(c->hwnd, SW_HIDE);
    c->hidden = 1;
    if (c->parent != NULL)
        uiContainerUpdate(c->parent);
}

static void containerEnable(uiControl *cc)
{
    struct container *c = (struct container *) (cc->Internal);
    uiControlSysFuncParams p;

    EnableWindow(c->hwnd, TRUE);
    p.Func = uiWindowsSysFuncContainerEnable;
    uiControlSysFunc(cc, &p);
}

static void containerDisable(uiControl *cc)
{
    struct container *c = (struct container *) (cc->Internal);
    uiControlSysFuncParams p;

    EnableWindow(c->hwnd, FALSE);
    p.Func = uiWindowsSysFuncContainerDisable;
    uiControlSysFunc(cc, &p);
}

static void containerUpdate(uiContainer *cc)
{
    struct container *c = (struct container *) (uiControl(cc)->Internal);

    SendMessageW(c->hwnd, msgUpdateChild, 0, 0);
}

void uiMakeContainer(uiContainer *cc)
{
    struct container *c;

    c = uiNew(struct container);

    c->hwnd = CreateWindowExW(WS_EX_CONTROLPARENT,
        containerClass, L"",
        WS_CHILD | WS_VISIBLE,
        0, 0,
        100, 100,
        initialParent, NULL, hInstance, cc);
    if (c->hwnd == NULL)
        logLastError("error creating uiContainer window in uiMakeContainer()");

    uiControl(cc)->Internal = c;
    uiControl(cc)->Destroy = containerDestroy;
    uiControl(cc)->Handle = containerHandle;
    uiControl(cc)->SetParent = containerSetParent;
    // PreferredSize() is provided by subclasses
    uiControl(cc)->Resize = containerResize;
    uiControl(cc)->Visible = containerVisible;
    uiControl(cc)->Show = containerShow;
    uiControl(cc)->Hide = containerHide;
    uiControl(cc)->Enable = containerEnable;
    uiControl(cc)->Disable = containerDisable;

    // ResizeChildren() is provided by subclasses
    uiContainer(cc)->Update = containerUpdate;
}
解决方案

Okay I think I've mostly figured it out.

The problem is there are two different theme parts at play here, TABP_PANE and TABP_BODY. TABP_PANE is the background the tab control itself draws, and TABP_BODY is what I assume is the actual tab background.

Compare:

I'm guessing what Microsoft expected you to do, and what I guess the property sheet control does, is have each tab page be a WC_DIALOG dialog box and that you call the EnableThemeDialogTexture() function to have the TABP_BODY texture drawn on top.

But if you look closely at the screenshot of the Explorer property sheet in the original question, our TABP_BODY is still not quite the same. So that leaves two questions:

  1. How is TABP_BODY drawn on top of TABP_PANE? Is it merely drawn on top? Is it blended somehow?

  2. How is the theme dialog texture drawn? Is it tiled with a minimum width TABP_BODY? That's what it looks like to me, anyway...

So this is a partial answer, at least. I'm willing to investigate further, though I'm not sure when.


Source of pictured program:

// 20 may 2015
// based on wintabprintclient.c 18 may 2015
// based on wintabparentwinebug.c 3 may 2015
#define UNICODE
#define _UNICODE
#define STRICT
#define STRICT_TYPED_ITEMIDS
#define CINTERFACE
// get Windows version right; right now Windows XP
#define WINVER 0x0501
#define _WIN32_WINNT 0x0501
#define _WIN32_WINDOWS 0x0501       /* according to Microsoft's winperf.h */
#define _WIN32_IE 0x0600            /* according to Microsoft's sdkddkver.h */
#define NTDDI_VERSION 0x05010000    /* according to Microsoft's sdkddkver.h */
#include <windows.h>
#include <commctrl.h>
#include <stdint.h>
#include <uxtheme.h>
#include <string.h>
#include <wchar.h>
#include <windowsx.h>
#include <vsstyle.h>
#include <vssym32.h>
#include <stdarg.h>
#include <oleacc.h>
#include <stdio.h>

void die(char *s)
{
    // TODO
}

HWND mainwin;

#define BGCOLOR RGB(0x0A, 0x24, 0x6A)
#define PCOLOR RGB(0x6A, 0x24, 0x0A)

LRESULT CALLBACK wndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    NMHDR *nm = (NMHDR *) lParam;
    PAINTSTRUCT ps;
    HDC dc;
    POINT pt;
    RECT r;
    HTHEME theme;

    switch (uMsg) {
    case WM_CLOSE:
        PostQuitMessage(0);
        return 0;
    case WM_PAINT:
        dc = BeginPaint(hwnd, &ps);
#define X 20
#define Y 20
#define X2 240
#define YT 40
#define WIDTH 200
#define HEIGHT 440
        theme = OpenThemeData(hwnd, L"tab");
        r.left = X;
        r.top = Y;
        r.right = r.left + WIDTH;
        r.bottom = YT - 5;
        DrawTextW(dc, L"TABP_PANE", -1, &r, DT_LEFT | DT_TOP);
        r.left = X;
        r.top = YT;
        r.right = r.left + WIDTH;
        r.bottom = r.top + HEIGHT;
        DrawThemeBackground(theme, dc,
            TABP_PANE, 0,
            &r, NULL);
        r.left = X2;
        r.top = Y;
        r.right = r.left + WIDTH;
        r.bottom = YT - 5;
        DrawTextW(dc, L"TABP_BODY", -1, &r, DT_LEFT | DT_TOP);
        r.left = X2;
        r.top = YT;
        r.right = r.left + WIDTH;
        r.bottom = r.top + HEIGHT;
        DrawThemeBackground(theme, dc,
            TABP_BODY, 0,
            &r, NULL);
        CloseThemeData(theme);
        EndPaint(hwnd, &ps);
        return 0;
    }
    return DefWindowProcW(hwnd, uMsg, wParam, lParam);
}

static void makeWindows(void)
{
    mainwin = CreateWindowExW(0,
        L"mainwin", L"Full Window",
        WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT, CW_USEDEFAULT,
        500, 500,
        NULL, NULL, GetModuleHandle(NULL), NULL);
}

int main(int argc, char *argv[])
{
    WNDCLASSW wc;
    MSG msg;
    HBRUSH b;

    ZeroMemory(&wc, sizeof (WNDCLASSW));
    wc.lpszClassName = L"mainwin";
    wc.lpfnWndProc = wndProc;
    wc.hInstance = GetModuleHandle(NULL);
    wc.hIcon = LoadIconW(NULL, IDI_APPLICATION);
    wc.hCursor = LoadCursorW(NULL, IDC_ARROW);
    // if printing client doesn't print the tab background, this color will bleed through instead
    b = CreateSolidBrush(BGCOLOR);
    wc.hbrBackground = b;
    RegisterClassW(&wc);

    makeWindows();

    ShowWindow(mainwin, SW_SHOWDEFAULT);
    UpdateWindow(mainwin);

    while (GetMessageW(&msg, NULL, 0, 0) > 0) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    return 0;
}

这篇关于为什么我的程序的选项卡控件以块状方式呈现其背景,而标准的“窗口"对话框却没有?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

09-05 14:30
查看更多