Bypassing WS_CLIPCHILDREN - c++

I have create a custom scroll bar with transparency option. The way this transparency works is very simple, I invalidate the required region and let the parent to erase and paint itself, after that I repaint the scroll bar itself. Here's some piece of code:
void Refresh(bool bForceOwnerRedraw)
{
if (Gui.OwnerRedraw || bForceOwnerRedraw)
InvalidateBackgroundRegion(State.CurrentState);
RedrawWindow(Gui.OwnerWindow, &Gui.DrawingRectangle, NULL, RDW_INVALIDATE | RDW_NOERASE | RDW_INTERNALPAINT);
}
void InvalidateBackgroundRegion(DWORD State)
{
HWND hWndParent = GetParent(Gui.OwnerWindow);
POINT p = {0};
MapWindowPoints(Gui.OwnerWindow, hWndParent, &p, 1);
HRGN BackGroundRegion = CreateRectRgn(Gui.DrawingRectangle.left + p.x, Gui.DrawingRectangle.top + p.y, Gui.DrawingRectangle.right + p.x, Gui.DrawingRectangle.bottom + p.y);
HRGN MainRegion = NULL;
if (State & SELECTED || State & SELECTED_HOVER)
{
int Width = Gui.DrawingRectangle.left + Position.SelectedState.MainDrawing.right + p.x + 1,
Height = Gui.DrawingRectangle.top + Position.SelectedState.MainDrawing.bottom + p.y + 1;
MainRegion = CreateRoundRectRgn(Position.SelectedState.MainDrawing.left + Gui.DrawingRectangle.left + p.x, Position.SelectedState.MainDrawing.top + Gui.DrawingRectangle.top + p.y, Width, Height, Gui.uiCornerRoundness, Gui.uiCornerRoundness);
}
else if (State & UNSELECTED || State & UNSELECTED_HOVER)
{
int Width = Gui.DrawingRectangle.left + Position.UnselectedState.MainDrawing.right + p.x + 1,
Height = Gui.DrawingRectangle.top + Position.UnselectedState.MainDrawing.bottom + p.y + 1;
MainRegion = CreateRoundRectRgn(Position.UnselectedState.MainDrawing.left+Gui.DrawingRectangle.left+p.x, Position.UnselectedState.MainDrawing.top+Gui.DrawingRectangle.top+p.y, Width, Height, Gui.uiCornerRoundness, Gui.uiCornerRoundness);
}
else if (State & INTERMEDIATE || State & INTERMEDIATE_HOVER)
{
int Width = Gui.DrawingRectangle.left + Position.IntermediateState.MainDrawing.right + p.x + 1,
Height = Gui.DrawingRectangle.top + Position.IntermediateState.MainDrawing.bottom + p.y + 1;
MainRegion = CreateRoundRectRgn(Position.IntermediateState.MainDrawing.left + Gui.DrawingRectangle.left + p.x, Position.IntermediateState.MainDrawing.top + Gui.DrawingRectangle.top + p.y, Width, Height, Gui.uiCornerRoundness, Gui.uiCornerRoundness);
}
CombineRgn(BackGroundRegion, BackGroundRegion, MainRegion, RGN_DIFF);
RedrawWindow(hWndParent, NULL, BackGroundRegion, RDW_INVALIDATE | RDW_ERASE | RDW_INTERNALPAINT);
DeleteObject(MainRegion);
DeleteObject(BackGroundRegion);
}
The problem occurs when I resize the main window, everything flickers. However I can prevent that by using WS_CLIPCHILDREN flag on main window, but then my scroll bar looses it's transparency, because I no longer can't invalidate parts, that are owned by child windows.
What I have tried so far:
Before invalidating parent, I removed WS_CLIPCHILDREN flag, repainted, and then set WS_CLIPCHILDREN flag back. [Result: It worked, however my CPU usage went over 30%, so it's a no no.]
Tried validating child windows (without WS_CLIPCHILDREN), before erasing and repainting.
[Result: didn't work, don't know why... maybe I... nope no idea.]
Completely disabled all repainting in my scroll bar (without WS_CLIPCHILDREN) [Result: didn't work, still flickers.]
What I would like to achieve here is to find a way to to bypass WS_CLIPCHILDREN by invalidating window manually (maybe somehow invalidating using SendMessage() to bypass it). Any other suggestion are also welcome. Thanks
EDIT:
Some more information about how I create my scroll bar. In the code below you can see that my scroll bar is just an empty child window with WS_VISIBLE and WS_CHILD flags, all of the painting is done using good old gdi.
void RegisterScrollbarClass()
{
if (RegisteredClasses.bCustomScrollBar)
return;
WNDCLASSEX wc;
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = BaseWindow::stWinMsgHandler;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = NULL;
wc.hIcon = NULL;
wc.hCursor = NULL;
wc.hbrBackground = NULL;
wc.lpszMenuName = NULL;
wc.lpszClassName = TEXT("CUSTOM_SCROLLBAR");
wc.hIconSm = NULL;
if (!RegisterClassEx(&wc))
{
MessageBox(NULL, TEXT("Button Class Registration Failed!"), TEXT("ERROR!"), MB_ICONERROR);
exit(EXIT_FAILURE);
}
RegisteredClasses.bCustomScrollBar = true;
}
HWND CreateScrollbar(DWORD dwExStyle, LPCTSTR lpClassName, LPCTSTR lpWindowName, DWORD dwStyle, int x, int y, int nWidth, int nHeight, HWND hWndParent, HMENU hMenu, HINSTANCE hInstance, LPVOID lpParam)
{
DWORD ScrollbarStyle = NULL, Child = NULL;
if ((dwStyle & SB_VERT) == SB_VERT)
ScrollbarStyle = SB_VERT;
else if ((dwStyle & SB_HORZ) == SB_HORZ)
ScrollbarStyle = SB_HORZ;
else
return NULL;
if ((dwStyle & WS_CHILD) == WS_CHILD)
Child = WS_CHILD;
if ((dwStyle & WS_VISIBLE) == WS_VISIBLE)
Child |= WS_VISIBLE;
RegisterScrollbarClass();
Scrollbar.push_back(CustomScrollbar(ScrollbarStyle, x, y, nWidth, nHeight, (int)hMenu));
for(unsigned int i = 0; i < Scrollbar.size(); i++)
Scrollbar.at(i).Create();
if (Scrollbar.at(Scrollbar.size()-1).Create(dwExStyle, lpClassName, NULL, Child, x, y, nWidth, nHeight, hWndParent, hMenu, hInstance, lpParam) != NULL)
{
if (Scrollbar.size() == 2)
Scrollbar.at(Scrollbar.size() - 1).EnableTransparent(true);
return Scrollbar.at(Scrollbar.size()-1).WindowHandle();
}
else
{
Scrollbar.pop_back();
return NULL;
}
}
Here you can download sample program: http://www.putlocker.com/file/B8704C613BC7EC96
SOLVED:
Thanks to Raymond Chen, I have removed CS_HREDRAW | CS_VREDRAW from my Scroll bar class and I works like a charm without WS_CLIPCHILDREN.

Related

How to made JAWS readout ListView Item use Server Annotation

I have write a windows client, which UI framework is ATL/WTL. And There is a ListView (CListViewCtrl in ATL) with LVS_OWNERDRAWFIXED style.
For support Accessibility tool JAWS.I Want Jaws read ListView item.
I follow the doc:https://learn.microsoft.com/en-us/windows/win32/winauto/server-annotation-sample
0.Impl of IAccPropServer for support GetPropValue .
class ListViewAccServer : public IAccPropServer
{
ULONG m_Ref;
IAccPropServices * m_pAccPropSvc;
public:
/* skip over ListViewAccServer/ ~ListViewAccServer
*/
static ListViewAccServer * CreateProvider(HWND hControl)
{
ATL::CComPtr<IAccPropServices> pAccPropSvc;
HRESULT hr = pAccPropSvc.CoCreateInstance(CLSID_AccPropServices, NULL, CLSCTX_SERVER);
if (hr == S_OK && pAccPropSvc)
{
ListViewAccServer * pLVServer = new (std::nothrow) ListViewAccServer(pAccPropSvc);
if (pLVServer)
{
MSAAPROPID propid = PROPID_ACC_NAME;
//pAccPropSvc->SetHwndPropServer(hControl, (DWORD)OBJID_CLIENT, CHILDID_SELF, &propid, 1, pLVServer, ANNO_CONTAINER);
pAccPropSvc->SetHwndPropServer(hControl, (DWORD)OBJID_CLIENT, CHILDID_SELF, &propid, 1, pLVServer, ANNO_CONTAINER);
pLVServer->Release();
}
return pLVServer;
}
return NULL;
}
/* skip over: Addref/Release/QI
*/
HRESULT STDMETHODCALLTYPE GetPropValue(const BYTE * pIDString,
DWORD dwIDStringLen, MSAAPROPID idProp, VARIANT * pvarValue,
BOOL * pfGotProp)
{
if (!pfGotProp)
return E_POINTER;
pvarValue->vt = VT_EMPTY;
*pfGotProp = FALSE;
//HWND hwnd;
DWORD idObject;
DWORD idChild;
HWND dwHcontrol;
if (S_OK != m_pAccPropSvc->DecomposeHwndIdentityString(pIDString,
dwIDStringLen, &dwHcontrol, &idObject, &idChild))
{
return S_OK;
}
HWND Hwnd = dwHcontrol;
// Only supply name string for child elements, not the listview itself
if (idChild != CHILDID_SELF)
{
if (idProp == PROPID_ACC_NAME)//Jaws should read acc name?
{
CString str;
str.Format(L"Line index %d", idChild);
OutputDebugPrintfW(_T("GetPropValue str =%s\n"), str);
BSTR bstr = ::SysAllocString((LPCTSTR)str.GetString());
pvarValue->vt = VT_BSTR;
pvarValue->bstrVal = bstr;
*pfGotProp = TRUE;
}
}
return S_OK;
}
};
1.initUI
//if Set 'LVS_OWNERDRAWFIXED',JAWS will read onDraw Item "Dummy List Item"
m_lvMain.Create(m_hWnd, rc, NULL,
WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN |
LVS_REPORT | LVS_AUTOARRANGE | LVS_SHOWSELALWAYS | LVS_SHAREIMAGELISTS | WS_TABSTOP|LVS_OWNERDRAWFIXED,
WS_EX_CLIENTEDGE, LIST_ID);
/*
m_lvMain.Create(m_hWnd, rc, NULL,
WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN |
LVS_REPORT | LVS_AUTOARRANGE | LVS_SHOWSELALWAYS | LVS_SHAREIMAGELISTS | WS_TABSTOP ,
WS_EX_CLIENTEDGE, LIST_ID);
*/
//otherwise JAWS will read Hello,Hello1,Hello2...
m_lvMain.InsertColumn(0, _T("Column"), LVCFMT_LEFT, 200);
m_lvMain.InsertItem(0, _T("Hello"));
m_lvMain.InsertItem(0, _T("Hello1"));
m_lvMain.InsertItem(0, _T("Hello2"));
m_lvMain.InsertItem(0, _T("Hello3"));
2.Bind Listview m_hwnd with ListViewAccServer
m_pAccServer = ListViewAccServer::CreateProvider(m_lvMain.m_hWnd);
3
LRESULT OnDrawItem(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
//LRESULT onDraw(WORD /*wNotifyCode*/, WORD wID, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
{
LPDRAWITEMSTRUCT pDIS = (LPDRAWITEMSTRUCT)(lParam);
//OutputDebugPrintfW(_T("OnDrawItem i = %d, %d, %08x \n"), pDIS->itemID, pDIS->itemData, pDIS->hwndItem);
BOOL bSelected = ((pDIS->itemState & ODS_SELECTED) == ODS_SELECTED);
BOOL bFocus = ((pDIS->itemState & ODS_FOCUS) == ODS_FOCUS);
HDC hDC = pDIS->hDC;
RECT rc = pDIS->rcItem;
HBRUSH bg = (HBRUSH)(::GetStockObject(WHITE_BRUSH));
if (bSelected)
{
bg = (HBRUSH)(::GetStockObject(LTGRAY_BRUSH));
}
if (bFocus)
{
bg = (HBRUSH)(::GetStockObject(LTGRAY_BRUSH));
}
HPEN pn = (HPEN)(::GetStockObject(BLACK_PEN));
::SelectObject(hDC, bg);
::SelectObject(hDC, pn);
::SetTextColor(hDC, RGB(0, 0, 0));
const wchar_t *text = L"Dummy List Item";
::Rectangle(hDC, rc.left, rc.top, rc.right, rc.bottom);
::DrawText(hDC, text, wcslen(text), &rc, DT_SINGLELINE | DT_VCENTER);
return S_OK;
}
When use UP and down array By Keyboard alert in ListView items.I want Jaws read out "Line index i" in GetPropValue.
But
1: If I set ListView with style:LVS_OWNERDRAWFIXED.On some PC, Jaws will read out "Dummy List Item". On Some PC, It read nothing.
2: If remove style:LVS_OWNERDRAWFIXED. Jaws will read out 'Hello','Hello1'...
both 1 or 2 do not read out GetPropValue give string"Line index i".
The log shows GetPropValue is be called:
GetPropValue str =Line index 1
GetPropValue str =Line index 2
My test PC machine OSs are Win10,Win7
So what's the problem?
Thanks a lot.
PS:At last , I found this:
https://bugs.eclipse.org/bugs/show_bug.cgi?id=73496
I can use Insert+6 to bring up the JAWS Configuration Manager.
- click OK to Add a New Configuration (for your application)
- in the Configuration Manager dialog, type "listv" in the filter to see the page containing the "Rely on MSAA for ListViews" checkbox.
- check "Rely on MSAA for ListViews"
- click OK to save and exit the Configuration Manager.
So Jaws default do not use MSAA to get information?

AdjustWindowRectExForDpi doesn't seem to adjust client area size

While trying to create an application that correctly supports per-monitor DPI-awareness version 2, I encountered an issue where my application's window's client area size was incorrect when started on a monitor, where DPI scaling was enabled.
I leave choosing the appropriate location for the window up to Windows, so I can't know on which monitor the window is going to be created, thus I'm also unable to know the DPI I should scale for before the creation of the window.
The solution to this is that I obtain the DPI for the monitor, once the window has been created, using GetDpiForWindow and set the size, so it matches the client area size I desire. In this case I want the client area to be scaled - for example 300x150 client area when on a 125% display should be 375x187.
The DPI is obtained correctly (120 in my case), but using SetWindowPos means that I have to account for the window borders, titlebar, etc. For that purpose I use AdjustWindowRectExForDpi, which accounts for the DPI scaling of the window borders.
To my surprise, the resulting client area size still is 300x150, when the application is started on a DPI-scaled monitor. Starting the application on a non-DPI-scaled monitor and then moving it to one that is, results in a correct client area size.
Minimal example:
#include <Windows.h>
LRESULT CALLBACK startup_window_procedure(HWND window, UINT message, WPARAM w_param, LPARAM l_param)
{
switch (message)
{
case WM_DESTROY:
{
PostQuitMessage(0);
return 0;
}
case WM_DPICHANGED:
{
// Resize the window
RECT* new_rect = reinterpret_cast<RECT*>(l_param);
if (!SetWindowPos(window, nullptr, new_rect->left, new_rect->top, new_rect->right - new_rect->left, new_rect->bottom - new_rect->top, SWP_NOZORDER | SWP_NOACTIVATE))
{
return 1;
}
return 0;
}
}
return DefWindowProcW(window, message, w_param, l_param);
}
int CALLBACK wWinMain(HINSTANCE instance, HINSTANCE prev_instance, PWSTR cmd_line, int cmd_show)
{
constexpr auto window_class_name = L"example_dialog";
constexpr auto window_style = WS_OVERLAPPEDWINDOW;
// Enable per-monitor DPI-awareness version 2
if (!SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2))
{
return 1;
}
// Create the window
WNDCLASSEXW window_class;
window_class.cbSize = sizeof(window_class);
window_class.style = CS_HREDRAW | CS_VREDRAW;
window_class.lpfnWndProc = startup_window_procedure;
window_class.cbClsExtra = 0;
window_class.cbWndExtra = 0;
window_class.hInstance = instance;
window_class.hIcon = nullptr;
window_class.hCursor = nullptr;
window_class.hbrBackground = reinterpret_cast<HBRUSH>(COLOR_WINDOW + 1);
window_class.lpszMenuName = nullptr;
window_class.lpszClassName = window_class_name;
window_class.hIconSm = nullptr;
if (!RegisterClassExW(&window_class))
{
return 1;
}
HWND window = CreateWindowExW(0, window_class_name, L"Example window", window_style, CW_USEDEFAULT, CW_USEDEFAULT, 0, 0, nullptr, nullptr, instance, nullptr);
if (!window)
{
return 1;
}
UINT dpi = GetDpiForWindow(window);
// Actually set the appropriate window size
RECT scaled_size;
scaled_size.left = 0;
scaled_size.top = 0;
scaled_size.right = 300;
scaled_size.bottom = 150;
if (!AdjustWindowRectExForDpi(&scaled_size, window_style, false, 0, dpi))
{
return 1;
}
if (!SetWindowPos(window, nullptr, 0, 0, scaled_size.right - scaled_size.left, scaled_size.bottom - scaled_size.top, SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOMOVE))
{
return 1;
}
ShowWindow(window, SW_SHOWNORMAL);
// Message loop
MSG message;
int result;
while ((result = GetMessageW(&message, nullptr, 0, 0)) != 0)
{
if (result == -1)
{
return 1;
}
else
{
TranslateMessage(&message);
DispatchMessageW(&message);
}
}
return static_cast<int>(message.wParam);
}
The example requires at least Windows 10 1607 to run and Windows SDK 14393 to compile.
How do I properly scale the client area size, when the application is started on a DPI-scaled monitor?
AdjustWindowRectExForDpi doesn't adjust the client area size, though it does account for the increased size of window borders and the title bar, when the non-client area of a window is DPI-scaled. This means that you must scale the client area size yourself.
The example's code can be modified to scale the client area size manually like this:
// Calculate the scaling factor. 96 is the default Windows DPI, unless DPI scaling is enabled.
float scaling_factor = static_cast<float>(dpi) / 96;
RECT scaled_size;
scaled_size.left = 0;
scaled_size.top = 0;
scaled_size.right = static_cast<LONG>(300 * scaling_factor);
scaled_size.bottom = static_cast<LONG>(150 * scaling_factor);
if (!AdjustWindowRectExForDpi(&scaled_size, window_style, false, 0, dpi))
{
return 1;
}
if (!SetWindowPos(window, nullptr, 0, 0, scaled_size.right - scaled_size.left, scaled_size.bottom - scaled_size.top, SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOMOVE))
{
return 1;
}

An Aero caption title bar issue using DWM API on the windows 10

In order to draw the icon on the caption title bar, I have refereed this MSDN article and used DWM API to create my customize client area by calling DwmExtendFrameIntoClientArea.
my code:
CMainFrame::CMainFrame()
{
Gdiplus::GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR gdiplusToken;
GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
BOOL fDwmEnabled = FALSE;
if (SUCCEEDED(DwmIsCompositionEnabled(&fDwmEnabled)))
TRACE0("DWM is enabled\n");
TCHAR szLogoPath[MAX_PATH];
GetModuleFileName ( GetModuleHandle(NULL), szLogoPath, _countof(szLogoPath) );
PathRemoveFileSpec ( szLogoPath );
PathAppend ( szLogoPath, _T("lena.bmp") );
m_pLogoImage = m_pLogoImage->FromFile ( CT2CW(szLogoPath) );
if(NULL == m_pLogoImage)
TRACE0("load image fail\n");
}
void CMainFrame::OnNcCalcSize(BOOL bCalcValidRects, NCCALCSIZE_PARAMS* lpncsp)
{
int xFrame = 2;
int yFrame = 2;
int nTHight = 30;
NCCALCSIZE_PARAMS * p;
RECT * rc;
RECT aRect;
RECT bRect;
RECT acRect;
p = (NCCALCSIZE_PARAMS *)lpncsp;
CopyRect(&bRect,&p->rgrc[1]);
CopyRect(&aRect,&p->rgrc[0]);
acRect.left = aRect.left + xFrame;
acRect.top = aRect.top - nTHight;
acRect.right = aRect.right - xFrame;
acRect.bottom = aRect.bottom - yFrame;
CopyRect(&p->rgrc[0],&acRect);
CopyRect(&p->rgrc[1],&aRect);
CopyRect(&p->rgrc[2],&bRect);
CFrameWnd::OnNcCalcSize(TRUE, lpncsp);
}
LRESULT CMainFrame::OnNcHitTest(CPoint p)
{
BOOL dwm_enabled = FALSE;
if (SUCCEEDED(DwmIsCompositionEnabled(&dwm_enabled)))
{
LRESULT result = 0;
if (!DwmDefWindowProc(m_hWnd, WM_NCHITTEST, 0, MAKELPARAM(p.x, p.y), &result))
result = HitTestNCA(m_hWnd, p);
if (result == HTNOWHERE && GetForegroundWindow() != this)
{
return HTCAPTION;
}
return result;
}
return CWnd::OnNcHitTest(p);
}
BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs)
{
if(cs.hMenu!=NULL)
{
::DestroyMenu(cs.hMenu);
cs.hMenu = NULL ;
}
if( !CFrameWnd::PreCreateWindow(cs) )
return FALSE;
// TODO: Modify the Window class or styles here by modifying
// the CREATESTRUCT cs
cs.style = WS_CAPTION | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_OVERLAPPED| WS_SYSMENU | WS_THICKFRAME;
cs.dwExStyle &= ~WS_EX_CLIENTEDGE;
cs.lpszClass = AfxRegisterWndClass(0);
return TRUE;
}
void CMainFrame::OnActivate(UINT nState,CWnd* pWndOther,BOOL bMinimized )
{
CFrameWnd::OnActivate(nState,pWndOther,bMinimized);
BOOL fDwmEnabled = FALSE;
if (SUCCEEDED(DwmIsCompositionEnabled(&fDwmEnabled)))
{
if(nState == WA_ACTIVE )
{
MARGINS margins = {-1};
/*margins.cyTopHeight = 30;
margins.cxLeftWidth = 0;
margins.cxRightWidth = 0;
margins.cyBottomHeight = 0;*/
HRESULT hr = DwmExtendFrameIntoClientArea(m_hWnd, &margins);
if (!SUCCEEDED(hr))
TRACE0("Failed in DwmExtendFrameIntoClientArea\n");
}
}
}
void CMainFrame::OnNcPaint()
{
CFrameWnd::OnPaint();
CDC* dc = GetWindowDC();
RECT rcClient;
GetWindowRect(&rcClient);
dc->FillSolidRect(0,0,RECTWIDTH(rcClient),RECTHEIGHT(rcClient),RGB(255,0,0));
CPaintDC gdc(this); // device context for painting
Graphics gr(gdc.m_hDC);
gr.DrawImage ( m_pLogoImage, 0, 0 );
ReleaseDC(dc);
}
The result under Windows 7 is fine.
However, my window appears another unknown caption title bar under Windows 10.
I found out the unknown caption is caused by WS_THICKFRAME in the cs.style.
If I remove WS_THICKFRAME, the unknown cation bar will disappear, but I cannot resizing the border of my window. Furthermore, my program cannot capture the minimum, maximum and the close button message on my custom caption bar anymore.
I want to remove the unknown title bar without any side effect.
Does anyone could provide me a good solution or suggestion?
Best Regards,
When using DwmExtendFrameIntoClientArea, it means frame is extended in to client area. It is no longer in non-client area. So there is no need to override OnNcPaint, you can do all of the painting in OnPaint
void CMainFrame::OnPaint()
{
CPaintDC dc(this);
//paint titlebar area (this used to be the non-client area)
CRect rc;
GetClientRect(&rc);
rc.bottom = titlebar_height;
CDC memdc;
memdc.CreateCompatibleDC(&dc);
BITMAPINFOHEADER bmpInfoHeader = {
sizeof(BITMAPINFOHEADER), rc.Width(), -rc.Height(), 1, 32 };
HBITMAP hbitmap = CreateDIBSection(
dc, (BITMAPINFO*)(&bmpInfoHeader), DIB_RGB_COLORS, NULL, NULL, 0);
auto oldbitmap = memdc.SelectObject(hbitmap);
dc.BitBlt(0, 0, rc.Width(), rc.Height(), &memdc, 0, 0, SRCCOPY);
memdc.SelectObject(oldbitmap);
DeleteObject(hbitmap);
//begin normal paint
//The new client area begins below titlebar_height which we define earlier
GetClientRect(&rc);
rc.top = titlebar_height;
dc.FillSolidRect(&rc, RGB(0, 0, 255));
Gdiplus::Image *image = Gdiplus::Image::FromFile(L"file.jpg");
Gdiplus::Graphics gr(dc);
gr.DrawImage(image, 0, 0);
delete image;
}
Use a member variable CRect m_border to keep track of border's thickness. You can use AdjustWindowRectEx to find the thickness of the borders.
void CMainFrame::OnActivate(UINT nState, CWnd* pWndOther, BOOL bMinimized)
{
CFrameWnd::OnActivate(nState, pWndOther, bMinimized);
titlebar_height = 100;
//find border thickness
if (GetWindowLongPtr(m_hWnd, GWL_STYLE) & WS_THICKFRAME)
{
m_border = { 0,0,0,0 };
AdjustWindowRectEx(&m_border, GetWindowLongPtr(m_hWnd,
GWL_STYLE) & ~WS_CAPTION, FALSE, NULL);
m_border.left = abs(m_border.left);
m_border.top = abs(m_border.top);
}
else if (GetWindowLongPtr(m_hWnd, GWL_STYLE) & WS_BORDER)
{
m_border = { 1,1,1,1 };
}
else
{
m_border = { 0,0,0,0 };
}
//Extend frame in to client area
MARGINS margins = { 0 };
margins.cyTopHeight = titlebar_height; //<<=== *** edited
DwmExtendFrameIntoClientArea(m_hWnd, &margins);
SetWindowPos(NULL, 0, 0, 0, 0,
SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE | SWP_FRAMECHANGED);
}
m_border will be for example {7,7,7,7};
Allow Windows to do the painting on left, right, bottom border. The top border is the only one changed
void CMainFrame::OnNcCalcSize(BOOL validate, NCCALCSIZE_PARAMS FAR* lpncsp)
{
if (validate)
{
lpncsp->rgrc[0].left += m_border.left;
lpncsp->rgrc[0].right -= m_border.right;
lpncsp->rgrc[0].bottom -= m_border.bottom;
}
else
{
CFrameWnd::OnNcCalcSize(validate, lpncsp);
}
}
see also How to glow the minimum. maximum and close button?

Resizing a modeless property sheet

I have a class derived from CPropertysheet. It has two property pages in it. I have made the sheet modeless. But resizing using the mouse drag is not possible. How to make the propertysheet a resizable one?
For modal property sheet see links in comment section. For modeless version, create the property sheet with WS_THICKFRAME. This is enough to make the dialog resizable. For example:
propSheet->Create(this, WS_THICKFRAME |
WS_VISIBLE | WS_SYSMENU | WS_POPUP | WS_VISIBLE | WS_CAPTION);
To handle the resizing, add the following members:
class CMyPropertySheet:public CPropertySheet
{
CRect save_rc;//used in OnSize
CRect minimum_rc;//used in OnGetMinMaxInfo
BOOL OnInitDialog();
void OnSize(UINT nType, int cx, int cy);
void OnGetMinMaxInfo(MINMAXINFO FAR* lpMMI);
...
};
Overload OnInitDialog as follows:
BOOL CMyPropertySheet::OnInitDialog()
{
//override for modeless:
m_bModeless = FALSE;
m_nFlags |= WF_CONTINUEMODAL;
BOOL bResult = CPropertySheet::OnInitDialog();
m_bModeless = TRUE;
m_nFlags &= ~WF_CONTINUEMODAL;
//save rectangles for resizing
GetClientRect(&save_rc); //save the old rect for resizing
GetClientRect(&minimum_rc); //save the original rect for OnGetMinMaxInfo
return bResult;
}
The rest of it is explained in MSDN example:
void CMyPropertySheet::OnSize(UINT nType, int cx, int cy)
{
CPropertySheet::OnSize(nType, cx, cy);
if(nType == SIZE_MINIMIZED)
return;
if (!GetActivePage()) return;
if (!GetTabControl()) return;
int dx = cx - save_rc.Width();
int dy = cy - save_rc.Height();
//count how many childs are in window
int count = 0;
for(CWnd *child = GetWindow(GW_CHILD); child; child = child->GetWindow(GW_HWNDNEXT))
count++;
HDWP hDWP = ::BeginDeferWindowPos(count);
for (CWnd *child = GetWindow(GW_CHILD); child; child = child->GetWindow(GW_HWNDNEXT))
{
CRect r;
child->GetWindowRect(&r);
ScreenToClient(&r);
if (child->SendMessage(WM_GETDLGCODE) & DLGC_BUTTON)
{
r.left += dx;
r.top += dy;
::DeferWindowPos(hDWP, child->m_hWnd, 0, r.left, r.top, 0, 0,
SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
}
else
{
r.right += dx;
r.bottom += dy;
::DeferWindowPos(hDWP, child->m_hWnd, 0, 0, 0, r.Width(), r.Height(),
SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);
}
}
::EndDeferWindowPos(hDWP);
GetClientRect(&save_rc);
}
void CMyPropertySheet::OnGetMinMaxInfo(MINMAXINFO FAR* lpMMI)
{
lpMMI->ptMinTrackSize.x = minimum_rc.Width();
lpMMI->ptMinTrackSize.y = minimum_rc.Height();
CPropertySheet::OnGetMinMaxInfo(lpMMI);
}
Also add ON_WM_SIZE and ON_WM_GETMINMAXINFO to message map

moving window and resize bottom flickers

I am trying to make a kind of popup notification window that slides out of the task bar.
At the moment I am only trying to get it to work if the taskbar is on bottom of screen, I have it working like it should.
But the problem im having is when the window is starting to appear/slide the bottom part of it flickers (depending on how big i have set my m_nIncrement value, it flashes the bottom area the same size as the m_nIncrement value)
The window disappears fine with no flicker.
//MyWindow
WNDCLASS wc =
{
CS_HREDRAW | CS_VREDRAW, __sWndProcDlg, 0, 0, g_hInst, nullptr,
LoadCursor(nullptr, IDC_ARROW), (HBRUSH)::GetStockObject(BLACK_BRUSH),
nullptr, m_lpszClassName
};
if (!RegisterClass(&wc))
return;
m_hWnd = CreateWindowEx(WS_EX_NOACTIVATE, m_lpszClassName, NULL, WS_POPUP | WS_VISIBLE, 0, 0, 0, 0, m_hWndParent, NULL, g_hInst, NULL);
//ShowWindow
void _ShowWindow(int _show)
{
unsigned int nDesktopWidth = m_rcDesktop.right - m_rcDesktop.left;
unsigned int nDesktopHeight = m_rcDesktop.bottom - m_rcDesktop.top;
unsigned int nScreenWidth = ::GetSystemMetrics(SM_CXSCREEN);
unsigned int nScreenHeight = ::GetSystemMetrics(SM_CYSCREEN);
BOOL bTaskbarOnRight = nDesktopWidth<nScreenWidth && m_rcDesktop.left == 0;
BOOL bTaskbarOnLeft = nDesktopWidth<nScreenWidth && m_rcDesktop.left != 0;
BOOL bTaskBarOnTop = nDesktopHeight<nScreenHeight && m_rcDesktop.top != 0;
BOOL bTaskbarOnBottom = nDesktopHeight<nScreenHeight && m_rcDesktop.top == 0;
if (_show)
{
ShowWindow(m_hWnd, SW_SHOW);
if (bTaskbarOnBottom)
{
m_nCurrentPos.cx = m_rcDesktop.right - m_sMaxSize.cx;
m_nCurrentPos.cy = m_rcDesktop.bottom - m_nCurrentSize.cy;
m_nTaskbarPlacement = TASKBAR_ON_BOTTOM;
}
KillTimer(m_hWnd, IDT_DISAPPEARING);
SetTimer(m_hWnd, IDT_APPEARING, 1, nullptr);
}
else
{
KillTimer(m_hWnd, IDT_APPEARING);
if (bTaskbarOnRight)
m_nCurrentPos.cx = m_rcDesktop.right - m_sMaxSize.cx;
else if (bTaskbarOnLeft)
m_nCurrentPos.cx = m_rcDesktop.left;
else if (bTaskBarOnTop)
m_nCurrentPos.cy = m_rcDesktop.top;
else //if (bTaskbarOnBottom)
m_nCurrentPos.cy = m_rcDesktop.bottom - m_nCurrentSize.cy;
SetTimer(m_hWnd, IDT_DISAPPEARING, 1, NULL);
}
}
//__OnTimer
LRESULT __OnTimer(HWND hWnd, UINT nIDEvent)
{
int m_nIncrement = 20;
if (nIDEvent == IDT_APPEARING)
{
switch (m_nTaskbarPlacement)
{
case TASKBAR_ON_BOTTOM:
if (m_nCurrentPos.cy > (m_rcDesktop.bottom - m_sMaxSize.cy))
{
m_nCurrentPos.cy -= m_nIncrement;
m_nCurrentSize.cy = m_rcDesktop.bottom - m_nCurrentPos.cy;
}
else
KillTimer(hWnd, IDT_APPEARING);
break;
}
SetFocus(hWnd);
SetWindowPos(hWnd, HWND_TOPMOST, m_nCurrentPos.cx, m_nCurrentPos.cy, m_sMaxSize.cx, m_nCurrentSize.cy, SWP_NOOWNERZORDER | SWP_NOZORDER | SWP_NOACTIVATE);
}
else if (nIDEvent == IDT_DISAPPEARING)
{
switch (m_nTaskbarPlacement)
{
case TASKBAR_ON_BOTTOM:
if (m_nCurrentPos.cy < m_rcDesktop.bottom)
{
m_nCurrentPos.cy += m_nIncrement;
m_nCurrentSize.cy = m_rcDesktop.bottom - m_nCurrentPos.cy;
}
else
{
KillTimer(hWnd, IDT_DISAPPEARING);
MoveWindow(m_hWnd, 0, 0, 0, 0, FALSE);
ShowWindow(m_hWnd, SW_HIDE);
}
break;
}
SetWindowPos(hWnd, HWND_TOPMOST, m_nCurrentPos.cx, m_nCurrentPos.cy, m_sMaxSize.cx, m_nCurrentSize.cy, SWP_NOOWNERZORDER | SWP_NOZORDER | SWP_NOACTIVATE);
}
return true;
}
use AnimateWindow instead. it is easy to use. you may need to handle WM_PRINT, and this also is quit straightforward.
if ever you want to keep working on your code then:
1) remove from popup class the flags CS_HREDRAW & CS_VREDRAW
2) handle WM_ERASEBACKGROUND
finally. if it is a popup window, do not call SetFocus(); maybe you have to set zorder to TOP_MOST
good luck