web-dev-qa-db-fra.com

Comment créer un CDialog redimensionnable dans MFC?

Je dois créer une application basée sur un dialogue, au lieu de l'ancien type de conception CFormView. Mais CDialog produit des boîtes de dialogue de taille fixe. Comment créer des applications basées sur des dialogues avec des dialogues redimensionnables?

19
Serkan

Dans le fichier de ressources RC, si le style de la boîte de dialogue est similaire à celui-ci, sa taille sera fixe:

IDD_DIALOG_DIALOG DIALOGEX 0, 0, 320, 201
STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU

Si la boîte de dialogue a ce style, elle sera importante:

IDD_DIALOG_DIALOG DIALOGEX 0, 0, 320, 201
STYLE WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME

Avec ces options de cadres dimensionnables, la boîte de dialogue sera redimensionnable, mais vous devrez néanmoins effectuer beaucoup de travail pour gérer le message WM_SIZE afin de gérer le dimensionnement et le positionnement des commandes dans la boîte de dialogue.

20
jussij

En plus de définir le style sur WS_THICKFRAME, vous souhaiterez probablement également disposer d'un système pour déplacer et redimensionner les contrôles dans une boîte de dialogue lorsque celle-ci est redimensionnée. Pour mon usage personnel, j'ai créé une classe de base pour remplacer CDialog qui offre cette possibilité. Dérivez de cette classe et, dans votre fonction InitDialog, appelez la fonction AutoMove pour chaque contrôle enfant afin de définir le déplacement et le redimensionnement par rapport au dialogue parent. La taille de la boîte de dialogue dans le fichier de ressources est utilisée comme taille minimale.

BaseDialog.h:

#if !defined(AFX_BASEDIALOG_H__DF4DE489_4474_4759_A14E_EB3FF0CDFBDA__INCLUDED_)
#define AFX_BASEDIALOG_H__DF4DE489_4474_4759_A14E_EB3FF0CDFBDA__INCLUDED_

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000

#include <vector>

class CBaseDialog : public CDialog
{
// Construction
public:
    CBaseDialog(UINT nIDTemplate, CWnd* pParent = NULL);   // standard constructor

    void AutoMove(int iID, double dXMovePct, double dYMovePct, double dXSizePct, double dYSizePct);

// Overrides
    // ClassWizard generated virtual function overrides
    //{{AFX_VIRTUAL(CBaseDialog)
protected:
    //}}AFX_VIRTUAL

protected:
    //{{AFX_MSG(CBaseDialog)
    virtual BOOL OnInitDialog();
    afx_msg void OnGetMinMaxInfo(MINMAXINFO FAR* lpMMI);
    afx_msg void OnSize(UINT nType, int cx, int cy);
    //}}AFX_MSG
    DECLARE_MESSAGE_MAP()

public:
    bool            m_bShowGripper;         // ignored if not WS_THICKFRAME

private:
    struct SMovingChild
    {
        HWND        m_hWnd;
        double      m_dXMoveFrac;
        double      m_dYMoveFrac;
        double      m_dXSizeFrac;
        double      m_dYSizeFrac;
        CRect       m_rcInitial;
    };
    typedef std::vector<SMovingChild>   MovingChildren;

    MovingChildren  m_MovingChildren;
    CSize           m_szInitial;
    CSize           m_szMinimum;
    HWND            m_hGripper;
};

//{{AFX_INSERT_LOCATION}}
// Microsoft Visual C++ will insert additional declarations immediately before the previous line.

#endif // !defined(AFX_BASEDIALOG_H__DF4DE489_4474_4759_A14E_EB3FF0CDFBDA__INCLUDED_)

BaseDialog.cpp:

#include "stdafx.h"
#include "BaseDialog.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

CBaseDialog::CBaseDialog(UINT nIDTemplate, CWnd* pParent /*=NULL*/)
    : CDialog(nIDTemplate, pParent),
      m_bShowGripper(true),
      m_szMinimum(0, 0),
      m_hGripper(NULL)
{
}


BEGIN_MESSAGE_MAP(CBaseDialog, CDialog)
    //{{AFX_MSG_MAP(CBaseDialog)
    ON_WM_GETMINMAXINFO()
    ON_WM_SIZE()
    //}}AFX_MSG_MAP
