깔끔한 분할 윈도우 만들기

 여기서 참고한 자료는 마이크로소프트웨어 97년 12월호와 99년 6월호입니다.

 

 스플릿을 많이 나누는 프로그램의 경우 화면이 꽤 지저분 할 수 있다. 스플릿바도 돌출돼 보이고, 각 화면의 컨트롤도 들쭉날쭉하기 때문에 전체적인 인터페이스가 아마추어 같은 느낌을 들게한다. 여기서는 스플릿 기능을 하면서 보이지 않게 하는 루틴을 살펴보겠다.

 

 스플릿을 만들어주는 CSplitterWnd 클래스에 OnDrawSplitter() 라는 가상함수가 있다. CDC의 객체를 얻어 화면에 그려주기 직전에 호출되는 함수이다. 기존 루틴인 CSplitterWnd::OnDrawSplitter() 에서 해주는 일은 그려야 할 부분을 바, 보더, 박스 등으로 구분해 스플릿처럼 보이도록 각각 그려주는 기능을 수행한다. 따라서 이 함수를 재정의해 돌출루틴을 실행하지 않도록 하면 된다.

 가상함수를 재정의하기 위해선 CSplitterWnd를 기반 클래스로 하는 파생클래스를 하나 만들어야 한다. 그런데 클래스 위저드에서 클래스를 새로 만들 때 Base class를 아무리 뒤져봐도 CSplitterWnd 라는 클래스는 보이지 않는다. splitter라는 건 보이지만 이건 아무런 상관이 없는 것 같다. 비주얼 C++을 만들 때 Base Class로 쓸 수 있는 클래스를 제한해 놓았기 때문에 클래스 위저드로 작업할 때는 모든 클래스를 모두 Base Class로 쓸 수 있는 것은 아니다. 하지만 클래스 위저드가 지원하지 않는다고 해서 쓸 수 없는 것은 아니다. 만약 그러한 몇몇 클래스를 Base Class로 쓰려고 한다면 generic CWnd를 선택하고 나중에 CWnd 부분을 모두 자기가 Base Class로 쓰길 원하는 클래스로 바꾸어 주면 된다. 여기서는 CWnd를 CSplitterWnd로 바꾸어 주면 된다.

 Help를 보면 더 자세한 내용을 확인해 볼 수 있다. 여기서는 새로만든 파생 클래스를 CMySplitterWnd 라고 이름지었다. 다음 소스 코드에서 굵은 글씨 부분이 고친 부분이다.

 

==========================소스 코드=========================


// MySplitterWnd.h
#if !defined(AFX_MYSPLITTERWND_H__9F0AFD05_1618_11D3_BB2A_C7F3CB57D251__INCLUDED_)
#define AFX_MYSPLITTERWND_H__9F0AFD05_1618_11D3_BB2A_C7F3CB57D251__INCLUDED_

#if _MSC_VER >= 1000
#pragma once
#endif // _MSC_VER >= 1000
// MySplitterWnd.h : header file
//

/////////////////////////////////////////////////////////////////////////////
// CMySplitterWnd window

class CMySplitterWnd : public CSplitterWnd
{
// Construction
public:
        CMySplitterWnd();

// Attributes
public:

// Operations
public:

// Overrides
        // ClassWizard generated virtual function overrides
        //{{AFX_VIRTUAL(CMySplitterWnd)
protected:
        // 직접 추가한 것
        virtual void OnDrawSplitter( CDC* pDC, ESplitType nType, const CRect& rect );
        //}}AFX_VIRTUAL

// Implementation
public:
        virtual ~CMySplitterWnd();

        // Generated message map functions
protected:
        //{{AFX_MSG(CMySplitterWnd)
                // NOTE - the ClassWizard will add and remove member functions here.
        //}}AFX_MSG
        DECLARE_MESSAGE_MAP()
};

/////////////////////////////////////////////////////////////////////////////

//{{AFX_INSERT_LOCATION}}
// Microsoft Developer Studio will insert additional declarations immediately before the previous line.

#endif // !defined(AFX_MYSPLITTERWND_H__9F0AFD05_1618_11D3_BB2A_C7F3CB57D251__INCLUDED_)


=============================================================================================


// MySplitterWnd.cpp : implementation file
//
#include "stdafx.h"
#include "MySplitterWnd.h"

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

/////////////////////////////////////////////////////////////////////////////
// CMySplitterWnd

CMySplitterWnd::CMySplitterWnd()
{
}

CMySplitterWnd::~CMySplitterWnd()
{
}


BEGIN_MESSAGE_MAP(CMySplitterWnd, CSplitterWnd)
        //{{AFX_MSG_MAP(CMySplitterWnd)
                // NOTE - the ClassWizard will add and remove mapping macros here.
        //}}AFX_MSG_MAP
END_MESSAGE_MAP()


/////////////////////////////////////////////////////////////////////////////
// CMySplitterWnd message handlers

// MySplitterWnd 에서 overriding 한 함수
// VC 5.0에서 CSplitterWnd 가 설정되지 않으므로 CWnd에서 상속받아
// CWnd만 CSplitterWnd로 변경
// 함수는 직접 추가
void CMySplitterWnd::OnDrawSplitter( CDC* pDC, ESplitType nType, const CRect& rect )
{
        if( nType == splitBorder ) {
                CClientDC dc( this );
                CPen *pBorderPen, *pOldPen;
                pBorderPen = new CPen( PS_SOLID, 3, ::GetSysColor( COLOR_3DFACE ) );
                pOldPen = dc.SelectObject( pBorderPen );
                dc.SelectStockObject( HOLLOW_BRUSH );
                dc.Rectangle( rect );
                dc.SelectObject( pOldPen );
                if ( pBorderPen ) delete pBorderPen;    
        }
        if( nType == splitBar )
        {
                CSplitterWnd::OnDrawSplitter( pDC, nType, rect );
         }
}

 

 함수의 인자를 보면 디바이스 컨텍스트의 포인터인 pDC가 있고, 그려야 할 스플릿의 부위를 구분해주는 nType, 그릴 영역의 좌표를 가지고 있는 rect 등이 있다. 이 중에서 nType 에 들어올 수 있는 값은 splitBox, splitBar, splitIntersection, splitBorder 등이다. splitBox는 분할 윈도우가 드래깅할 때 그려지는 상자를 나타내며, splitBar는 스플릿의 표면을 그려줄 때 넘어온다. splitIntersection은  분할 윈도우의 교차되는 부분을 그릴 때 넘어오는 값인데 윈도우 95에서는 발생하지 않는다. 마지막으로 splitBorder는 스플릿 중 돌출 빛을 받는 부위와 그림자를 그려줄 때 넘어온다. 위의 코드를 보면 표면을 그려줄 땐(splitBar) 기존 루틴을 그냥 실행하고 splitBorder일 때만 표면과 같은 색상으로 테두리만 있는 투명 사각형을 그려 보더 부분을 그려주게 되어있다.

 

 이와 같이 재정의한 다음 MySplitterWnd.h 헤더파일을 MainFrm.h 헤더파일에 include 시켜주고 CSplitterWnd 대신 CMySplitterWnd를 사용하면 깔끔한 분할 윈도우를 만들 수 있다.