How do I prevent flickering on CTreeCtrl? - c++

I am using VS2010 to develop a MFC project. When I clicked on an item of a CTreeCtrl control, It's so strange that some other unrelated items flickers sometimes. I tried many computers, and this problem still exists. Any solutions?
This is a BCGControlBar project, the CTreeCtrl is linked with a CWorkSpaceBar2 class (Like the workspace bar in VS). I think this is not the point, because many other pure CTreeList demos also share the same flicker problem. A demo is provided below, you can find the flicker problem when you click an CTreeCtrl item. Although it is not my code, but we share the same problem.
http://www.verysource.com/testmytreectrl-59412.html
Below is some pieces of my code. You can find that I didn't do something strange that maybe cause the problem.
initIcons() function is for initializing icons for the items.
add2Tree(CTreeCtrl* pTree, AFDir* pDir, HTREEITEM hItemParent, bool bHead) function is for adding items.
OnSelchanged(NMHDR* pNMHDR, LRESULT* pResult) function does nothing when an item is clicked.
void CWorkSpaceBar2::initIcons()
{
// SHFILEINFO sfi;
// SHGetFileInfo(m_strDataDir, 0, &sfi, sizeof(SHFILEINFO), SHGFI_SYSICONINDEX | SHGFI_SMALLICON);
// m_iIconFolder = sfi.iIcon;
//
// SHFILEINFO sfi2;
// SHGetFileInfo(m_strDataDir + _T("coloring_rules.txt"), 0, &sfi2, sizeof(SHFILEINFO), SHGFI_SYSICONINDEX | SHGFI_SMALLICON);
// m_iIconScript = sfi2.iIcon;
//
// SHFILEINFO sfi3;
// SHGetFileInfo(m_strDataDir + _T("color2.ini"), 0, &sfi3, sizeof(SHFILEINFO), SHGFI_SYSICONINDEX | SHGFI_SMALLICON);
// m_iIconProperty = sfi3.iIcon;
// HICON hIcon;
// hIcon = AfxGetApp()->LoadIcon(IDB);
/* Cil1.Create(IDB_VSICON, 16, 1, RGB(255, 0, 255)); // 建立16 位图像控制*/
m_imageList.Create(16, 16, ILC_COLOR32 | ILC_MASK, 10, 10);
CBitmap bitmap;
bitmap.LoadBitmap(IDB_VSICON);
m_imageList.Add(&bitmap, RGB(255, 0, 255));
// int bbb = Cil1.Add(aaa.FromHandle((HBITMAP) aaa), RGB(0, 0, 0));
//Cil1.Add(LoadIcon(NULL, (LPCTSTR) IDR_AffensicsTYPE));// 增加选中状态图像
m_wndTree.SetImageList(&m_imageList, TVSIL_NORMAL); //LVSIL_SMALL
m_iIconFolderClosed = LIST_ICON_FOLDER_CLOSED;
m_iIconScript = LIST_ICON_SCRIPT;
m_iIconProperty = LIST_ICON_PROPERTY;
//
// HTREEITEM root = m_wndTree.InsertItem(_T("root"), 0, 0);
// int i,j;
// for (i = 0; i < 4; i++)
// {
// HTREEITEM item = m_wndTree.InsertItem(_T("item"), 1, 1,root);
// for (j = 0; j < 3; j++)
// {
// m_wndTree.InsertItem(_T("small"), 0, 1, item);
// }
// }
}
void CWorkSpaceBar2::add2Tree(CTreeCtrl* pTree, AFDir* pDir, HTREEITEM hItemParent, bool bHead)
{
HTREEITEM hItem;
int iIcon = pDir->is_dir? m_iIconFolderClosed : m_iIconScript;
if (bHead)
{
hItem = pTree->InsertItem(pDir->name, iIcon, iIcon, TVI_ROOT);
m_mapDirs[hItem] = pDir;
m_mapDirs_R[pDir] = hItem;
}
else
{
hItem = pTree->InsertItem(pDir->name, iIcon, iIcon, hItemParent);
m_mapDirs[hItem] = pDir;
m_mapDirs_R[pDir] = hItem;
}
if (pDir->is_dir == TRUE)
{
for (size_t i = 0; i < pDir->dirs.size(); i ++)
{
add2Tree(pTree, pDir->dirs[i], hItem, FALSE);
}
for (size_t i = 0; i < pDir->files.size(); i ++)
{
add2Tree(pTree, pDir->files[i], hItem, FALSE);
}
}
else
{
addScript2Tree(pTree, pDir, hItem);
}
}
void CWorkSpaceBar2::OnSelchanged(NMHDR* pNMHDR, LRESULT* pResult)
{
*pResult = 0;
return;
}

Related

DrawText Refresh Problem - Text Disappears

I'm using Objective Grid and wanted to have a grid cell control that can show an icon and text. So I took RogueWave's example (https://rwkbp.makekb.com/entry/466/) and modified it as below.
Initially the text renders correctly, but if I do anything in the grid that refreshes the relevant cell, the text disappears. If I update the whole grid, or even refresh the cell by dragging it off screen and back again, the text reappears - until it disappears again. Seems like a paint or InvalidateRect problem - but I can't figure out how to fix it. Any ideas?
Thanks!
//------------------------------------------------------------------------------
void CGXIconControl::Draw(CDC* pDC, CRect rect, ROWCOL nRow, ROWCOL nCol, const CGXStyle& style, const CGXStyle* pStandardStyle)
//------------------------------------------------------------------------------
{
BOOL b;
ASSERT(pDC != NULL && pDC->IsKindOf(RUNTIME_CLASS(CDC)));
// ASSERTION-> Invalid Device Context ->END
ASSERT(nRow <= Grid()->GetRowCount() && nCol <= Grid()->GetColCount());
// ASSERTION-> Cell coordinates out of range ->END
ASSERT_VALID(pDC);
DrawBackground(pDC, rect, style);
pDC->SetBkMode(TRANSPARENT);
if (rect.right <= rect.left || rect.Width() <= 1 || rect.Height() <= 1)
{
return;
}
CString str = style.GetIncludeValue() ? style.GetValue() : _T("");
CString sTxtBefore = _T("");
CString sTxtAfter = _T("");
int nHAlign = style.GetHorizontalAlignment();
int nVAlign = style.GetVerticalAlignment();
// Save these value to restore them when done drawing
COLORREF crOldTextColor = pDC->GetTextColor();
COLORREF crOldBkColor = pDC->GetBkColor();
if (!style.GetEnabled())
{
pDC->SetTextColor(::GetSysColor(COLOR_GRAYTEXT));
}
CBrush Brush;
Brush.CreateSolidBrush(style.GetEnabled() ? ::GetSysColor(COLOR_WINDOWTEXT) : ::GetSysColor(COLOR_GRAYTEXT));
LOGBRUSH lBrush = { 0 };
Brush.GetLogBrush(&lBrush);
CBrush* pOldBrush = (CBrush*)pDC->SelectStockObject(NULL_BRUSH);
CPen linePen;
linePen.CreatePen(PS_SOLID, 1, &lBrush);
CPen* pOldPen = pDC->SelectObject(&linePen);
// Set font bold if necessary
CFont* pCurfont = pDC->GetCurrentFont();
LOGFONT lf;
CFont font;
if (pCurfont)
{
pCurfont->GetLogFont(&lf);
}
if (style.GetFont().GetBold())
{
lf.lfWeight = FW_BOLD;
}
font.CreateFontIndirect(&lf);
CFont* pOldFont = pDC->SelectObject(&font);
int nIcoStart = str.Find(_T("#ICO"));
if (nIcoStart == -1)
{
// We didn't find an icon indicator, so just draw the text
pDC->DrawText(str, rect, DT_SINGLELINE|nHAlign|nVAlign);
}
else
{
sTxtBefore = str.Left(nIcoStart);
CSize szBefore = pDC->GetTextExtent(sTxtBefore);
int nIconEnd = str.Find(_T(")"), nIcoStart);
CString strIDResource = str.Mid(nIcoStart + 5, nIconEnd - (nIcoStart + 5));
UINT nIDResource = _ttoi(strIDResource);
// Load the highest bit-depth available
HICON hIcon = NULL;
hIcon = LoadResourceIcon(nIDResource, m_nIconSize, m_nIconSize, 32);
hIcon == NULL ? LoadResourceIcon(nIDResource, m_nIconSize, m_nIconSize, 24) : NULL;
hIcon == NULL ? LoadResourceIcon(nIDResource, m_nIconSize, m_nIconSize, 16) : NULL;
hIcon == NULL ? LoadResourceIcon(nIDResource, m_nIconSize, m_nIconSize, 8) : NULL;
sTxtAfter = str.Right(str.GetLength() - nIconEnd - 1);
CSize szAfter = pDC->GetTextExtent(sTxtAfter);
CRect rectCell = CGXControl::GetCellRect(nRow, nCol, rect, &style);
CRect rectBefore = rectCell;
CRect rectAfter = rectCell;
int nTotalWidth = szBefore.cx + m_nIconSize + szAfter.cx;
// Calculate positions
int nTop, nLeft;
switch (nHAlign)
{
case DT_LEFT:
{
rectBefore.right = rectBefore.left + szBefore.cx;
nLeft = rectBefore.right;
rectAfter.left = nLeft + m_nIconSize;
rectAfter.right = rectAfter.left + szAfter.cx;
} break;
case DT_CENTER:
{
rectBefore.left = (rectCell.right - rectCell.Width() / 2) - nTotalWidth / 2;
rectBefore.right = rectBefore.left + szBefore.cx;
nLeft = rectBefore.right;
rectAfter.left = nLeft + m_nIconSize;
rectAfter.right = rectAfter.left + szAfter.cx;
} break;
case DT_RIGHT:
{
// Work from the right
rectAfter.right = rectCell.right;
rectAfter.left = rectAfter.right - szAfter.cx;
nLeft = rectAfter.left - m_nIconSize;
rectBefore.right = nLeft;
rectBefore.left = rectBefore.right - szBefore.cx;
} break;
}
switch (nVAlign)
{
case DT_TOP:
nTop = rectCell.top;
break;
case DT_VCENTER:
nTop = rectCell.top + (rectCell.Height() / 2 - m_nIconSize / 2);
break;
case DT_BOTTOM:
nTop = rectCell.bottom - m_nIconSize;
break;
}
if (!sTxtBefore.IsEmpty())
{
pDC->DrawText(sTxtBefore, rectBefore, DT_SINGLELINE|nHAlign|nVAlign);
}
b = ::DrawIconEx(pDC->m_hDC, nLeft, nTop, hIcon, m_nIconSize, m_nIconSize, 0, NULL, DI_NORMAL);
b = ::DestroyIcon(hIcon);
if (!sTxtAfter.IsEmpty())
{
pDC->DrawText(sTxtAfter, rectAfter, DT_SINGLELINE|nHAlign|nVAlign);
}
}
// Reset original values
pDC->SetTextColor(crOldTextColor);
pDC->SetBkColor(crOldBkColor);
pDC->SelectObject(pOldBrush);
pDC->SelectObject(pOldPen);
pDC->SelectObject(pOldFont);
// Child Controls: spin-buttons, hotspot, combobox btn, ...
CGXControl::Draw(pDC, rect, nRow, nCol, style, pStandardStyle);
}

Set position of MFC dialog-based application window

I have a CDialog based MFC application. I save the window's current position when the program ends. When the program starts, I want to restore the previous position. I'm currently trying to do this in OnInitDialog(), however, the program asserts when I call SetWindowPos() from within OnInitDialog(). My call to SetWindowPos() is similar to:
SetWindowPos(&CWnd::wndTop, 10, 10, 500, 500, SWP_NOREDRAW | SWP_NOZORDER);
The assertion relates to a null m_hWnd handle.
Is this the correct place to re-position a dialog-based application's window?
Any ideas on why I'm asserting?
In the process of providing more information to my initial question, I discovered that calling SetWindowPos() passed messages to toolbars within the dialog that hadn't been created, yet. I moved the SetWindowPos() to the end of OnInitiDialog() and it worked.
Thanks for the encouragement, #zett42.
This is how I do it:
#include "stdafx.h"
#include "resource.h"
#include "ResizingDialog.h"
IMPLEMENT_DYNAMIC(CResizingDialog, CDialogEx)
CResizingDialog::CResizingDialog(const CString& strWindowID, UINT nIDTemplate, CWnd* pParent /* nullptr */, bool bOnlyStorePosition /* false */)
: CDialogEx(nIDTemplate, pParent)
, m_strWindowID(strWindowID)
, m_bOnlyStorePosition(bOnlyStorePosition)
, m_bDoNotShowResizeIcon(false)
{
m_rcInit.SetRect(0, 0, 0, 0);
}
CResizingDialog::~CResizingDialog()
= default;
void CResizingDialog::DoDataExchange(CDataExchange* pDX)
{
CDialogEx::DoDataExchange(pDX);
}
BEGIN_MESSAGE_MAP(CResizingDialog, CDialogEx)
ON_WM_GETMINMAXINFO()
ON_WM_DESTROY()
ON_WM_PAINT()
ON_WM_NCHITTEST()
ON_WM_SIZE()
END_MESSAGE_MAP()
BOOL CResizingDialog::OnInitDialog()
{
CDialogEx::OnInitDialog();
// Save Initial window size to m_rcInit
GetWindowRect(&m_rcInit);
//if (!m_bOnlyStorePosition && !m_bDoNotShowResizeIcon)
//InitialiseResizeIcon(m_bmpResize, m_lblResize, this);
RestoreWindowPosition(m_strWindowID, this, true);
return TRUE; // return TRUE unless you set the focus to a control
// EXCEPTION: OCX Property Pages should return FALSE
}
void CResizingDialog::OnGetMinMaxInfo(MINMAXINFO* lpMMI)
{
// Set the minimum window size to initial size.
lpMMI->ptMinTrackSize.x = m_rcInit.Width();
lpMMI->ptMinTrackSize.y = m_rcInit.Height();
CDialogEx::OnGetMinMaxInfo(lpMMI);
}
void CResizingDialog::RestoreWindowPosition(CString strWindow, CWnd* pWindow, bool bOverrideState)
{
int max_x, max_y;
RECT rtWindow;
if (pWindow == nullptr)
return;
// Only restore if there is a previously saved position
if ((rtWindow.top = AfxGetApp()->GetProfileInt(strWindow, _T("Top"), -1)) != -1 &&
(rtWindow.left = AfxGetApp()->GetProfileInt(strWindow, _T("Left"), -1)) != -1 &&
(rtWindow.bottom = AfxGetApp()->GetProfileInt(strWindow, _T("Bottom"), -1)) != -1 &&
(rtWindow.right = AfxGetApp()->GetProfileInt(strWindow, _T("Right"), -1)))
{
max_x = rtWindow.right - rtWindow.left;
max_y = rtWindow.bottom - rtWindow.top;
// Get a handle to the monitor
HMONITOR hMonitor = ::MonitorFromPoint(
CPoint(rtWindow.left, rtWindow.top), MONITOR_DEFAULTTONEAREST);
// Get the monitor info
MONITORINFO monInfo;
monInfo.cbSize = sizeof(MONITORINFO);
if (::GetMonitorInfo(hMonitor, &monInfo) == 0)
AfxMessageBox(_T("GetMonitorInfo failed"));
else
{
// Adjust for work area
rtWindow.left += monInfo.rcWork.left - monInfo.rcMonitor.left;
rtWindow.top += monInfo.rcWork.top - monInfo.rcMonitor.top;
// Ensure top left point is on screen
if (CRect(monInfo.rcWork).PtInRect(CPoint(rtWindow.left, rtWindow.top)) == FALSE)
{
rtWindow.left = monInfo.rcWork.left;
rtWindow.top = monInfo.rcWork.top;
}
rtWindow.right = rtWindow.left + max_x;
rtWindow.bottom = rtWindow.top + max_y;
// Restore window size
pWindow->MoveWindow(&rtWindow, FALSE);
}
if (bOverrideState)
{
// Let us override by restoring the window state
int iState = AfxGetApp()->GetProfileInt(strWindow, _T("ShowCmd"), SW_SHOWNORMAL);
pWindow->ShowWindow(iState);
}
}
}
void CResizingDialog::SaveWindowPosition(CString strWindow, CWnd* pWindow)
{
WINDOWPLACEMENT wp;
if (pWindow == nullptr)
return;
pWindow->GetWindowPlacement(&wp);
// Commit to registry
AfxGetApp()->WriteProfileInt(strWindow, _T("Top"), wp.rcNormalPosition.top);
AfxGetApp()->WriteProfileInt(strWindow, _T("Left"), wp.rcNormalPosition.left);
AfxGetApp()->WriteProfileInt(strWindow, _T("Bottom"), wp.rcNormalPosition.bottom);
AfxGetApp()->WriteProfileInt(strWindow, _T("Right"), wp.rcNormalPosition.right);
AfxGetApp()->WriteProfileInt(strWindow, _T("ShowCmd"), wp.showCmd);
}
void CResizingDialog::InitialiseResizeIcon(CBitmap& rBmpResize, CStatic& rLblResize, CWnd* pDialog)
{
CRect rcIcon, rcClient;
if (pDialog != nullptr)
{
rBmpResize.LoadOEMBitmap(OBM_SIZE);
rLblResize.Create(nullptr, WS_CHILD | WS_VISIBLE | SS_BITMAP,
CRect(0, 0, 16, 16), pDialog, IDC_STATIC_RESIZE);
rLblResize.SetBitmap(rBmpResize);
//theApp.UpdateBitmapBackground(rLblResize.GetBitmap(), true, GetSysColor(COLOR_ACTIVECAPTION));
pDialog->GetClientRect(rcClient);
rLblResize.GetClientRect(rcIcon);
rLblResize.SetWindowPos(&CWnd::wndTop,
rcClient.right - rcIcon.Width(),
rcClient.bottom - rcIcon.Height(), 0, 0, SWP_NOSIZE);
CMFCDynamicLayout *pDynamicLayout = pDialog->GetDynamicLayout();
if (pDynamicLayout != nullptr)
{
CMFCDynamicLayout::MoveSettings moveSettings = CMFCDynamicLayout::MoveHorizontalAndVertical(100, 100);
CMFCDynamicLayout::SizeSettings sizeSettings = CMFCDynamicLayout::SizeNone();
pDynamicLayout->AddItem(rLblResize.GetSafeHwnd(), moveSettings, sizeSettings);
}
}
}
void CResizingDialog::OnDestroy()
{
CDialogEx::OnDestroy();
SaveWindowPosition(m_strWindowID, this);
}
void CResizingDialog::DoNotShowResizeIcon()
{
m_bDoNotShowResizeIcon = true;
}
void CResizingDialog::OnPaint()
{
CPaintDC dc(this); // device context for painting
// TODO: Add your message handler code here
// Do not call CDialogEx::OnPaint() for painting messages
if (!m_bOnlyStorePosition && !m_bDoNotShowResizeIcon)
{
CRect rc;
GetClientRect(&rc);
rc.left = rc.right - ::GetSystemMetrics(SM_CXHSCROLL);
rc.top = rc.bottom - ::GetSystemMetrics(SM_CYVSCROLL);
HTHEME ht = OpenThemeData(GetSafeHwnd(), L"STATUS");
if (ht)
{
DrawThemeBackground(ht, dc, SP_GRIPPER, 0, &rc, nullptr);
CloseThemeData(ht);
}
else
{
dc.DrawFrameControl(rc, DFC_SCROLL, DFCS_SCROLLSIZEGRIP);
}
}
}
LRESULT CResizingDialog::OnNcHitTest(CPoint point)
{
CRect rc;
GetWindowRect(rc);
rc.left = rc.right - ::GetSystemMetrics(SM_CXHSCROLL);
rc.top = rc.bottom - ::GetSystemMetrics(SM_CYVSCROLL);
if (rc.PtInRect(point))
return HTBOTTOMRIGHT;
return CDialogEx::OnNcHitTest(point);
}
void CResizingDialog::OnSize(UINT nType, int cx, int cy)
{
CDialogEx::OnSize(nType, cx, cy);
Invalidate(TRUE);
}
I don't know how you are storing the window position but my code factors in for multiple monitor configurations.

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?

Does CMFCStatusBar::SetPaneIcon supports alpha icon?

My App setup icons for panes in status bar (class CMFCStatusBar). There is only one method for this task - CMFCStatusBar::SetPaneIcon(). But if icon have alpha channel this method loads wrong image (on black background).
I looked into the source code and I saw that CMFCStatusBar uses internal HIMAGELISTs for every panes. All this HIMAGELIST creates with flag ILC_MASK | ILC_COLORDDB, not ILC_COLOR32 or ILC_COLOR24.
This is a bug!
So, is there a way to use the icons with alpha channel in CMFCStatusBar panes?
Example:
HICON hIcon = ::LoadImage(hModule, MAKEINTRESOURCE(nIconID), IMAGE_ICON, 16, 16, LR_DEFAULTCOLOR);
m_StatusBar.SetPaneIcon(m_StatusBar.CommandToIndex(ID_INDICATOR_1), hIcon);
Microsoft developed the CMFC classes (Feature Pack) from the BCG toolkit. If you compare the CMFCStatusBar::SetPaneIcon() method with the BCG method from the same class, you’ll notice a few subtle differences.
BCG defines its method with a flag for alpha blend. The method looks like:
void CBCGPStatusBar::SetPaneIcon (int nIndex, HBITMAP hBmp,
COLORREF clrTransparent, BOOL bUpdate, BOOL bAlphaBlend/* = FALSE*/)
The CMFC equivalent removes the ‘bAlphaBlend’ flag.
Comparing the method code, you’ll see that BCG uses the following code that has been removed from the CMFC version:
DWORD dwFlags = ILC_MASK | ILC_COLORDDB;
if (bAlphaBlend)
{
dwFlags = ILC_COLOR32;
}
pSBP->hImage = ::ImageList_Create (pSBP->cxIcon, pSBP->cyIcon, dwFlags, 1, 0);
I’m not sure why the CMFC version differs so much (Microsoft may have a valid reason), but, I’ve included the BCG version below. I would study the BCG version and possibly create your own ‘SetPaneIcon’ method from that code (at your own risk).
void CBCGPStatusBar::SetPaneIcon (int nIndex, HBITMAP hBmp,
COLORREF clrTransparent, BOOL bUpdate, BOOL bAlphaBlend/* = FALSE*/)
{
ASSERT_VALID(this);
CBCGStatusBarPaneInfo* pSBP = _GetPanePtr(nIndex);
if (pSBP == NULL)
{
ASSERT (FALSE);
return;
}
// Disable animation (if exist):
SetPaneAnimation (nIndex, NULL, 0, FALSE);
if (hBmp == NULL)
{
if (pSBP->hImage != NULL)
{
::ImageList_Destroy (pSBP->hImage);
}
pSBP->hImage = NULL;
if (bUpdate)
{
InvalidatePaneContent (nIndex);
}
return;
}
BITMAP bitmap;
::GetObject (hBmp, sizeof (BITMAP), &bitmap);
if (pSBP->hImage == NULL)
{
pSBP->cxIcon = bitmap.bmWidth;
pSBP->cyIcon = bitmap.bmHeight;
DWORD dwFlags = ILC_MASK | ILC_COLORDDB;
if (bAlphaBlend)
{
dwFlags = ILC_COLOR32;
}
pSBP->hImage = ::ImageList_Create (pSBP->cxIcon, pSBP->cyIcon, dwFlags, 1, 0);
RecalcLayout ();
}
else
{
ASSERT (pSBP->cxIcon == bitmap.bmWidth);
ASSERT (pSBP->cyIcon == bitmap.bmHeight);
::ImageList_Remove (pSBP->hImage, 0);
}
//---------------------------------------------------------
// Because ImageList_AddMasked changes the original bitmap,
// we need to create a copy:
//---------------------------------------------------------
HBITMAP hbmpCopy = (HBITMAP) ::CopyImage (hBmp, IMAGE_BITMAP, 0, 0, 0);
if (bAlphaBlend)
{
::ImageList_Add (pSBP->hImage, hbmpCopy, NULL);
}
else
{
::ImageList_AddMasked (pSBP->hImage, hbmpCopy, clrTransparent);
}
::DeleteObject (hbmpCopy);
if (bUpdate)
{
InvalidatePaneContent (nIndex);
}
}
My solution, based in rrirower code.
CMFCStatusBarPaneInfo* CMyStatusBar::GetPane(int nIndex) const {
if (nIndex == 255 && m_nCount < 255) {
// Special case for the simple pane
for (int i = 0; i < m_nCount; i++)
{
CMFCStatusBarPaneInfo* pSBP = GetPane(i);
ENSURE(pSBP != NULL);
if (pSBP->nStyle & SBPS_STRETCH) {
return pSBP;
}
}
}
if (nIndex < 0 || nIndex >= m_nCount) {
return NULL;
}
if (m_pData == NULL) {
ASSERT(FALSE);
return NULL;
}
return((CMFCStatusBarPaneInfo*)m_pData) + nIndex;
}
void CMyStatusBar::SetPaneIcon(int nIndex, HICON hIcon, BOOL bUpdate /*= TRUE*/)
{
ASSERT_VALID(this);
CMFCStatusBarPaneInfo* pSBP = GetPane(nIndex);
if (pSBP == NULL)
{
ASSERT(FALSE);
return;
}
// Disable animation(if exist):
SetPaneAnimation(nIndex, NULL, 0, FALSE);
if (hIcon == NULL)
{
if (pSBP->hImage != NULL)
{
::ImageList_Destroy(pSBP->hImage);
}
pSBP->hImage = NULL;
if (bUpdate)
{
InvalidatePaneContent(nIndex);
}
return;
}
ICONINFO iconInfo;
::GetIconInfo(hIcon, &iconInfo);
BITMAP bitmap;
::GetObject(iconInfo.hbmColor, sizeof(BITMAP), &bitmap);
::DeleteObject(iconInfo.hbmColor);
::DeleteObject(iconInfo.hbmMask);
if (pSBP->hImage == NULL)
{
pSBP->cxIcon = bitmap.bmWidth;
pSBP->cyIcon = bitmap.bmHeight;
pSBP->hImage = ::ImageList_Create(pSBP->cxIcon, pSBP->cyIcon, ILC_COLOR32 | ILC_MASK, 1, 0);
::ImageList_AddIcon(pSBP->hImage, hIcon);
RecalcLayout();
}
else
{
ASSERT(pSBP->cxIcon == bitmap.bmWidth);
ASSERT(pSBP->cyIcon == bitmap.bmHeight);
::ImageList_ReplaceIcon(pSBP->hImage, 0, hIcon);
}
if (bUpdate)
{
InvalidatePaneContent(nIndex);
}
}

MFC Custom Button control

I have created a custom button in mfc(VS 2013)DLL. It worked Fine. I have created a test dialog exe to test the dll. Then I added some code to simulate hover effect. Now when I run the Dialog exe, the button appears as wanted but when move the cursor on top of the button, the gradient changes but it throws an Debug Assertion.
void CBootstrapButton::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
{
dc = CDC::FromHandle(lpDrawItemStruct->hDC);//Get device context object
m_bIsFocused = (lpDrawItemStruct->itemState & ODS_FOCUS);
m_bIsDisabled = (lpDrawItemStruct->itemState & ODS_DISABLED);
m_bIsPressed = (lpDrawItemStruct->itemState & ODS_SELECTED);
//Preparing the Region to Draw
CRect btnRect = lpDrawItemStruct->rcItem;
int iCX = lpDrawItemStruct->rcItem.right;
int iCY = lpDrawItemStruct->rcItem.bottom;
CRgn Rgn1;//<-this is where I am getting assertion error
Rgn1.CreateRoundRectRgn(0, 0, lpDrawItemStruct->rcItem.right, lpDrawItemStruct->rcItem.bottom, 10, 10);
dc->SelectClipRgn(&Rgn1);
MemDC.CreateCompatibleDC(dc);
pDC = &MemDC;
Bmp.CreateCompatibleBitmap(dc, iCX, iCY);
OldBitmap = MemDC.SelectObject(&Bmp);
border = RGB(219, 219, 219);//Button Border color
/******* Some Color Logic*******/
//Applying Border
pDC->RoundRect(0, 0, lpDrawItemStruct->rcItem.right, lpDrawItemStruct->rcItem.bottom, 10, 10);
pDC->FillSolidRect(&lpDrawItemStruct->rcItem, border);
if (m_bIsFocused)
{
pDC->DrawFocusRect(&lpDrawItemStruct->rcItem);
}
//Applying Gradients
CRgn Rgn2;
Rgn2.CreateRoundRectRgn(1, 1, lpDrawItemStruct->rcItem.right - 1, lpDrawItemStruct->rcItem.bottom - 1, 10, 10);
pDC->SelectClipRgn(&Rgn2);
RECT rect = lpDrawItemStruct->rcItem;
for (int i = 0; i<rect.bottom; i++)
{
int r, g, b;
r = r1 + (i * (r2 - r1) / rect.bottom);
g = g1 + (i * (g2 - g1) / rect.bottom);
b = b1 + (i * (b2 - b1) / rect.bottom);
pDC->FillSolidRect(0, i, rect.right, 1, RGB(r, g, b));
}
//Start Drawing Text
CString strText;
GetWindowText(strText);
int iOldMode = pDC->SetBkMode(TRANSPARENT);
COLORREF crOldColor;
//Setting Text Color
if (btnStyle==BTN_DEFAULT)
{
crOldColor = pDC->SetTextColor(RGB(0, 0, 0));
}
else
{
crOldColor = pDC->SetTextColor(RGB(255, 255, 255));
}
//Setting Font for Text (Default Font Name=Open Sans,Default Font Size=24)
//TODO Font Size according to Button Size.
CFont font;
font.CreateFont(
24, // nHeight//Font Size
0, // nWidth
0, // nEscapement
0, // nOrientation
FW_NORMAL, // nWeight
FALSE, // bItalic
FALSE, // bUnderline
0, // cStrikeOut
ANSI_CHARSET, // nCharSet
OUT_DEFAULT_PRECIS, // nOutPrecision
CLIP_DEFAULT_PRECIS, // nClipPrecision
DEFAULT_QUALITY, // nQuality
DEFAULT_PITCH | FF_SWISS, // nPitchAndFamily
_T("Open Sans"));
CFont* def_font = pDC->SelectObject(&font);
//Draw Text
pDC->DrawText(strText, &lpDrawItemStruct->rcItem, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
dc->BitBlt(0, 0, iCX, iCY, pDC, 0, 0, SRCCOPY);
pDC->SetTextColor(crOldColor);
pDC->SetBkMode(iOldMode);
pDC->SelectObject(OldBitmap);
pDC->SelectObject(def_font);
//End Drawing Text
//Cleaning up
pDC->DeleteDC();
dc->SelectClipRgn(NULL);
dc->Detach();
}
Code for hover Effect
void CBootstrapButton::OnMouseMove(UINT nFlags, CPoint point)
{
CWnd* wndUnderMouse = NULL;
CWnd* wndActive = this;
TRACKMOUSEEVENT csTME;
CButton::OnMouseMove(nFlags, point);
ClientToScreen(&point);
wndUnderMouse = WindowFromPoint(point);
if (nFlags & MK_LBUTTON && m_bMouseOnButton == FALSE) return;
wndActive = GetActiveWindow();
if (wndUnderMouse && wndUnderMouse->m_hWnd == m_hWnd && wndActive)
{
if (!m_bMouseOnButton)
{
m_bMouseOnButton = TRUE;
Invalidate();
csTME.cbSize = sizeof(csTME);
csTME.dwFlags = TME_LEAVE;
csTME.hwndTrack = m_hWnd;
::_TrackMouseEvent(&csTME);
} // if
}
else CancelHover();
}
void CBootstrapButton::CancelHover()
{
if (m_bMouseOnButton)
{
m_bMouseOnButton = FALSE;
Invalidate();
}
}
Try
this->ReleaseDC(dc);
instead of
dc->Detach();
at the end of your function.