END_MESSAGE_MAP()

void CBaseDialog::AutoMove(int iID, double dXMovePct, double dYMovePct, double dXSizePct, double dYSizePct)
{
    ASSERT((dXMovePct + dXSizePct) <= 100.0);   // can't use more than 100% of the resize for the child
    ASSERT((dYMovePct + dYSizePct) <= 100.0);   // can't use more than 100% of the resize for the child
    SMovingChild s;
    GetDlgItem(iID, &s.m_hWnd);
    ASSERT(s.m_hWnd != NULL);
    s.m_dXMoveFrac = dXMovePct / 100.0;
    s.m_dYMoveFrac = dYMovePct / 100.0;
    s.m_dXSizeFrac = dXSizePct / 100.0;
    s.m_dYSizeFrac = dYSizePct / 100.0;
    ::GetWindowRect(s.m_hWnd, &s.m_rcInitial);
    ScreenToClient(s.m_rcInitial);
    m_MovingChildren.Push_back(s);
}

BOOL CBaseDialog::OnInitDialog()
{
    CDialog::OnInitDialog();

    // use the initial dialog size as the default minimum
    if ((m_szMinimum.cx == 0) && (m_szMinimum.cy == 0))
    {
        CRect rcWindow;
        GetWindowRect(rcWindow);
        m_szMinimum = rcWindow.Size();
    }

    // keep the initial size of the client area as a baseline for moving/sizing controls
    CRect rcClient;
    GetClientRect(rcClient);
    m_szInitial = rcClient.Size();

    // create a gripper in the bottom-right corner
    if (m_bShowGripper && ((GetStyle() & WS_THICKFRAME) != 0))
    {
        SMovingChild s;
        s.m_rcInitial.SetRect(-GetSystemMetrics(SM_CXVSCROLL), -GetSystemMetrics(SM_CYHSCROLL), 0, 0);
        s.m_rcInitial.OffsetRect(rcClient.BottomRight());
        m_hGripper = CreateWindow(_T("Scrollbar"), _T("size"), WS_CHILD | WS_VISIBLE | SBS_SIZEGRIP,
                                  s.m_rcInitial.left, s.m_rcInitial.top, s.m_rcInitial.Width(), s.m_rcInitial.Height(),
                                  m_hWnd, NULL, AfxGetInstanceHandle(), NULL);
        ASSERT(m_hGripper != NULL);
        if (m_hGripper != NULL)
        {
            s.m_hWnd = m_hGripper;
            s.m_dXMoveFrac = 1.0;
            s.m_dYMoveFrac = 1.0;
            s.m_dXSizeFrac = 0.0;
            s.m_dYSizeFrac = 0.0;
            m_MovingChildren.Push_back(s);

            // put the gripper first in the z-order so it paints first and doesn't obscure other controls
            ::SetWindowPos(m_hGripper, HWND_TOP, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOOWNERZORDER | SWP_NOSIZE | SWP_SHOWWINDOW);
        }
    }

    return TRUE;  // return TRUE  unless you set the focus to a control
}

void CBaseDialog::OnGetMinMaxInfo(MINMAXINFO FAR* lpMMI) 
{
    CDialog::OnGetMinMaxInfo(lpMMI);

    if (lpMMI->ptMinTrackSize.x < m_szMinimum.cx)
        lpMMI->ptMinTrackSize.x = m_szMinimum.cx;
    if (lpMMI->ptMinTrackSize.y < m_szMinimum.cy)
        lpMMI->ptMinTrackSize.y = m_szMinimum.cy;
}

