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
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?
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;
}
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?
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
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