Question

Dans mon application MFC (Feature Pack), on peut créer dynamiquement des vitres d'accueil pour afficher des graphiques / tableaux etc.
Cependant, je ne veux pas laisser l'utilisateur ouvrir la même chose deux fois.

Je crée une fenêtre comme ceci:

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

Cela semble fonctionner très bien.

Voici comment j'ai essayé de vérifier si un volet existe déjà. Un volet est identifié par son type (classe) et un paramètre.

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

Le problème est que lorsque je ferme une fenêtre, ce n'est pas reconnu. L'objet CDockingManager renvoie toujours le volet dans l'appel GetPanes ().

Comment puis-je dire au gestionnaire de ne pas retourner les vitres qui sont fermés?
ou Comment puis-je supprimer le volet d'une liste de fenêtre, quand il est fermé?


Mise à jour

Je plongeais un peu plus profond et a trouvé que les objets CWnd ne sont pas réellement fermés, en cliquant sur le bouton « x » dans la barre de titre, mais seulement leurs conteneurs.
Donc, le vrai problème semble être vraiment fermer les vitres.
J'ai aussi changé la question afin de mieux refléter le problème.

Était-ce utile?

La solution

Comme décrit dans ma mise à jour, le problème pour le gestionnaire d'accueil pour me donner des vitres fermées, était que les vitres ne sont pas réellement fermées. Seuls leurs conteneurs ont été fermés; les vitres elles-mêmes étaient tout simplement cachés.

Donc, pour vraiment fermer les volets, je l'emportaient sur les méthodes suivantes dans ma classe principale CMDIFrameWndEx dérivées d'images:

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
}

Et le second:

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
}

Autres conseils

ajouter à yor CMainFram une entrée msg comme suit:

ON_REGISTERED_MESSAGE(AFX_WM_ON_PRESS_CLOSE_BUTTON,OnClosePane)

OnClosePane ressembler à ceci:

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
}

COUTION:

OnClosePane est appelé au milieu de CBasePane :: gestionnaire OnLButtonDown, fenêtre détruire fera votre assert de code, vous devez poster un message (WM_CLOSE) au lieu de l'envoyer, cela donne CBasePane :: gestionnaire OnLButtonDown une chance de terminer l'exécution tandis que le volet hWnd toujours valide. et pour la même resone je retourne vrai pour éviter près parce que nous fermons déjà via WM_CLOSE qui détruira la fenêtre ainsi.

WM_RESETMEMBER message est enregistré un message de fenêtre pour réinitialiser élément de volet à null.

regard de la mise en œuvre comme ceci:

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

vous devriez msg entrée comme une carte:

ON_REGISTERED_MESSAGE(WM_RESETMEMBER,OnResetMember)

et vous devez enregistrer un message globalement comme ceci:

const UINT WM_RESETMEMBER = ::RegisterWindowMessage(_T("WM_RESETMEMBER"));

J'attends un appel à CDockingManager :: RemovePaneFromDockManager lorsque vous fermez votre fenêtre pour faire le travail.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top