void CBaseDialog::OnSize(UINT nType, int cx, int cy) 
{
    CDialog::OnSize(nType, cx, cy);

    int iXDelta = cx - m_szInitial.cx;
    int iYDelta = cy - m_szInitial.cy;
    HDWP hDefer = NULL;
    for (MovingChildren::iterator p = m_MovingChildren.begin();  p != m_MovingChildren.end();  ++p)
    {
        if (p->m_hWnd != NULL)
        {
            CRect rcNew(p->m_rcInitial);
            rcNew.OffsetRect(int(iXDelta * p->m_dXMoveFrac), int(iYDelta * p->m_dYMoveFrac));
            rcNew.right += int(iXDelta * p->m_dXSizeFrac);
            rcNew.bottom += int(iYDelta * p->m_dYSizeFrac);
            if (hDefer == NULL)
                hDefer = BeginDeferWindowPos(m_MovingChildren.size());
            UINT uFlags = SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOZORDER;
            if ((p->m_dXSizeFrac != 0.0) || (p->m_dYSizeFrac != 0.0))
                uFlags |= SWP_NOCOPYBITS;
            DeferWindowPos(hDefer, p->m_hWnd, NULL, rcNew.left, rcNew.top, rcNew.Width(), rcNew.Height(), uFlags);
        }
    }
    if (hDefer != NULL)
        EndDeferWindowPos(hDefer);

    if (m_hGripper != NULL)
        ::ShowWindow(m_hGripper, (nType == SIZE_MAXIMIZED) ? SW_HIDE : SW_SHOW);
}
21
Mark Ransom

Si vous utilisez un modèle de dialogue, ouvrez-le dans l’éditeur de ressources et définissez la propriété Style sur Popup et la propriété Border sur Redimensionnement . Je suis à peu près sûr que cela fera la même chose que ce que jussij dit et définit les styles WS_POPUP et WS_THICKFRAME. Pour les définir dynamiquement, remplacez la fonction PreCreateWindow et ajoutez ce qui suit:

cs.style |= WS_POPUP | WS_THICKFRAME;
3
Steiny

Il n'y a pas de moyen facile de faire cela. Fondamentalement, vous devrez disposer de manière dynamique les contrôles lorsque la taille de la fenêtre est modifiée.

Voir http://www.codeproject.com/KB/dialog/resizabledialog.aspx pour un exemple.

2
user22044

J'ai quelques instructions de blog sur la façon de créer un dialogue très minimaliste et redimensionnable dans MFC.

Il s’agit en fait d’une implémentation de de l’affichage de Paulo Messina dans CodeProject mais avec le plus possible d’éléments superflus supprimés, juste pour aider à clarifier la façon de mieux le faire.

Il est assez simple à mettre en œuvre une fois que vous avez eu un peu de pratique: les éléments importants sont les suivants:

je. Assurez-vous que ses bibliothèques CodeProject, etc., sont bien insérées dans votre projet et que tout est compilé correctement.

ii. effectuez l'initialisation supplémentaire requise dans la méthode OnInitDialog: rendez visible le préhenseur, définissez la taille maximale du dilog, ajoutez des points d'ancrage aux éléments de contrôle de la boîte de dialogue que vous souhaitez "étirer", etc.

iii. Remplacez l'utilisation de CDialog par CResizableDialog aux points appropriés: dans la définition de classe de dialogue, le constructeur, DoDataExchange, BEGIN_MESSAGE_MAP, OnInitDialog, etc.

0
AndyUK

J'ai essayé de nombreuses bibliothèques de structures MFC et j'ai trouvé celle-ci la meilleure: http://www.codeproject.com/KB/dialog/layoutmgr.aspx . Consultez les commentaires pour des corrections de bugs et des améliorations (disclaimer: certains d’entre eux par moi;)). Lorsque vous utilisez cette bibliothèque, la définition des indicateurs de redimensionnement corrects sur votre fenêtre sera gérée pour vous.

0
Roel

Depuis Visual Studio 2015, vous pouvez utiliser MFC Dynamic Dialog Layout , mais il semble qu’il n’existe aucun moyen de limiter la taille de la boîte de dialogue à une taille minimale (toujours à l’ancienne méthode de traitementWM_GETMINMAXINFO).

La mise en page dynamique peut être faite:

  • au moment de la conception dans l’éditeur de ressources, en sélectionnant le contrôle et en définissant les propriétés type de déplacement _ et type de dimensionnement} __ (ceci émet une nouvelle section AFX_DIALOG_LAYOUT dans un fichier .rc);
  • ou par programme en utilisant le CMFCDynamicLayoutclass .

Documentation: Disposition dynamique

0
tibx