Панель заголовка приложения на основе диалогового окна MFC, выделяющая визуальные артефакты в Windows 10 (например, ошибки в CDialogEx) - PullRequest
0 голосов
/ 13 декабря 2018

Я не уверен, почему я получаю этот визуальный артефакт?

Вот как воспроизвести:

Я использую сообщество Visual Studio 2017.Создайте новый проект C ++ -> MFC:

enter image description here

Затем укажите «на основе диалога»:

enter image description here

Затем создайте приложение «Отладка» x86 и запустите его.

Итак, я запускаю его на Windows 10.

Когда этот процесс на основе диалога имеет фокусвыглядит так, как я и ожидал:

enter image description here

, но если я переключаю фокус клавиатуры на какое-то другое приложение (нажав на него), это диалоговое окнооснованный процесс все еще сохраняет свой цвет строки заголовка:

enter image description here

Я не уверен, является ли это просто визуальным затруднением или есть более глубокий беспорядокс обработкой сообщений окна.Как мне это исправить?(Это не было проблемой со старыми проектами MFC.)

Ответы [ 3 ]

0 голосов
/ 15 декабря 2018

Хорошо, насколько я ценю решение @ VuVirt, оно не полностью удаляет все ошибки, которые поставляются в стандартном решении на основе Dialog в VS2017.Это решает проблему фокуса заголовка, но, продолжая развивать мой проект, я столкнулся с еще одной ошибкой.Поэтому я копирую и вставляю это из своего комментария к его ответу:

Там все еще есть какая-то ошибка.Я не уверен, связано ли это с этим исправлением или нет.Пример: если вы создаете кнопку, а затем в ее обработчике попробуйте сделать: CFileDialog d (TRUE, NULL, NULL, OFN_HIDEREADONLY | OFN_EXPLORER, NULL, this);d.DoModal ();открыть диалог выбора файлов.Когда средство выбора файлов откроется, закройте его и посмотрите, вернется ли активная строка заголовка родительского диалогового окна MFC.В моем случае он остается неактивным до тех пор, пока я не нажму на панель задач Windows, а затем вернусь в это приложение MFC.

После того, как я ударился головой о стену, пытаясь понять, что там происходит, я решил попробовать более раннее решение, предложенное @ zett42 в комментариях к моему первоначальному вопросу (т.е. заменить CDialogEx на CDialog) и это сработало!Все ошибки исчезли!

Итак, вот мой вердикт: CDialogEx содержит ошибку.

Разрешение довольно простое: При создании нового диалогового окнаВ проекте используйте поиск и замену всего проекта (в меню «Правка») и замените все вхождения CDialogEx на CDialog.И это все.(Я пытался использовать инструмент рефакторинга VS2017 для этого, но он испортил его и не заменил все. Так что простой поиск и замена делает свою работу.)

И если вы думаете, что вы будетепропустите некоторые функции без CDialogEx, тогда вы не будете.Все, что он делает (помимо внесения ошибок), это добавляет фоновые изображения и цвета в диалог.

Так что, пока MS не исправит эти явные ошибки в своих шаблонах, я придерживаюсь этого подхода.

0 голосов
/ 15 декабря 2018

Это, похоже, ошибка в CDialogImpl::OnActivate и 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);
  }
}

Это означает, что CDialogEx может оставаться активным, например,когда отображается CMFCPopupMenu.

Но m_Dlg.SendMessage(WM_NCPAINT, 1) является подозрительным вызовом.Использование не соответствует документации для WM_NCPAINT:

Параметры

wParam Дескриптор области обновления окна.Область обновления ограничена рамкой окна.

lParam

Этот параметр не используется.

Кроме того, OnNcActivate имеет переопределение на основеIsWindowEnabled().Похоже, это исправление для исправления более ранней проблемы в OnActivate.Но это вызывает проблемы в других местах, например, при использовании CFileDialog в CDialogEx

Предлагаемое решение:

Измените 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остается один.

0 голосов
/ 13 декабря 2018

Мне удалось воспроизвести вашу проблему и найти быстрое решение для нее.Вам необходимо добавить обработчик сообщений 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 вызов необходим для сохранения активной / неактивной визуализациикнопка по умолчанию.

...