簡體   English   中英

MFC基於對話框的應用程序標題欄突出顯示Windows 10上的可視化工件(即CDialogEx中的錯誤)

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

我不確定為什么我會得到這個視覺神器?

以下是如何重現:

我正在使用Visual Studio 2017社區。 創建一個新的C ++ - > MFC項目:

在此輸入圖像描述

然后指定“基於對話框”:

在此輸入圖像描述

然后構建為“Debug”x86應用程序並運行它。

所以我在Windows 10上運行它。

當這個基於對話框的過程具有焦點時,它看起來像我期望的那樣:

在此輸入圖像描述

但如果我將鍵盤焦點切換到其他應用程序(通過點擊它),這個基於對話框的過程仍然保留其標題欄顏色:

在此輸入圖像描述

我不確定這是否只是一個視覺故障的問題,或者是否有更深層次的窗口消息處理問題。 我該如何糾正? (對於較舊的MFC項目,這不是問題。)

我設法復制你的問題並找到了一個快速解決方案。 您需要將WM_ACTIVATE消息處理程序添加到主對話框中,注釋掉基類OnActivate並將其修改為:

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調用以保持默認按鈕的活動/非活動可視化。

好的,盡管我很欣賞@VuVirt的解決方案,但它並沒有完全刪除VS2017中基於對話框的默認解決方案中提供的所有錯誤。 它解決了標題欄焦點問題,但在繼續開發我的項目時,我遇到了另一個錯誤。 所以我將它從我的評論中復制並粘貼到他的答案中:

那里還有一些搞砸了。 我不確定它是否與此修復有關。 例如:如果你創建一個按鈕,然后在其處理程序中嘗試執行:CFileDialog d(TRUE,NULL,NULL,OFN_HIDEREADONLY | OFN_EXPLORER,NULL,this); d.DoModal(); 打開文件選擇器對話框。 打開文件選擇器時,關閉它並查看父MFC對話框窗口的標題欄是否恢復活動狀態。 在我的情況下,它一直處於非活動狀態,直到我點擊Windows任務欄然后再回到那個MFC應用程序。

在我的頭撞到牆上試圖看看那里發生了什么后,我決定嘗試@ zett42在我原來的問題的評論中提出的早期解決方案(即用CDialog替換CDialogEx)並且它有效! 所有的錯誤都消失了!

所以這是我的判決: CDialogEx是錯誤的。

解決方案非常簡單:當您創建一個新的基於對話框的項目時,使用項目范圍的查找和替換(在編輯菜單中)並用CDialog替換所有出現的CDialogEx 就是這樣。 (我嘗試使用VS2017的重構工具,但它搞砸了並沒有全部替換它。這樣簡單的搜索和替換工作。)

如果你認為沒有CDialogEx就會缺少某些功能,那么你就不會。 它所做的一切(除了引入錯誤)是它為對話框添加了背景圖像和顏色。

因此,在MS修復模板中那些明顯的錯誤之前,我堅持使用這種方法。

這似乎是CDialogImpl::OnActivateCDialogImpl::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); } } 

這意味着CDialogEx能夠保持活動狀態,例如,當顯示CMFCPopupMenu時。

但是m_Dlg.SendMessage(WM_NCPAINT, 1)是一個可疑的電話。 用法與WM_NCPAINT的文檔不匹配:

參數

wParam窗口更新區域的句柄。 更新區域被剪切到窗口框架。

lParam

不使用此參數。

此外, OnNcActivate具有基於IsWindowEnabled()的覆蓋。 這似乎是修復OnActivate早期問題的補丁。 但它會在其他地方引起問題,例如在CDialogEx使用CFileDialog

建議的解決方案:

修改CDialogEx::OnActivate ,使其運行默認過程。 或者,改變它以便強制重繪。

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();
}

要么

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!
    }
}

例如,這應該適用於CMFCPopupMenu MFC菜單將打開而不會取消激活對話框。

我不確定SendMessage(WM_FLOATSTATUS, FS_SYNCACTIVE)用於什么,我無法測試它...如果有必要,似乎可以在OnNcActivate上添加代碼,然后OnActivate保持OnNcActivate

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM