Change the color of the title bar (caption) of a win32 application
Asked Answered
G

3

35

I want to change the color of the title bar in my application as I have seen done in programs such as Skype Preview. I have found only one solution offered on the internet for this (WM_NCPAINT) which seems to require me to draw a completely custom title bar which is of course not ideal when all I want to do is change the background color. Is anyone aware of a better solution? Someone suggested hooking GetSysColor, but it is never called with an index of 2 (COLOR_ACTIVECAPTION) so the color is being retrieved from elsewhere.

Current title bar:


(source: pbrd.co)

End goal:

Gales answered 1/9, 2016 at 2:31 Comment(10)
@KenWhite: Make that an answerBlitzkrieg
Look up the Windows Desktop Manager API. The docs have some examples of custom drawn title bars.Epsilon
Chances are, that Skype really does render the entire non-client area. If you look closely, the spacing of the buttons in the top right is slightly different from your standard application (the Minimize button is moved one pixel to the left).Deal
Isn't Skype Preview a UWP app? And doesn't UWP draws its own title bars? If UWP allows you to use a dark theme, then I can assume it also draws a dark titlebar...Pinnatipartite
@Cheersandhth.-Alf have those APIs been updated for Windows 10 titlebars? Windows 7 didn't allow you to have a "dark" titlebar since the concept didn't make sense, and I don't think the situation changed on Windows 8 either... (I don't get why the Windows API designers seem to be trying to discourage custom drawing of titlebars for newer versions of Windows...)Pinnatipartite
It seems the only solutions right now are fairly terrible practiceMoldau
try SetSysColors(2, aElements, aNewColors); with int aElements[2] = {COLOR_WINDOW, COLOR_ACTIVECAPTION}; and DWORD aNewColors[2]; look at this: SetSysColors function exampleKiyokokiyoshi
#32310816Tetherball
I found also this code, I think you should take a look: github.com/Microsoft/Windows-universal-samples/tree/master/…Tetherball
When Windows was still new, Microsoft battled mightily to make sure applications had a consistent look and feel. The way they accomplished it was to make it easy to do things their way, and hard to do it any other way. Welcome to the hard way. That attitude is a quaint relic, but then so is the Windows API.Pincers
K
25

You can change window title bar color / behaviour with DWMAPI's DwmSetWindowAttribute function in Win32.

NOTE: Might require Windows SDK 10.0.22000.0 (aka first Windows 11 SDK) as DWMWA_USE_IMMERSIVE_DARK_MODE|DWMWA_BORDER_COLOR|DWMWA_CAPTION_COLOR were not documented in Windows SDK 10.0.19041.0 (latest Windows 10 SDK). People have got DWMWA_USE_IMMERSIVE_DARK_MODE working before the public documentation of the variable by simply using dwAttribute as 20 (or 19 if Windows was before Windows 10 20H1). Here is an example of Qt using it in their Windows platform integration by allowing application to use it by passing -platform windows:darkmode=1 flag when launching the application.

To enable immersive dark mode (available in Windows 10 / 11, but undocumented in Windows 10 SDK (can be used by setting value as dwAttribute as 20), value was 19 pre Windows 10 20H1 Update)

#include <dwmapi.h>

BOOL USE_DARK_MODE = true;
BOOL SET_IMMERSIVE_DARK_MODE_SUCCESS = SUCCEEDED(DwmSetWindowAttribute(
    WINhWnd, DWMWINDOWATTRIBUTE::DWMWA_USE_IMMERSIVE_DARK_MODE,
    &USE_DARK_MODE, sizeof(USE_DARK_MODE)));

To change border color (only in Windows 11 SDK)

#include <dwmapi.h>

COLORREF DARK_COLOR = 0x00505050;
BOOL SET_CAPTION_COLOR = SUCCEEDED(DwmSetWindowAttribute(
    WINhWnd, DWMWINDOWATTRIBUTE::DWMWA_BORDER_COLOR,
    &DARK_COLOR, sizeof(DARK_COLOR)));

To change caption color (only in Windows 11 SDK)

#include <dwmapi.h>

COLORREF DARK_COLOR = 0x00505050;
BOOL SET_CAPTION_COLOR = SUCCEEDED(DwmSetWindowAttribute(
    WINhWnd, DWMWINDOWATTRIBUTE::DWMWA_CAPTION_COLOR,
    &DARK_COLOR, sizeof(DARK_COLOR)));

Redraw window

Windows 10 22H2 (haven't tested on 11 or other versions of 10) does not update the window titlebar after setting the color flags. A solution is to minimize, then restore the window to force redraw.

ShowWindow(hwnd, SW_MINIMIZE);
ShowWindow(hwnd, SW_RESTORE);
Killebrew answered 13/1, 2022 at 8:4 Comment(4)
There is slight problem with this solution, it does not make the titlebar redraw. If you set it on already open window, the titlebar will stay white until you minimize and maximize it. I haven't found a way how to force the title bar to redraw yet.Labiate
@Labiate you can activate an entire window or simply copy the re-draw code github.com/jredfox/EConHostProc/blob/…Elijaheliminate
it would be nice if you could have provided a dynamic statement to determine whether or not 19 or 20 was going to be usedElijaheliminate
@Labiate SetWindowPos(hWnd, NULL, rcClient.left, rcClient.top, RECTWIDTH(rcClient), RECTHEIGHT(rcClient), SWP_FRAMECHANGED);Spillman
G
15

The first thing I'm going to say is: You have been warned! This is an awfully laborious task. It's a long way from easy and much time was spent reading the Wine sources (linux implementation of native win32 functionality)

Seeing this question brought back 'fond' (read: exasperating!) memories of my efforts to achieve the same result. The process is a somewhat convoluted one and foisters upon you a great many more responsibilities than simply painting the title-bar. (I've included about 500 lines of code)

Amongst other things, you need to handle window activation/inactivation, sizing, NC area buttons, application icon and title-text.

Using some (drawing) utility functions in other files I've not included, the following was achieved: enter image description hereenter image description here

Both of which are modifications to this dialog:

enter image description here

With the assistance of these (color-keyed) images: enter image description here enter image description here and some stretching/drawing (the image is divided into 9 pieces)

I note upon re-vising this code, that the borders are over-drawn by the client-area. I imagine, because I've not sized it correctly in response to the WM_NCCALCSIZE message. I also used another image which did indeed have borders of 8pixels wide, rather than the 14 that these two show. (You can see the commented-out code in response to the message I mentioned)

The idea is that first, we sub-class the standard dialog-box's WindowProc. In this subclassed handler, we tell the Desktop Window Manager to disable composition for our window, we setup a layered window (this is how the black one appears semi-transparent) and then finally, do the non-client drawing ourselves in response to a WM_NCPAINT message.

I'll also point out that for reasons that have long since escaped me, I wasn't especially satisfied with the functioning of the code.

With that said, here's some code to sink your teeth into:

#define _WIN32_WINNT 0x0501
#define UNICODE 1
#include <windows.h>
#include <commctrl.h>
#include <stdio.h>
#include "resource.h"
#include "../graphics/graphics.h"
#include <dwmapi.h>
using namespace Gdiplus;

HINSTANCE hInst;
LRESULT CALLBACK DlgSubclassProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData);
LRESULT onNcPaint(HWND hwnd, WPARAM wParam, LPARAM lParam);

HBITMAP frameImg, frameRedImg, frameOrigImg;
int frameIndex = 0;


//HRESULT DwmEnableComposition(UINT uCompositionAction);

typedef HRESULT (WINAPI *pFnDwmEnableComposition)(UINT uCompAction);
typedef HRESULT (WINAPI *pFnDwmSetWindowAttribute)(HWND hwnd, DWORD dwAttribute, LPCVOID pvAttribute, DWORD cbAttribute);

#define WM_DMWNCRENDERINGCHANGED 0x31F
// wParam = 1 (fRenderingEnabled = true)
// wParam = 0 (fRenderingEnabled = false)


HRESULT EnableNcDwm(HWND hwnd, bool enable)
{
    HMODULE dwmMod = LoadLibrary(L"dwmapi.dll");
    if (dwmMod)
    {
        pFnDwmSetWindowAttribute DwmSetWindowAttribute;
        DwmSetWindowAttribute = (pFnDwmSetWindowAttribute)GetProcAddress(dwmMod, "DwmSetWindowAttribute");

       HRESULT hr = S_OK;
       DWMNCRENDERINGPOLICY ncrp;
       if (enable)
            ncrp = DWMNCRP_ENABLED;
       else
            ncrp = DWMNCRP_DISABLED;

       // Disable non-client area rendering on the window.
       hr = DwmSetWindowAttribute(hwnd, DWMWA_NCRENDERING_POLICY, &ncrp, sizeof(ncrp));
       FreeLibrary(dwmMod);
       if (SUCCEEDED(hr))
       {
          return hr;
       }
   }
   return S_FALSE;
}

/*
#define DWM_EC_DISABLECOMPOSITION 0
#define DWM_EC_ENABLECOMPOSITION 1
HRESULT EnableDWM(HWND hwnd, bool enable)
{
    HMODULE dwmMod = LoadLibrary(L"dwmapi.dll");
    pFnDwmEnableComposition DwmEnableComposition;
    DwmEnableComposition = (pFnDwmEnableComposition)GetProcAddress(dwmMod, "DwmEnableComposition");
    HRESULT hr = S_OK;
    // Disable DWM Composition
    if (enable)
        hr = DwmEnableComposition(DWM_EC_ENABLECOMPOSITION);
    else
        hr = DwmEnableComposition(DWM_EC_DISABLECOMPOSITION);
    FreeLibrary(dwmMod);
   return hr;
}
*/

BOOL CALLBACK DlgMain(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    static bool isSubclassed = false;
    switch(uMsg)
    {
        case WM_DMWNCRENDERINGCHANGED:
        {
            long dwEx = GetWindowLong(hwndDlg, GWL_EXSTYLE);
            dwEx &= ~(WS_EX_LAYERED);
            SetWindowLong(hwndDlg, GWL_EXSTYLE, dwEx);
            InvalidateRect(hwndDlg, NULL, true);
            UpdateWindow(hwndDlg);
            MoveAnchorsImmediatelly(hwndDlg);
        }
        return 0;

   //     case WM_ERASEBKGND:
   //         {
   //             RECT rc;
   //             GetClientRect(hwndDlg, &rc);
   //             FillRect((HDC)wParam, &rc, (HBRUSH) COLOR_BACKGROUND);
   //             return 1;
   //         }

        case WM_INITDIALOG:
        {
            mSetAnchorMode(GetDlgItem(hwndDlg, IDC_BUTTON1), ANCHOR_CENTER);
        }
        return TRUE;

        case WM_SIZE:
        {
            //MoveAnchorsImmediatelly(hwndDlg);
            DeferAnchorsMove(hwndDlg);
        }
        return TRUE;

        case WM_CLOSE:
        {
            EndDialog(hwndDlg, 0);
        }
        return TRUE;

        case WM_COMMAND:
        {
            switch(LOWORD(wParam))
            {
            case IDC_BUTTON1:
                if (isSubclassed == false)
                {
                    SetWindowSubclass( hwndDlg, DlgSubclassProc, 1, NULL);
                    EnableNcDwm(hwndDlg, false);
                    frameIndex++;
                    frameIndex &= 1;        // make sure it can only be in range [0..1]
                }
                else
                {
                    RemoveWindowSubclass( hwndDlg, DlgSubclassProc, 1);
                    EnableNcDwm(hwndDlg, true);
                }
                isSubclassed = !isSubclassed;


             //   InvalidateRect(hwndDlg, NULL, true);
             //   UpdateWindow(hwndDlg);
             //   MoveAnchorsImmediatelly(hwndDlg);
                break;
            }
        }
        return TRUE;
    }

    return FALSE;
}

LRESULT CALLBACK DlgSubclassProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
{
    static byte alpha = 255;
    switch (uMsg)
    {
        case WM_ENTERSIZEMOVE:
            printf("WM_ENTERSIZEMOVE\n");
            return 0;
            break;
        case WM_EXITSIZEMOVE:
            printf("WM_EXITSIZEMOVE\n");
            return 0;
            break;

        case WM_MOUSEWHEEL:
            if (((SHORT)(HIWORD(wParam))) > 0)
            {
                if (alpha > 30)
                    alpha -= 5;
            }
            else if (alpha < 255)
                alpha += 5;
            SetLayeredWindowAttributes(hwnd, RGB(255,0,255), alpha, LWA_COLORKEY|LWA_ALPHA);
            UpdateWindow(hwnd);
            return 0;

        case WM_DMWNCRENDERINGCHANGED:
            {
               // printf("WM_DMWNCRENDERINGCHANGED\n");
                long dwEx = GetWindowLong(hwnd, GWL_EXSTYLE);
                dwEx |= WS_EX_LAYERED;
                SetWindowLong(hwnd, GWL_EXSTYLE, dwEx);
                SetLayeredWindowAttributes(hwnd, RGB(255,0,255), alpha, LWA_COLORKEY|LWA_ALPHA);
                //MoveAnchorsImmediatelly(hwnd);
                DeferAnchorsMove(hwnd);
                InvalidateRect(hwnd, NULL, true);
                UpdateWindow(hwnd);
//                showWndRect(hwnd);
                return 0;
            }
            break;

        case WM_NCACTIVATE:
        case WM_NCPAINT:
          //  printf("WM_NCPAINT -");
          //  printf("wParam: 0x%08d lParam 0x%08x\n", wParam, lParam);
            onNcPaint(hwnd, wParam, lParam);
            return 0;

        case WM_NCCALCSIZE:
            {
                RECT *rc = (RECT*)lParam;
                rc->left += 8;             // frame image margin widths
                rc->top += 30;
                rc->right -= 8;
                rc->bottom -= 8;

//                rc->left += 14;             // frame image margin widths
//               rc->top += 39;
//                rc->right -= 14;
//                rc->bottom -= 14;
            }
            return (WVR_HREDRAW | WVR_VREDRAW);

        case WM_NCHITTEST:
            {
                POINT mousePos, rawMousePos;
                RECT clientRect, windowRect;

                mousePos.x = LOWORD(lParam);
                mousePos.y = HIWORD(lParam);
                rawMousePos = mousePos;

                GetClientRect(hwnd, &clientRect);
                GetWindowRect(hwnd, &windowRect);
                ScreenToClient(hwnd, &mousePos);

                if ((mousePos.x < clientRect.left) && (rawMousePos.y < windowRect.top+8))
                    return HTTOPLEFT;

                if ((mousePos.x > clientRect.right) && (rawMousePos.y < windowRect.top+8))
                    return HTTOPRIGHT;

                if ( (mousePos.x < clientRect.left) && (mousePos.y > clientRect.bottom))
                    return HTBOTTOMLEFT;

                if ( (mousePos.x > clientRect.right) && (mousePos.y > clientRect.bottom))
                    return HTBOTTOMRIGHT;

                if (rawMousePos.x < windowRect.left+11)
                    return HTLEFT;

                if (rawMousePos.x > windowRect.right-11)
                    return HTRIGHT;

                if (mousePos.y  > clientRect.bottom)
                    return HTBOTTOM;

                RECT closeRect;
                SetRect(&closeRect, windowRect.left + 15, windowRect.top+7, windowRect.left+15+16, windowRect.top+25);
                if (PtInRect(&closeRect, rawMousePos))
                {
//                    printf("over sys menu (appIcon) - %d,%d\n", mousePos.x, mousePos.y);
                    return HTSYSMENU;
                }

                if (rawMousePos.y < windowRect.top+8)
                    return HTTOP;

                if (mousePos.y < 0)
                    return HTCAPTION;
                else
                    return HTCLIENT;
            }

    }
    return DefSubclassProc(hwnd, uMsg, wParam, lParam);
//    return DefWindowProc(hwnd, uMsg, wParam, lParam);
}

LRESULT onNcPaint(HWND hwnd, WPARAM wParam, LPARAM lParam)
{
    // draw Frame
//     HBRUSH mBrush = CreateSolidBrush( RGB(0,113,201) );
    HDC hdc = GetWindowDC(hwnd);
//     HDC hdc = GetDCEx(hwnd, (HRGN)wParam, DCX_WINDOW);//|DCX_INTERSECTRGN);

    RECT mRect, wndRect;
    GetWindowRect(hwnd, &mRect);
    wndRect = mRect;
    mRect.right -= mRect.left;
    mRect.bottom -= mRect.top;
    mRect.left = 0;
    mRect.top = 0;

    HDC memDC = CreateCompatibleDC(hdc);
    HBITMAP old, memBmp;
    old = (HBITMAP)GetCurrentObject(memDC, OBJ_BITMAP);

    memBmp = CreateCompatibleBitmap(hdc, mRect.right, mRect.bottom);
    //memBmp = zCreateDibSection(hdc, mRect.right, mRect.bottom, 24);

    SelectObject(memDC, memBmp);


    //StretchNineDraw(HDC destDC, RECT destRect, HBITMAP srcImage, RECT srcRect,
    //    int marginLeft, int marginTop, int marginRight, int marginBottom, int alpha);

    if (frameIndex == 0)
//        StretchNineDraw(memDC, mRect, frameImg, (RECT){0,0,33,58}, 16,41,16,16, 255);
        StretchNineDrawNoAlpha(memDC, mRect, frameImg, (RECT){0,0,33,58}, 16,41,16,16);
    else
        StretchNineDraw(memDC, mRect, frameRedImg, (RECT){0,0,33,58}, 16,41,16,16, 255);
//        StretchNineDrawNoAlpha(memDC, mRect, frameRedImg, (RECT){0,0,33,58}, 16,41,16,16);
    //    StretchNineDrawNoAlpha(memDC, mRect, frameOrigImg, (RECT){0,0,17,39}, 8,30,8,8);
    //1111drawImgNineSquareStretching(memDC, mRect, frameImg, (RECT){0,0,33,58}, 16,41,16,16);

//void StretchNineDrawNoAlpha(HDC destDC, RECT destRect, HBITMAP srcImage, RECT srcRect,
//                   int marginLeft, int marginTop, int marginRight, int marginBottom)



    // draw icon
    HICON smallIcon = LoadIcon (NULL, IDI_APPLICATION);
    DrawIconEx(memDC, 15, 9, smallIcon, 16, 16, 0,0, DI_NORMAL  );

    // draw window text
    wchar_t wndText[100];
    RECT textRect;
    textRect.left = 9 + 16 + 9;
    textRect.top = 0;
    textRect.right = 1000;
    textRect.bottom = 32;
    GetWindowText(hwnd, wndText, 99);
    //int oldMode = SetBkMode(hdc, TRANSPARENT);
    //int oldMode = SetBkMode(memDC, TRANSPARENT);
    SetBkMode(memDC, TRANSPARENT);

    HFONT oldFont, hfont0 = CreateFont(-13, 0, 0, 0, 0, FALSE, FALSE, FALSE, 1, 0, 0, 0, 0, (L"Ms Shell Dlg"));
    oldFont = (HFONT)SelectObject(memDC, hfont0);
    DrawText(memDC, wndText, -1, &textRect, DT_VCENTER|DT_SINGLELINE|DT_LEFT);
    SelectObject(memDC, oldFont);
    DeleteObject(hfont0);


    // top slice
    BitBlt(hdc, 0,0, mRect.right,41, memDC, 0,0, SRCCOPY);

    // left edge
    BitBlt(hdc, 0, mRect.top + 41, 16, mRect.bottom - (41+16),
           memDC, 0, mRect.top + 41, SRCCOPY);

    // right edge
    BitBlt(hdc, mRect.right-16, mRect.top + 41, 16, mRect.bottom - (41+16),
           memDC, mRect.right-16, mRect.top + 41, SRCCOPY);

    // bottom slice
    BitBlt(hdc, 0,mRect.bottom-16, mRect.right,16, memDC, 0,mRect.bottom-16, SRCCOPY);


 //   BitBlt(hdc, 0,0, mRect.right,mRect.bottom, memDC, 0,0, SRCCOPY);

    ReleaseDC(hwnd, hdc);
    SelectObject(memDC, old);
    DeleteDC(memDC);
    DeleteObject(memBmp);
 //   ValidateRgn(hwnd, (HRGN)wParam);
    return 0;
}


int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
{
    GdiplusStartupInput gdiplusStartupInput;
    ULONG_PTR           gdiplusToken;
    GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);

    frameImg = mLoadImageFile(L"frame.png");
    frameRedImg = mLoadImageFile(L"frameRed.png");
    frameOrigImg = mLoadImageFile(L"frameOrig.png");

    hInst=hInstance;
    InitCommonControls();
    int result = DialogBox(hInst, MAKEINTRESOURCE(DLG_MAIN), NULL, (DLGPROC)DlgMain);

    GdiplusShutdown(gdiplusToken);
    return result;
}

Find below the functions that: - Load a png file (uses GDI+) - stretch draw this image onto the borders

/* BMP, GIF, JPEG, PNG, TIFF, Exif, WMF, and EMF */
HBITMAP mLoadImageFile(wchar_t *filename)
{
    HBITMAP result = NULL;
    Bitmap bitmap(filename, false);
    bitmap.GetHBITMAP(0, &result);
    return result;
}


void GetRectSize(LPRECT tgt, int *width, int *height)
{
    *width = (tgt->right - tgt->left) + 1;
    *height = (tgt->bottom - tgt->top) + 1;
}

BOOL SetRectSizePos(LPRECT tgt, int xPos, int yPos, int width, int height)
{
    return SetRect(tgt, xPos, yPos, xPos+(width-1), yPos+(height-1));
}

BOOL MoveRect(LPRECT tgt, int newX, int newY)
{
    int width, height;
    GetRectSize(tgt, &width, &height);
    return SetRectSizePos(tgt, newX,newY, width,height);
}

//  ****** marginLeft = 6
//   ....|....|....  *
//  ..tL.|.tM.|.tR.. *
//  ---------------- * marginTop = 3
//  ..mL.|.mM.|.mR..
//  ---------------- * marginBottom = 3
//  ..bL.|.bM.|.bR.. *
//   ....|....|....  *
//            ****** marginRight = 6
void CalcNineRects(LPRECT srcRect, RECT *dest, int marginLeft, int marginTop, int marginRight, int marginBottom)
{
    int srcWidth, srcHeight;
    int leftWidth, midWidth, rightWidth;
    int topHeight, midHeight, botHeight;
    int xOrig, yOrig;

    GetRectSize(srcRect, &srcWidth, &srcHeight);
    xOrig = srcRect->left;
    yOrig = srcRect->top;

    leftWidth = marginLeft;
    midWidth = srcWidth - (marginLeft + marginRight) - 1;
    rightWidth = marginRight;

    topHeight = marginTop;
    midHeight = srcHeight - (marginTop + marginBottom) - 1;
    botHeight = marginBottom;

    SetRectSizePos(&dest[0], xOrig, yOrig, leftWidth, topHeight);
    SetRectSizePos(&dest[1], xOrig+(leftWidth), yOrig, midWidth, topHeight);
    SetRectSizePos(&dest[2], xOrig+(leftWidth+midWidth), yOrig, rightWidth, topHeight);

    SetRectSizePos(&dest[3], xOrig, yOrig+(topHeight), leftWidth, midHeight);
    SetRectSizePos(&dest[4], xOrig+(leftWidth), yOrig+(topHeight), midWidth, midHeight);
    SetRectSizePos(&dest[5], xOrig+(leftWidth+midWidth), yOrig+(topHeight), rightWidth, midHeight);

    SetRectSizePos(&dest[6], xOrig,yOrig+(topHeight+midHeight), leftWidth, botHeight);
    SetRectSizePos(&dest[7], xOrig+(leftWidth), yOrig+(topHeight+midHeight), midWidth, botHeight);
    SetRectSizePos(&dest[8], xOrig+(leftWidth+midWidth), yOrig+(topHeight+midHeight), rightWidth, botHeight);
}

void StretchNineDraw(HDC destDC, RECT destRect, HBITMAP srcImage, RECT srcRect,
                    int marginLeft, int marginTop, int marginRight, int marginBottom,int alpha)
{
    RECT destRectList[9], srcRectList[9];
    int i;
    int curSrcWidth, curSrcHeight, curDestWidth, curDestHeight;
    HDC srcDC;
    HBITMAP oldSrcBmp;

    srcDC = CreateCompatibleDC(destDC);
//    GetCurrentObject(srcDC, OBJ_BITMAP);
    oldSrcBmp = (HBITMAP) SelectObject(srcDC, srcImage);

    BLENDFUNCTION bf = {AC_SRC_OVER,0,alpha,AC_SRC_ALPHA};

    int destHeight, destWidth;
    GetRectSize(&destRect, &destWidth, &destHeight);

    CalcNineRects(&srcRect, srcRectList, marginLeft, marginTop, marginRight, marginBottom);
    CalcNineRects(&destRect, destRectList, marginLeft, marginTop, marginRight, marginBottom);
    // printf("dst rect: %d,%d - %d,%d -- \n", destRect.left, destRect.top, destRect.right,destRect.bottom);
    for (i=0; i<9; i++)
    {
        GetRectSize(&srcRectList[i], &curSrcWidth, &curSrcHeight);
        GetRectSize(&destRectList[i], &curDestWidth, &curDestHeight);

        AlphaBlend( destDC,
                    destRectList[i].left, destRectList[i].top,
                    curDestWidth, curDestHeight,

                    srcDC,
                    srcRectList[i].left, srcRectList[i].top,
                    curSrcWidth, curSrcHeight,

                    bf
                   );
    }
    SelectObject(srcDC, oldSrcBmp);
    DeleteDC(srcDC);
}

Finally, the code for anchoring controls to the window (automatically recalculate their position when the window is re-sized)

typedef struct ANCHORPROPERTY
{
    long anchorType;
    RECT rc;
} *pANCHORPROPERTY;

#define ANCHOR_NONE                  0
#define ANCHOR_WIDTH                 1
#define ANCHOR_RIGHT                 2
#define ANCHOR_CENTER_HORZ           3
#define ANCHOR_HEIGHT                4
#define ANCHOR_HEIGHT_WIDTH          5
#define ANCHOR_HEIGHT_RIGHT          6
#define ANCHOR_BOTTOM                7
#define ANCHOR_BOTTOM_WIDTH          8
#define ANCHOR_BOTTOM_RIGHT          9
#define ANCHOR_CENTER_HORZ_BOTTOM    10
#define ANCHOR_CENTER_VERT           11
#define ANCHOR_CENTER_VERT_RIGHT     12
#define ANCHOR_CENTER                13

pANCHORPROPERTY getWndAnchor(HWND hwnd);
bool removeAnchor(HWND hwnd);
bool mSetAnchorMode(HWND hwnd, long anchorMode);
BOOL CALLBACK AnchorEnum(HWND hwnd, LPARAM lParam);
void MoveAnchorsImmediatelly(HWND controlParent);
void DeferAnchorsMove(HWND controlParent);
long getChildCount(HWND controlParent);

pANCHORPROPERTY getWndAnchor(HWND hwnd)
{
    return (pANCHORPROPERTY)GetProp(hwnd, L"anchor");
}

bool removeAnchor(HWND hwnd)
{
    pANCHORPROPERTY pAnchor;
    if (GetProp(hwnd, L"anchor") != NULL)
    {
        pAnchor = (pANCHORPROPERTY)RemoveProp(hwnd, L"anchor");
        delete pAnchor;
    }
    return false;
}

bool mSetAnchorMode(HWND hwnd, long anchorMode)
{
    bool result = false;
    RECT rc, pr;
    POINT p;
    if (IsWindow(hwnd))
    {
        pANCHORPROPERTY pAnchor;
        pAnchor = getWndAnchor(hwnd);
        if (pAnchor == NULL)
        {
            pAnchor = new ANCHORPROPERTY;
            SetProp(hwnd, L"anchor", pAnchor);
        }
        GetWindowRect(hwnd, &rc);
        p.x = rc.left;
        p.y = rc.top;
        ScreenToClient( GetParent(hwnd), &p);
        GetClientRect( GetParent(hwnd), &pr);
    //    printf("pos: %d,%d\n", p.x, p.y);
        pAnchor->anchorType = mmin( mmax(anchorMode, ANCHOR_NONE), ANCHOR_CENTER);
        pAnchor->rc.left = p.x;
        pAnchor->rc.top = p.y;
        pAnchor->rc.right = pr.right - (rc.right-rc.left + p.x);
        pAnchor->rc.bottom = pr.bottom - (rc.bottom - rc.top + p.y);
        result = true;
    }
    return result;
}

