Pregunta

Tengo que crear una aplicación basada en cuadros de diálogo, en lugar del tipo de diseño antiguo de CFormView. Pero CDialog produce diálogos de tamaño fijo. ¿Cómo puedo crear aplicaciones basadas en diálogos con diálogos redimensionables?

¿Fue útil?

Solución

En el archivo de recursos RC, si el cuadro de diálogo tiene un estilo similar a este, tendrá un tamaño fijo:

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

Si el cuadro de diálogo tiene este estilo, será considerable:

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

Con estas opciones de marco de tamaño considerable, el diálogo será redimensionable, pero aún tendrá que trabajar mucho para manejar el mensaje WM_SIZE para administrar el tamaño y la posición de los controles dentro del diálogo.

Otros consejos

Además de configurar el estilo en WS_THICKFRAME , es probable que también desee tener un sistema para mover y cambiar el tamaño de los controles en un cuadro de diálogo mientras se cambia el tamaño del cuadro de diálogo. Para mi uso personal, he creado una clase base para reemplazar CDialog que tiene esta capacidad. Derive de esta clase y en su función InitDialog llame a la función AutoMove para cada control secundario para definir cuánto debe moverse y cuánto debe cambiar el tamaño en relación con el diálogo principal. El tamaño del diálogo en el archivo de recursos se utiliza como tamaño mínimo.

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

Si está utilizando una plantilla de diálogo, abra la plantilla de diálogo en el editor de recursos y configure la propiedad Estilo en Ventana emergente y la propiedad Borde para cambiar el tamaño . Estoy bastante seguro de que esto hará lo mismo que dijo jussij y establecerá los estilos WS_POPUP y WS_THICKFRAME. Para configurar estos dinámicamente, anule la función PreCreateWindow y agregue lo siguiente:

cs.style |= WS_POPUP | WS_THICKFRAME;

No hay una manera fácil de hacer esto. Básicamente, tendrá que diseñar dinámicamente los controles cuando se cambie el tamaño de la ventana.

Consulte http://www.codeproject.com/KB/dialog/resizabledialog. aspx para un ejemplo

Desde Visual Studio 2015 , puede usar MFC Dynamic Dialog Layout , pero parece que no hay forma de restringir el tamaño del diálogo a un tamaño mínimo (aún de la forma anterior por manejo WM_GETMINMAXINFO ).

El diseño dinámico se puede hacer:

  • en el momento del diseño en el editor de recursos seleccionando el control y configurando las propiedades Moving Type y Sizing Type (esto emite nuevas AFX_DIALOG_LAYOUT en archivo .rc);
  • o programáticamente usando CMFCDynamicLayout class .

Documentación: Dynamic Layout

He probado muchas bibliotecas de diseño MFC y encontré que esta es la mejor: http: //www.codeproject.com/KB/dialog/layoutmgr.aspx . Echa un vistazo a los comentarios para ver algunas correcciones de errores y mejoras (descargo de responsabilidad: algunos de ellos por mí;)). Cuando use esta biblioteca, la gestión de los indicadores de tamaño correcto en su ventana se manejará por usted.

Tengo algunas instrucciones del blog sobre cómo crear un cuadro de diálogo de tamaño muy minimalista en MFC.

Básicamente, es una implementación de la publicación de Paulo Messina en CodeProject pero con tantas cosas extrañas eliminadas como sea posible, solo para ayudar a aclarar cómo hacerlo mejor.

Es bastante sencillo de implementar una vez que hayas tenido un poco de práctica: los bits importantes son:

i. asegúrese de tener sus bibliotecas de CodeProject, etc. insertadas en su proyecto y todo se compila correctamente.

ii. realice la inicialización adicional requerida dentro del método OnInitDialog: haga visible la pinza, establezca el tamaño máximo de registro, agregue puntos de anclaje a los elementos de control de diálogo que desea "estirar", etc.

iii. Reemplace el uso de CDialog con CResizableDialog en los puntos apropiados: en la definición de la clase de diálogo, el constructor, DoDataExchange, BEGIN_MESSAGE_MAP, OnInitDialog etc.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top