Как закрыть динамически созданные окна CDockablePane?

В моем приложении MFC (Feature Pack) можно динамически создавать стыковочные панели для отображения диаграмм/таблиц и т. д.
Однако я не хочу, чтобы пользователь открывал одно и то же дважды.

Я создаю панель следующим образом:

// Create CMyDockablePane pPane
pPane->Create(...);
pPane->EnableDocking(CBRS_ALIGN_ANY);
// Create CRect rcPane
pPane->FloatPane(rcPane);

Кажется, это работает нормально.

Вот как я пытался проверить, существует ли уже панель. Панель идентифицируется по ее типу (классу) и параметру.

BOOL CanOpenPane(const type_info & paneType, const CMyParameter & parameter) const
{
    CMainFrame* pFrm = GetMainFrame();
    CDockingManager* pDockMan = pFrm->GetDockingManager();


    // Check if there already is a pane of the same type which also has the same parameter.
    bool canOpen = true;
    CObList panes;
    pDockMan->GetPaneList(panes);
    POSITION pos = panes.GetHeadPosition();
    while (pos)
    {
        CMyDockablePane* pPane = dynamic_cast<CMyDockablePane*>(panes.GetNext(pos));
        if (NULL == pPane) { continue; }

        if (paneType == typeid(*pPane) &&
                pPane->GetParameter() == parameter)
        {
            canOpen = false;
            break;
        }
    }


    return canOpen;
}

Проблема в том, что когда я закрываю панель, это не распознается. Объект CDockingManager по-прежнему возвращает панель в вызове GetPanes().

Как указать менеджеру не возвращать закрытые панели?
или
Как удалить панель из списка панелей, когда она закрыта?


Обновлять

Я копнул немного глубже и обнаружил, что объекты CWnd на самом деле не закрываются при нажатии кнопки «x» в строке заголовка, а только их контейнеры.
Таким образом, реальная проблема, похоже, заключается в том, чтобы действительно закрыть панели. .
Я также изменил вопрос, чтобы лучше отразить проблему.


person foraidt    schedule 26.08.2009    source источник
comment
На всякий случай, если вы не знаете, MFC — это зло. Используйте другой инструментарий GUI, если можете. Если не получится, да прибудет с вами сила.   -  person the_drow    schedule 26.08.2009


Ответы (4)


Как описано в моем обновлении, проблема с менеджером стыковки, который давал мне закрытые панели, заключалась в том, что панели на самом деле не были закрыты. Только их контейнеры были закрыты; сами панели были просто скрыты.

Поэтому, чтобы действительно закрыть панели, я переопределил следующие методы в моем производном от CMDIFrameWndEx классе основного фрейма:

BOOL CMainFrame::OnCloseMiniFrame(CPaneFrameWnd* pWnd)
{
    if(0 == pWnd->GetPaneCount()) { return TRUE; } // No panes.. allow closing

    // Close all child panes of the miniframe that is about to be closed.
    //
    // Panes are placed inside a mini frame when they have the "floating" status.
    // Since I didn't find a way to iterate over the panes of a mini frame
    // (CMultiPaneFrameWnd can have several panes), we iterate over all panes
    // and close those whose parent frame is pWnd.

    CDockingManager* pDockMan = GetDockingManager();
    if(NULL != pDockMan)
    {
        CObList allPanes;
        pDockMan->GetPaneList(allPanes, TRUE, NULL, TRUE);

        for(POSITION pos = allPanes.GetHeadPosition(); pos != NULL;)
        {
            CDockablePane* pPane = dynamic_cast<CDockablePane*>(allPanes.GetNext(pos));
            if (NULL == pPane) { continue; }

            if(pWnd == pPane->GetParentMiniFrame())
            {
                pPane->PostMessage(WM_CLOSE); // Note: Post instead of Send
            }
        }

    }

    return TRUE; // Allow closing
}

И второе:

