窗体皮肤实现 - 在VC中简单实现绘制(五)

第四部分Delphi XE3的代码能基本完成窗体界面的绘制。窗口中的其他控件的处理方法也是相同的,截获消息处理消息。

问题这个编译出来的个头可不小。Release版本竟然2.43M,完全是个胖子。系统中应该加入了大量基础代码(如泛型之类),用Delphi7编译出来应该能小一截。

delphi版本

使用默认Release的配置方案,没有第三方控件。

翻译到C++进行测试,发现明显这个个头没法比。Debug版本88.5K...~~~ 这货确实小。

vc版本

C版本只实现了部分代码(第一篇内容),并没有全部实现。思路一样,只是换个表示方法而已。

最终结果演示

代码中处理了四个消息,还是比较简单。只有 WM_WINDOWPOSCHANGING 消息的处理稍微长些。

  • WM_NCPAINT --- 绘制非客户区
  • WM_NCCALCSIZE --- 重新设置边缘宽度
  • WM_NCACTIVATE --- 程序切换时重绘非客户去
  • WM_WINDOWPOSCHANGING --- 重设界面样式

连注释和空行也就152行代码。

重新绘制非客户区 WM_NCPAINT

// 非客户去绘制
    case WM_NCPAINT:
        GetWindowRect(hwnd, &rw);
        GetClientRect(hwnd, &rc);
        pt.x = rc.left;
        pt.y = rc.top;
        ClientToScreen(hwnd, &pt);
        OffsetRect(&rc, pt.x - rw.left, pt.y - rw.top);

        hdc = GetWindowDC(hwnd);
        ExcludeClipRect(hdc, rc.left, rc.top, rc.right, rc.bottom);

        OffsetRect(&rw, -rw.left, -rw.top);
        // 使用这个方式比使用fillrect函数填充效果好,不闪烁
        SetBkColor(hdc, 0xBF7B18);
        ExtTextOut(hdc, 0, 0, ETO_OPAQUE, &rw, 0, 0, 0);

        ReleaseDC(hwnd, hdc);
        return 0;

重新设置窗体边缘宽度 WM_NCCALCSIZE

//  设置窗体边框宽度
    case WM_NCCALCSIZE:
        ((LPNCCALCSIZE_PARAMS)lParam)->rgrc[0].left += 3;
        ((LPNCCALCSIZE_PARAMS)lParam)->rgrc[0].top += 55;
        ((LPNCCALCSIZE_PARAMS)lParam)->rgrc[0].right -= 4;
        ((LPNCCALCSIZE_PARAMS)lParam)->rgrc[0].bottom -= 4;
        break;

切换程序时重绘非客户去WM_NCACTIVATE

case WM_NCACTIVATE:
    PostMessage(hWnd, WM_NCPAINT, 1, 0);  // 通知刷新非客户区
    break;

设置窗体样式 WM_WINDOWPOSCHANGING

// 有修改窗体尺寸时重设样式
//
    case WM_WINDOWPOSCHANGING:
        bChanged = FALSE;

        // 窗体位置发生改变,重新计算绘制样式
        if (!bChangeSizeCalled) {
            bChanged = (((LPWINDOWPOS)lParam)->flags & SWP_FRAMECHANGED);

            if ((((LPWINDOWPOS)lParam)->flags & SWP_NOMOVE) == 0) {
                rWindowSize.left = ((LPWINDOWPOS)lParam)->x;
                rWindowSize.top = ((LPWINDOWPOS)lParam)->y;
            }
            if ((((LPWINDOWPOS)lParam)->flags & SWP_NOSIZE) == 0) {
                bChanged = bChanged || (((LPWINDOWPOS)lParam)->cx != rWindowSize.right) || (((LPWINDOWPOS)lParam)->cy != rWindowSize.bottom);
                rWindowSize.right = ((LPWINDOWPOS)lParam)->cx;
                rWindowSize.bottom = ((LPWINDOWPOS)lParam)->cy;
            }

            bChanged = bChanged && ((rWindowSize.right * rWindowSize.bottom) != 0);

            if (bChanged) {
                bChangeSizeCalled = TRUE;
                __try {
                    hTmp = hRegion;
                    hRegion = CreateRoundRectRgn(0, 0, rWindowSize.right, rWindowSize.bottom, 3, 3);
                    SetWindowRgn(hwnd, hRegion, TRUE);
                    if (hTmp)
                        DeleteObject(hTmp);
                } __finally {
                    bChangeSizeCalled = FALSE;
                }
            }
        }

        if (!bChanged)
            return DefWindowProc(hwnd, message, wParam, lParam);

        return 0;

开发环境:

  • VS2015 社区版
  • Win7

完整源代码: