본문 바로가기

3.구현/VC++

[MFC]Resizing Dialog

작성자: Ospace (ospace114 at naver.com) http://discount77.com/blog/ospace

다이얼로그 형태의 MFC에서 매우 유용하게 사용할 수 있는 코드이다.

다운로드:

EasySize.h
6.3 kB

사용법:

  1. stdafx.h파일에 #include EasySize.h 을 추가.
  2. 다이얼로그 클래스 정의한 부분에 DECLARE_EASYSIZE 넣음
    class CEasySizeDemoDlg : public CDialog {
        DECLARE_EASYSIZE
        ...
  3. 해당 다이얼로그 클래스의 OnInitDialog()에 INIT_EASYSIZE;을 넣음
    BOOL CEasySizeDemoDlg::OnInitDialog() {
        CDialog::OnInitDialog();
        ...
        INIT_EASYSIZE;
        return TRUE;
        // return TRUE unless you set the focus to a control
    }
  4. 해당 클래스 OnSize() 핸들러를 다음과 같이 수정
    void CEasySizeDemoDlg::OnSize(UINT nType, int cx, int cy) {
        CDialog::OnSize(nType, cx, cy);
        UPDATE_EASYSIZE;
    }
  5. 해당 클래스 OnSizing() 핸들러를 다음과 같이 수정위의 280과 250은 창의 폭과 높이를 말한다.
    void CEasySizeDemoDlg::OnSizing(UINT fwSide, LPRECT pRect) {
        CDialog::OnSizing(fwSide, pRect);
        EASYSIZE_MINSIZE(280,250,fwSide,pRect);
    }
  6. 위의 같은 파일 앞부분에 다음 코드를 추가
    BEGIN_EASYSIZE_MAP(class_name)
      ...
      EASYSIZE(control,left,top,right,bottom,options)
      ...
    END_EASYSIZE_MAP`

실제 사용예는 다음과 같다.

  //}}AFX_MSG_MAP
END_MESSAGE_MAP()
BEGIN_EASYSIZE_MAP(CEasySizeDemoDlg)
  EASYSIZE(IDC_TITLE,ES_BORDER,ES_BORDER,
       ES_BORDER,ES_KEEPSIZE,ES_HCENTER)
  EASYSIZE(IDC_RADIO1,ES_BORDER,ES_BORDER,
       ES_KEEPSIZE,ES_KEEPSIZE,0)
  EASYSIZE(IDC_STATUSFRAME,ES_BORDER,ES_KEEPSIZE,
       ES_BORDER,ES_BORDER,0)
END_EASYSIZE_MAP

[EASYSIZE() 매크로 용법]


EASYSIZE(control,left,top,right,bottom,options)

control is the ID of the dialog item you want re-positioned (which will be referred to as the 'current control' further on).

left, top, right and bottom can be either the ID of another control in the dialog (not the current control), or one of the special values, ES_BORDER and ES_KEEPSIZE.

Basically, if you specify an ID, the distance from the current control and the item designated by the ID will remain the same when the dialog is resized: The current control will 'stick' to the other item. ES_BORDER works the same way as if you had specified a control ID, except that it's the distance between the current control and the dialog border that will be kept constant. Specifying ES_KEEPSIZE in, let's say left, will keep the width of the current control the same, and will make the current control right-aligned to whatever you specified in right. The width (or height, if you specified ES_KEEPSIZE in top or bottom) of the current control will always remain what it is in the dialog resource. (I know this explanation sucks, but look at the demo app if you are confused or post you question in the board below). Obviously ES_KEEPSIZE cannot be specified in both "left and right" or "top and bottom".

ospace 덧글:
ES_KEPSIZE는 말그대로 크기를 고정 시키는 것이다. top과 bottom, 그리고 left와 right는 같이 지정할 필요가 없다고 하는데, 예제를 보면 같이 사용하는 곳도 있고 있어서 말이 맞지 않는다.
코드를 분석해본 결과로는 약간 이상한 부분이 있다.
ES_BORDER는 해당하는 방향의 보더에 기준해서 이동하는 것이다. 즉 보더와 일정한 간격을 유지한체 이동하는 것이다.
그왜 컨트롤ID가 오는 경우 해당 컨트롤과 일정한 간격을 유지하는 것인데, 다른 옵션에 따라서 달라질 수 있다.
ES_HCENTER를 적용하면 가로 방향 다른 값들은 무시된다고 보면 된다. ES_VCENTER는 마찬가지 이다. 이때 ES_BORDER 값은 의미가 없다고 보면 된다.
주의 할 것은 옵션의 조합이 서로 맞아야 한다. 이는 직접해보면서 파악해보라.

options can be a combination of ES_HCENTER, ES_VCENTER and 0 (use 0 if you don't want any of the other). ES_HCENTER horizontally centers the control between the two items specified in left and right (both of those can not be ES_KEEPSIZE!). The width of the current control will always remain the same as in the dialog resource. ES_VCENTER works the same way, but for vertical centering (using top and bottom, and where the height will remain constant).

덧글 : FormView에서 사용에는 문제가 있다.
FormView에서는 CMainFame 부분에 위의 코드를 넣어야 한다. 그러나 5번 항목까지는 잘 작동하나. 6번 EASYSIZE 매크로 동작에서 문제가 발생한다.
원인을 찾아본 결과 MainFrame에서는 OnPaint()가 지원하지 않아서 발생하는 문제라고 하는데 정확히 이해가 안된다.
다음에 이에대한 해결책이라고 했는데 설명이 부족하다.

FormView Solution AliRafiee 17:07 17 Aug '05
I found a solution for the form view problem (and it's working great for me). The problem is that when OnInitalUpdate gets called the form (eventhough created) has a width and height of zero. So what I did was put an if statment in the OnSize where the first time around I call INIT_EASYSIZE, and from there on I call UPDATE_EASYSIZE. Also you have to call SetScrollSizes(MM_TEXT,0,0) in order to get rid of the scrollbars.

void CMyView::OnSize(UINT nType, int cx, int cy)
{
    CFormView::OnSize(nType, cx, cy);
    if (m_bFirst && IsWindowVisible()&& GetWindow(GW_CHILD)!=NULL)
    {
        INIT_EASYSIZE;
        m_bFirst = FALSE;
        SetScrollSizes(MM_TEXT,CSize(0,0));
        UPDATE_EASYSIZE;
    }
    else if (!m_bFirst)
    {
        SetScrollSizes(MM_TEXT,CSize(0,0));
        UPDATE_EASYSIZE;
    }
}

Ospace 덧글 :
FormView의 해결책으로 올려놓은 것은 각 컨트롤의 초기값을 언제 시작하는 가에 대한 문제인데.. m_bFirst가 FALSE인 초기에는 UPDATE_EASYSIZE 매크로가 실행되는 문제가 생긴다.
INIT_EASYSIZE 매크로를 왼도우가 다 만들어진 후에 한번만 호출하면 되는데, 그게 쉽지가 않다.
위의 해결책도 때에 따라서는 아직 컨트롤의 초기값이 없는 상태에서 MoveWindow()가 실행되므로 초기화되지 않은 값으로 MoveWindow()가 실행되어 오류가 발생하기도 한다.
불완전한 방법이지만 OnPaint()을 이용한 방법을 소개하겠다.
OnPaint()가 초기화 작업에서 ShowWindow(), UpdateWindow()가 뜨고 마지막에 실행되는 부분으로 가장 적합한 곳이라 생각된다.
단지 많이 호출되는 핸들러로서 처음 비교 구문 때문에 부하가 예상되나 단순 비교로 많은 부하를 차지하지 않을 것으로 본다.

void CMyView::OnPaint()
{
    if(!m_bInitDone) { 
        ChangeSize(TRUE);
        m_bInitDone = TRUE;
        TRACE0("InitDone\n");
    }
    ...
}

물론 위의 예제를 바로 FormView에 적용할 수 없다. 약간의 변경이 필요하다.

참조

[1] http://www.codeproject.com/dialog/easysize.asp

반응형