BOOL CMainFrame::OnCloseDockingPane(CDockablePane* pWnd)
{
    CObList paneList;

    // We can get CDockablePanes and CTabbedPanes here.
    // The tabbed panes contain dockable panes.
    CTabbedPane* pTabbed = dynamic_cast<CTabbedPane*>(pWnd);
    CDockablePane* pDockable = dynamic_cast<CDockablePane*>(pWnd);
    if(NULL != pTabbed)
    {
        pTabbed->GetPaneList(paneList);
    }
    else if(NULL != pDockable)
    {
        paneList.InsertAfter(paneList.GetHeadPosition(), pDockable);
    }

    // Whatever it was, we now have a list of dockable panes, which we will close.
    for(POSITION pos = paneList.GetHeadPosition(); NULL != pos;)
    {
        CDockablePane* pPane = dynamic_cast<CDockablePane*>(paneList.GetNext(pos));
        ASSERT(NULL != pPane);


        // Let the window disappear and then recalculate the layout.
        // Not doing this causes problems with panes grouped together in a tabbed pane.
        pPane->ShowWindow(SW_HIDE);
        RecalcLayout();

        // Really close the window so the docking manager also doesn't know of it anymore.
        pPane->Reset();
        pPane->PostMessage(WM_CLOSE); // Note: Post instead of Send
    }


    return TRUE; // Allow closing
}
person foraidt    schedule 26.08.2009

добавьте в свой CMainFram запись msg, подобную следующей:

ON_REGISTERED_MESSAGE(AFX_WM_ON_PRESS_CLOSE_BUTTON,OnClosePane)

OnClosePane выглядит так:

LRESULT CMainFrame::OnClosePane(WPARAM,LPARAM lp)
{
    CBasePane* pane = (CBasePane*)lp;
    int id = pane->GetDlgCtrlID();
    pane->ShowPane(FALSE, FALSE, FALSE);
    RemovePaneFromDockManager(pane,TRUE,TRUE,TRUE,NULL);
    AdjustDockingLayout();
    pane->PostMessage(WM_CLOSE);
    PostMessage(WM_RESETMEMBER,id,0);
    return (LRESULT)TRUE;//prevent close , we already close it
}

ВНИМАНИЕ:

OnClosePane вызывается в середине обработчика CBasePane::OnLButtonDown, уничтожение окна заставит ваш код утверждать, поэтому вам нужно опубликовать сообщение (WM_CLOSE) вместо его отправки, это даст обработчику CBasePane::OnLButtonDown возможность завершить выполнение, пока панель hWnd все еще действует . и по той же причине я возвращаю True, чтобы предотвратить закрытие, потому что мы уже закрываем его с помощью WM_CLOSE, что также уничтожит окно.

Сообщение WM_RESETMEMBER — это зарегистрированное оконное сообщение для сброса члена панели в значение null .

его реализация выглядит так:

LRESULT CMainFrame::OnResetMember(WPARAM wp,LPARAM)
{
    int id = (int)wp;
    switch(id)
    {
        case IDC_BIDBOND_TREE_PANE:
            m_pBBTreePane.reset((BBTreePane*)NULL);
            break;
        case IDC_REFTREE_PANE :
            m_pRefTreePane.reset((RefTreePane*)NULL);
            break;
        default :
            return (LRESULT)FALSE;//id warent found

    }
    return (LRESULT)TRUE;
}

вы должны ввести запись карты msg, например:

ON_REGISTERED_MESSAGE(WM_RESETMEMBER,OnResetMember)

и вы должны зарегистрировать сообщение глобально следующим образом:

const UINT WM_RESETMEMBER = ::RegisterWindowMessage(_T("WM_RESETMEMBER"));
person Huzifa Terkawi    schedule 04.11.2012

Я ожидаю вызова CDockingManager::RemovePaneFromDockManager, когда вы закрытие панели, чтобы выполнить задание.

person Steve Beedie    schedule 26.08.2009

в документе mfc сказано, что не используйте showwindow, поэтому используйте showpane, чтобы показать панель

person feiduoge    schedule 17.03.2020