BOOL CALLBACK AnchorEnum(HWND hwnd, LPARAM lParam)
{
    RECT pr, rc;
    long x,y,xW,yH;
    pANCHORPROPERTY pAnchor;
    pAnchor = (pANCHORPROPERTY)GetProp(hwnd, L"anchor");
    if (pAnchor != NULL)
    {
        if (pAnchor->anchorType != ANCHOR_NONE)
        {
       //     printf("child enumerated - %d\n", pAnchor->anchorType);
            RECT client, wnd;
            GetClientRect(hwnd, &client);
            GetWindowRect(hwnd, &wnd);
       //     printf("WndRect: %d x %d", (wnd.right-wnd.left) + 1, (wnd.bottom-wnd.top)+1);
       //     printf("client: %d x %d", client.right-client.left+1, client.bottom-client.top+1 );

            int wW, wH, cW,cH;
            wW = (wnd.right-wnd.left) + 1;
            wH = (wnd.bottom-wnd.top) + 1;
            cW = (client.right-client.left) + 1;
            cH = (client.bottom-client.top) + 1;

            GetClientRect(hwnd, &rc);
            GetClientRect(GetParent(hwnd), &pr);
            switch (pAnchor->anchorType)
            {
            case ANCHOR_WIDTH:
                 x = pAnchor->rc.left;
                 y = pAnchor->rc.top;
                 xW = mmax(pr.right - pAnchor->rc.left - pAnchor->rc.right, 0);
                 yH = rc.bottom;
                 break;

             case ANCHOR_RIGHT:               // = 2
                  x  = (pr.right - rc.right - pAnchor->rc.right) - (wW-cW);
                  y  = pAnchor->rc.top;
                  xW = rc.right +  (wW-cW);
                  yH = rc.bottom + (wH-cH);
               //   printf("xPos, yPos: %d, %d - Size: %d x %d\n", x,y,xW,yH);
                  break;

             case ANCHOR_CENTER_HORZ:         // = 3
                  x  = (pr.right - rc.right) / 2;
                  y  = pAnchor->rc.top;
                  xW = rc.right;
                  yH = rc.bottom;
                  break;

             case ANCHOR_HEIGHT:              // = 4
                  x  = pAnchor->rc.left;
                  y  = pAnchor->rc.top;
                  xW = rc.right;
                  yH = mmax(pr.bottom - pAnchor->rc.top - pAnchor->rc.bottom, 0);
                  break;

             case ANCHOR_HEIGHT_WIDTH:        // = 5
                  x = pAnchor->rc.left;
                  y = pAnchor->rc.top;
                  xW = mmax(pr.right - pAnchor->rc.left - pAnchor->rc.right, 0);
                  yH = mmax(pr.bottom - pAnchor->rc.top - pAnchor->rc.bottom, 0);
                  break;

             case ANCHOR_HEIGHT_RIGHT:        // = 6
                  x  = pr.right - rc.right - pAnchor->rc.right;
                  y  = pAnchor->rc.top;
                  xW = rc.right;
                  yH = mmax(pr.bottom - pAnchor->rc.top - pAnchor->rc.bottom, 0);
                  break;

             case ANCHOR_BOTTOM:              // = 7
                  x  = pAnchor->rc.left;
                  y  = pr.bottom - pAnchor->rc.bottom - rc.bottom;
                  xW = rc.right;
                  yH = rc.bottom;
                  break;

             case ANCHOR_BOTTOM_WIDTH:        // = 8
                  x  = pAnchor->rc.left;
                  y  = pr.bottom - pAnchor->rc.bottom - rc.bottom;
                  xW = mmax(pr.right - pAnchor->rc.left - pAnchor->rc.right, 0);
                  yH = rc.bottom;
                  break;

             case ANCHOR_BOTTOM_RIGHT:        // = 9
                  x  = pr.right - rc.right - pAnchor->rc.right;
                  y  = pr.bottom - pAnchor->rc.bottom - rc.bottom;
                  xW = rc.right;
                  yH = rc.bottom;
                  break;

             case ANCHOR_CENTER_HORZ_BOTTOM:  // = 10
                  x  = (pr.right - rc.right) / 2;
                  y  = pr.bottom - pAnchor->rc.bottom - rc.bottom;
                  xW = rc.right;
                  yH = rc.bottom;
                  break;

             case ANCHOR_CENTER_VERT:         // = 11
                  x  = pAnchor->rc.left;
                  y  = (pr.bottom - rc.bottom) / 2;
                  xW = rc.right;
                  yH = rc.bottom;
                  break;

             case ANCHOR_CENTER_VERT_RIGHT:   // = 12
                  x  = pr.right - rc.right - pAnchor->rc.right;
                  y  = (pr.bottom - rc.bottom) / 2;
                  xW = rc.right;
                  yH = rc.bottom;
                  break;

             case ANCHOR_CENTER:              // = 13
                  x  = (pr.right - rc.right) / 2;
                  y  = (pr.bottom - rc.bottom) / 2;
                  xW = rc.right;
                  yH = rc.bottom;
                  break;
            }
            if (lParam == 0)
                SetWindowPos(hwnd,0, x,y, xW,yH, SWP_NOZORDER|SWP_NOCOPYBITS);
            else
                DeferWindowPos((HDWP)lParam, hwnd, 0, x,y, xW,yH, SWP_NOZORDER|SWP_NOCOPYBITS);
        }
    }
    return true;
}

