Pergunta

Eu tenho que criar um aplicativo de diálogo com base, em vez de velho tipo CFormView de design. Mas CDialog produz diálogos de tamanho fixo. Como posso criar diálogo aplicações baseadas com diálogos redimensionáveis?

Foi útil?

Solução

No arquivo de recurso RC se o diálogo tem este estilo semelhante a este que será fixado size:

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

Se o diálogo tem esse estilo será considerável:

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

Com estas opções de moldura considerável de diálogo será re-considerável, mas você ainda precisa fazer um monte de trabalho lidar com o WM_SIZE mensagem para gerir o dimensionamento de um posicionamento dos controles na caixa de diálogo.

Outras dicas

Além de definir o estilo para WS_THICKFRAME, você provavelmente também quer ter um sistema para mover e redimensionar os controles em uma caixa de diálogo como o diálogo é redimensionado. Para meu uso pessoal Eu criei uma classe base para substituir CDialog que tem essa capacidade. Derivar dessa classe e em sua chamada de função InitDialog a função AutoMove para cada controle filho para definir o quanto ele deve se mover e quanto ele deve redimensionar em relação ao diálogo pai. O tamanho da caixa de diálogo no arquivo de recurso é usado como um tamanho 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);
}

Se o seu usando um modelo de diálogo, em seguida, abra o modelo de diálogo no editor de recursos e definir o Estilo para Popup e Border propriedade para redimensionamento . Eu tenho certeza que isso vai fazer o mesmo que jussij disse e definir os estilos WS_POPUP e WS_THICKFRAME. Para definir estes dinamicamente, em seguida, substituir a função PreCreateWindow e adicione a seguinte:

cs.style |= WS_POPUP | WS_THICKFRAME;

Não há nenhuma maneira fácil de fazer isso. Basicamente, você precisará controles dinamicamente layout quando o tamanho da janela é alterado.

Consulte http://www.codeproject.com/KB/dialog/resizabledialog. aspx para um exemplo

Desde Visual Studio 2015 , você pode usar MFC dinâmico diálogo layout , mas ao que parece, não há nenhuma maneira para restringir o tamanho de diálogo ao tamanho mínimo (ainda só a velha maneira de manipulação WM_GETMINMAXINFO ).

Layout dinâmico pode ser feito:

  • em tempo de design no editor de recursos, selecionando o controle e definir o Tipo Mover e Dimensionamento Tipo Propriedades (Emite novo AFX_DIALOG_LAYOUT seção em arquivo.rc);
  • ou programaticamente usando o CMFCDynamicLayout classe .

Documentação: layout dinâmico

Eu tentei muitas bibliotecas de layout MFC e acharam este o melhor: http: //www.codeproject.com/KB/dialog/layoutmgr.aspx . Confira os comentários lá por algumas correções de bugs e melhorias (Aviso: alguns deles por mim;)). Quando você usar esta biblioteca, definindo as bandeiras de redimensionamento corretos em sua janela será feita para você.

Eu tenho algumas instruções de blog sobre como criar um diálogo de re-considerável muito minimalista em MFC.

É basicamente uma implementação de postagem Paulo de Messina em CodeProject mas com tanta coisa estranha removido possível, apenas para ajudar a esclarecer como fazê-lo melhor.

É bastante simples de implementar, uma vez que você teve um pouco de prática: os bits importantes são:

i. garantir que você tenha o seu CodeProject bibliotecas etc puxado para o seu projeto e tudo compila corretamente.

ii. fazer a inicialização extra necessária dentro do método OnInitDialog:. tornar a garra visível, definir o tamanho máximo dilog, adicionar pontos de ancoragem para os itens de controle de diálogo que pretende 'esticar' etc

iii. Substituir o uso de CDialog com CResizableDialog nos pontos apropriados: na definição da classe de diálogo, construtor, DoDataExchange, BEGIN_MESSAGE_MAP, OnInitDialog etc.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top