كيفية إنشاء CDialog يمكن تغيير حجمه في MFC؟

StackOverflow https://stackoverflow.com/questions/138040

  •  02-07-2019
  •  | 
  •  

سؤال

لا بد لي من إنشاء تطبيق يعتمد على الحوار، بدلاً من نوع التصميم القديم CFormView.لكن CDialog ينتج مربعات حوار ذات حجم ثابت.كيف يمكنني إنشاء تطبيقات تعتمد على الحوار باستخدام مربعات حوار يمكن تغيير حجمها؟

هل كانت مفيدة؟

المحلول

في ملف مورد RC، إذا كان مربع الحوار يحتوي على هذا النمط المشابه لهذا، فسيتم تثبيت الحجم:

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

إذا كان مربع الحوار بهذا النمط فسيكون كبيرًا:

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

باستخدام خيارات الإطار الكبيرة هذه، سيتم تغيير حجم مربع الحوار ولكنك ستظل بحاجة إلى القيام بالكثير من العمل للتعامل مع WM_SIZE رسالة لإدارة تغيير الحجم وتحديد موضع عناصر التحكم داخل مربع الحوار.

نصائح أخرى

بالإضافة إلى تحديد النمط ل WS_THICKFRAME, ، ربما تريد أيضًا أن يكون لديك نظام لنقل عناصر التحكم وتغيير حجمها في مربع حوار أثناء تغيير حجم مربع الحوار.لاستخدامي الشخصي، قمت بإنشاء فئة أساسية لتحل محل CDialog الذي يتمتع بهذه الإمكانية.تستمد من هذا الفصل وفي الخاص بك InitDialog وظيفة استدعاء AutoMove وظيفة لكل عنصر تحكم تابع لتحديد المقدار الذي يجب أن يتحرك فيه والمقدار الذي يجب تغيير حجمه بالنسبة لمربع الحوار الأصلي.يتم استخدام حجم مربع الحوار في ملف المورد كحد أدنى للحجم.

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

إذا كنت تستخدم قالب حوار، فافتح قالب الحوار في محرر الموارد وقم بتعيين أسلوب الملكية ل يظهر فجأة و ال حدود الملكية ل تغيير الحجم.أنا متأكد من أن هذا سيفعل نفس ما jussij قال وقم بتعيين أنماط WS_POPUP وWS_THICKFRAME.لتعيين هذه العناصر ديناميكيًا، قم بتجاوز وظيفة PreCreateWindow وأضف ما يلي:

cs.style |= WS_POPUP | WS_THICKFRAME;

لا يوجد طريقه سهله لفعل ذلك.في الأساس، ستحتاج إلى تخطيط عناصر التحكم ديناميكيًا عند تغيير حجم النافذة.

يرى http://www.codeproject.com/KB/dialog/resizabledialog.aspx على سبيل المثال

منذ فيجوال ستوديو 2015, ، يمكنك استخدام تخطيط الحوار الديناميكي MFC, ، ولكن يبدو أنه لا توجد طريقة لتقييد حجم الحوار بالحجم الأدنى (لا تزال الطريقة القديمة فقط معالجة WM_GETMINMAXINFO).

يمكن إجراء التخطيط الديناميكي:

  • في وقت التصميم في محرر الموارد عن طريق تحديد عنصر التحكم وتعيين نوع متحرك و نوع التحجيم الخصائص (وهذا ينبعث منها ملفات جديدة AFX_DIALOG_LAYOUT قسم إلى ملف .rc)؛
  • أو برمجياً باستخدام CMFCDynamicLayout فصل.

توثيق: التخطيط الديناميكي

لقد قمت بتجربة العديد من مكتبات تخطيط MFC ووجدت أن هذه هي الأفضل: http://www.codeproject.com/KB/dialog/layoutmgr.aspx.تحقق من التعليقات هناك للحصول على بعض إصلاحات الأخطاء والتحسينات (إخلاء المسؤولية:وبعضها بقلمي ;)).عند استخدام هذه المكتبة، سيتم التعامل مع ضبط علامات تغيير الحجم الصحيحة على نافذتك نيابةً عنك.

لدي بعض تعليمات المدونة حول كيفية إنشاء مربع حوار بسيط جدًا يمكن تغيير حجمه في MFC.

هو في الأساس تنفيذ منشور باولو ميسينا في CodeProjectولكن مع إزالة أكبر قدر ممكن من العناصر الدخيلة، فقط للمساعدة في توضيح كيفية القيام بذلك بشكل أفضل.

إنه أمر سهل التنفيذ إلى حد ما بمجرد ممارسة القليل من التدريب:البتات المهمة هي:

أنا.تأكد من أن مكتبات CodeProject الخاصة به وما إلى ذلك قد تم سحبها إلى مشروعك ويتم تجميعها جميعًا بشكل صحيح.

ثانيا.قم بإجراء التهيئة الإضافية المطلوبة داخل طريقة OnInitDialog:اجعل المقبض مرئيًا، واضبط الحد الأقصى لحجم الحوار، وأضف نقاط ربط إلى عناصر التحكم في مربع الحوار التي ترغب في "تمديدها" وما إلى ذلك.

ثالثا.استبدل استخدام CDialog بـ CResizableDialog في النقاط المناسبة:في تعريف فئة الحوار، المُنشئ، DoDataExchange، BEGIN_MESSAGE_MAP، OnInitDialog وما إلى ذلك.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top