void MoveAnchorsImmediatelly(HWND controlParent)
{
    EnumChildWindows(controlParent, AnchorEnum, 0);
}

BOOL CALLBACK CountEnum(HWND hwnd, LPARAM lParam)
{
    long *pCount = (long*)lParam;
    ++(*pCount);
    return true;
}

long getChildCount(HWND controlParent)
{
    long curCount = 0;
    EnumChildWindows(controlParent, CountEnum, (LPARAM)&curCount);
  //  printf("Child count: %d\n", curCount);
    return curCount;
}

void DeferAnchorsMove(HWND controlParent)
{
    HDWP deferMoveHandle;
    int childCount = getChildCount(controlParent);
    deferMoveHandle = BeginDeferWindowPos(childCount);
    EnumChildWindows(controlParent, AnchorEnum, (LPARAM)deferMoveHandle);
    EndDeferWindowPos(deferMoveHandle);
}
Gascon answered 11/10, 2016 at 21:44 Comment(2)
@MoragHughson - Sort-of. I had to trigger the initial re-draw by dragging the window off-screen, couldn't cycle the appearance changed and a few other defects. Unfortunately, I don't have the time to re-visit this at present.Gascon
Maybe you can try calling the SetWindowPos function. SetWindowPos(hwnd, null, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE | SWP_NOREPOSITION | SWP_NOZORDER); reference: learn.microsoft.com/en-us/windows/win32/api/winuser/…Galvin
H
6

You may try to create a window without a frame; i found reference here: opening a window that has no title bar with win32

and then make your own title bar inside your application, in the color you like (located at the top of the window starting from the visible part).

Hellenist answered 11/10, 2016 at 19:12 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.