MFC's dialog-based app title bar highlighting visual artifacts on Windows 10 (i.e. bugs in CDialogEx)

I'm not sure why am I getting this visual artifact?

Here's how to repro:

I'm using Visual Studio 2017 Community. Create a new C++ -> MFC project:

Then specify "dialog based":

Then build as "Debug" x86 app and run it.

So I'm running it on Windows 10.

When this dialog-based process has focus, it looks as I would expect it:

but if I switch keyboard focus to some other app (by clicking on it), this dialog-based process still retains its title bar color:

I'm not sure if it's just a matter of a visual glitch or if there's a deeper mess-up with the window message handling. How do I correct it? (This wasn't an issue with older MFC projects.)

I managed to replicate your problem and found a quick fix for it. You need to add the WM_ACTIVATE message handler to your main dialog, comment out the base class OnActivate and modify it like this:

void CMFCApplication1Dlg::OnActivate(UINT nState, CWnd* pWndOther, BOOL bMinimized)
{
    //CDialogEx::OnActivate(nState, pWndOther, bMinimized);

    // TODO: Add your message handler code here
    this->Default();
}

CWnd::Default call is needed to keep the active/inactive visualization of the default button.

Middlesbrough F.C. (Soccer club), 412.3k Posts - See Instagram photos and videos from 'mfc' hashtag. Manulife (MFC) is poised to grow over the long term given its operational excellence and solid capital position. PR Newswire • 24 days ago John Hancock Retirement Launches Onboarding Resource

OK, as much as I appreciate @VuVirt's solution, it doesn't completely remove all the bugs that are shipped in the default Dialog-based solution in VS2017. It solves the title bar focus issue, but while continuing to develop my project I encountered another bug. So I'm copy-and-pasting it from my comment to his answer:

There's still some kinda screw-up there. I'm not sure if it's related to this fix or not. Ex: If you create a button and then in its handler try to do: CFileDialog d(TRUE, NULL, NULL, OFN_HIDEREADONLY | OFN_EXPLORER, NULL, this); d.DoModal(); to open a file picker dialog. When file picker opens up, close it and see if the title bar of the parent MFC dialog window goes back to being active. In my case it remains inactive until I click onto the Windows taskbar and then back onto that MFC app.

After banging my head against the wall trying to see what is going on there, I decided to try an earlier solution proposed by @zett42 in the comments to my original question (i.e. to replace CDialogEx with CDialog) and it worked! All the bugs are gone!

So here's my verdict: CDialogEx is buggy af.

The resolution is quite simple: When you create a new dialog-based project use project-wide find-and-replace (in the Edit menu) and replace all occurrences of CDialogEx with CDialog. And that is it. (I tried to use VS2017's refactoring tool for that but it messed it up and didn't replace it all. So simple search-and-replace does the job.)

And if you think that you'll be missing some functionality without CDialogEx, then you won't. All it does (besides introducing bugs) is that it adds background images and colors to the dialog.

So until MS fixes those glaring bugs in their templates I'm sticking with this approach.

MFC, The Microsoft Foundation Class (MFC) Library provides an object-oriented wrapper over much of the Win32 and COM APIs. Although it can be  Manulife Financial Corporation Common Stock (MFC) Stock Quotes - Nasdaq offers stock quotes & market activity data for US and global markets.

This seems to be a bug in CDialogImpl::OnActivate and CDialogImpl::OnNcActivate:

void CDialogImpl::OnNcActivate(BOOL& bActive)
{
  if (m_Dlg.m_nFlags & WF_STAYACTIVE)
      bActive = TRUE;
  if (!m_Dlg.IsWindowEnabled())
      bActive = FALSE;
}

void CDialogImpl::OnActivate(UINT nState, CWnd* pWndOther)
{
  m_Dlg.m_nFlags &= ~WF_STAYACTIVE;
  CWnd* pWndActive = (nState == WA_INACTIVE) ? pWndOther : &m_Dlg;
  if (pWndActive != NULL)
  {
      BOOL bStayActive = (pWndActive->GetSafeHwnd() == m_Dlg.GetSafeHwnd()
            || pWndActive->SendMessage(WM_FLOATSTATUS, FS_SYNCACTIVE));
      if (bStayActive)
          m_Dlg.m_nFlags |= WF_STAYACTIVE;
  }
  else
  {
      m_Dlg.SendMessage(WM_NCPAINT, 1);
  }
}

This is meant to give CDialogEx the ability to stay active, for example, when CMFCPopupMenu is shown.

But m_Dlg.SendMessage(WM_NCPAINT, 1) is a suspicious call. The usage doesn't match the documentation for WM_NCPAINT:

Parameters

wParam A handle to the update region of the window. The update region is clipped to the window frame.

lParam

This parameter is not used.

Additionally, OnNcActivate has an override based on IsWindowEnabled(). This seems to be a patch to fix the earlier problem in OnActivate. But it causes problems elsewhere, for example when using CFileDialog in CDialogEx

Suggested solution:

Modify CDialogEx::OnActivate so that it runs the default procedure. Or, change it such that it will force repaint.

BOOL CDialogEx::OnNcActivate(BOOL active)
{
    if(m_nFlags & WF_STAYACTIVE)
        active = TRUE;
    return(BOOL)DefWindowProc(WM_NCACTIVATE, active, 0L);
}

void CDialogEx::OnActivate(UINT nState, CWnd* pWndOther, BOOL bMinimized)
{
    Default();
}

or

void CDialogEx::OnActivate(UINT nState, CWnd* pWndOther, BOOL bMinimized)
{
    Default();

    //save the previous flag
    UINT previous_flag = m_nFlags;
    m_nFlags &= ~WF_STAYACTIVE;

    // Determine if this window should be active or not:
    CWnd* pWndActive = (nState == WA_INACTIVE) ? pWndOther : this;
    if(pWndActive != NULL)
    {
        BOOL bStayActive = pWndActive->GetSafeHwnd() == GetSafeHwnd() ||
            pWndActive->SendMessage(WM_FLOATSTATUS, FS_SYNCACTIVE);
        if(bStayActive)
            m_nFlags |= WF_STAYACTIVE;
    }

    if(previous_flag != m_nFlags && previous_flag & WF_STAYACTIVE)
    {
        //if the flag is changed, 
        //and if WF_STAYACTIVE was previously set, 
        //then OnNcActivate had handled it wrongly, do it again
        SendMessage(WM_NCACTIVATE, FALSE); //<- less wrong!
    }
}

This should work with CMFCPopupMenu for example. The MFC menu will open without deactivating the dialog.

I am not sure what SendMessage(WM_FLOATSTATUS, FS_SYNCACTIVE) is for, I haven't been able to test it... If it's necessary, it seems the code could be added on OnNcActivate, and then OnActivate is left alone.

Microsoft Foundation Class Library, See Tweets about #mfc on Twitter. See what people are saying and join the conversation. Headquartered in Toronto, Canada, Manulife Financial Corporation was founded in 1887. Manulife is one of the three dominant life insurers within its domestic Canadian market and possesses rapidly

#mfc hashtag on Instagram • Photos and Videos, Should you wish to skip a payment, do not use “Change due date” on self-service​. Please make a “Request for debt assistance” by clicking on the link in the  We demonstrate how MFC can solve typical challenges in waterborne high-quality architectural coatings regarding, for example, mud-cracking, viscosity loss upon tinting, sagging and sprayability. Microfibrillated Cellulose: A Novel and Renewable Multifunctional Additive for Waterborne Coatings

MFC Desktop Applications, Find the latest Manulife Financial Corporation (MFC) stock quote, history, news and other vital information to help you with your stock trading and investing. The Microsoft Foundation Class (MFC) Library provides an object-oriented wrapper over much of the Win32 and COM APIs. Although it can be used to create very simple desktop applications, it is most useful when you need to develop more complex user interfaces with multiple controls.

#mfc hashtag on Twitter, MFC Tutorial - The Microsoft Foundation Class (MFC) library provides a set of functions, constants, data types, and classes to simplify creating applications for  View and watch RocknRose's photo and video albums on MFC Share. 159 Albums, 11 Collections, 72 Items, 17 Clubs, 41 Goals. Menu. View On MFC Share.

Comments
  • I am not even sure how to make Windows switch colors of the title bar, based on whether it has focus.. :/
  • I can repro. The bug only happens with dialogs derived from CDialogEx. After I replaced all occurences of CDialogEx with CDialog, the problem disappeared.
  • @zett42 It's actually all wrong. When nState == WA_INACTIVE, then pWndOther points to our same dialog. The next line is basically if (m_Dlg.GetSafeHwnd() == m_Dlg.GetSafeHwnd()){m_Dlg.m_nFlags |= WF_STAYACTIVE;...} It's not even affected by WM_FLOATSTATUS. OnNcActivate does its job right and keeps the dialog look active based on WA_INACTIVE. You can remove all that code to fix it, but I am not sure how it affects the program if the dialog has children or siblings etc. with MFS_SYNCACTIVE flag.
  • Reported: developercommunity.visualstudio.com/content/problem/409317/…
  • They got back to me and they say this is not a bug "Thank you for your feedback! We have determined that this issue is not a bug. It displays a modal dialog after called dlg.DoModal(), the DoModal() function does not return until the user closes the dialog. If the user clicks the "OK/Cancel" button, the DoModal() function returns "IDOK/ IDCANCEL". So this behavior is by-design." What a joke!
  • Hmm, interesting. Thanks. Let me see what are they messing up there in that handler ...
  • @c00000fd the handler sets the default button active or inactive but obviously messes up the frame rendering
  • Yeah, it seems like the CDialogEx::OnActivate handler calls two methods: CWnd::Default() and then CDialogImpl::OnActivate(). The latter one seems to be causing this issue, or maybe it sending out the WM_FLOATSTATUS message does. (I'm not really sure what it's for, though.) The solution seems to be commenting out CDialogEx::OnActivate like you suggested, but then adding this->Default(); instead.
  • @c00000fd Thanks for the clarification, the answer is updated.
  • If anyone has a better solution please post it. Otherwise I'll mark this one as the answer.
  • Thanks for your analysis. Unfortunately there's too many bugs in that CDialogEx to justify using it. Like I said, simply replacing it with CDialog seems to do the trick. What I'm curious about, did they totally abandon any MFC/C++ in favor of .NET, WPF and other stuff that Visual Studio is packed with? It's been about 2 years since VS2017 was out and we're still seeing these glaring bugs in one of the main MFC classes. Their wizard creates any MFC dialog derived from that CDialogEx by default.
  • No, it works fine with CFileDialog. It needs additional override for OnNcActivate. The purpose of this post was to describe the problem which has to be fixed by Microsoft.
  • OK. Thanks. Are you going to report it to them then? If so can you post a link so that we don't do it multiple times.
  • And no, it doesn't work fine with CFileDialog. It's a somewhat different issue (again see my explanation in a separate answer) but it is still a bug in CDialogEx.
  • Did you add override for OnNcActivate? See the second suggestion I posted, it's shorter. Anyway, CDialog will work fine unless you are adding stuff like CMFCPopupMenu