<다이얼로그 박스 없이 뷰 화면에 버튼 만들기>

뷰에서 다이얼로그 박스를 만들지 않고 버튼을 뷰 화면에 만드는 방법을 알고 싶습니다.

일반적으로 CWnd에서 상속받은 윈도우에 버튼을 넣고 싶으면 다음과 같이 해보세요.

[1] 자신이 특별히 사용할 버튼을 CButton으로부터 상속받는다.

class iMyButton : public CButton
{
     ...
};

[2] 윈도우 클래스(CChildView)에 멤버로 버튼을 가지게 한다.

class CChildView : public CWnd
{
     // Construction
     public:
          CChildView();

     private:
          iMyButton m_btn;
     ...

     // 메시지 맵 함수를 생성
     protected:
          // {{AFX_MSG(CChildView)
          ...
               afx_msg void OnParentNotify(UINT message, LPARAM lParam);
          // }} AFX_MSG
          DECLARE_MESSAGE_MAP()
};

[3] 부모 윈도우가 생성될 때 버튼도 생성시킨다.

 void CChildView::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
     CRect rcBtn(30, 50, 150, 90);
     m_btn.Create("MyBtn", BS_PUSHBUTTON | WS_CHILD | WS_VISIBLE,
          rcBtn, this, IDC_MY_BUTTON);
}

[4] 버튼을 생성/소멸할 때나 버튼을 눌렀을 때 특별한 처리를 해주고 싶으면 WM_PARENTNOTIFY 메시지를 처리해준다.

void CChildView::OnParentNotify(UINT message, LPARAM lParam)
{
     switch(message) {
     case WM_CREATE :
          TRACE("CChildView::OnParentNotify --> WM_CREATE\n");
          break;
     case WM_DESTROY :
          TRACE("CChildView::OnParentNotify --> WM_DESTROY\n");
          break;
     case WM_LBUTTONDOWN :
          TRACE("CChildView::OnParentNotify --> WM_LBUTTONDOWN\n");
          break;
     case WM_MBUTTONDOWN :
          TRACE("CChildView::OnParentNotify --> WM_MBUTTONDOWN\n");
          break;
     case WM_RBUTTONDOWN :
          TRACE("CChildView::OnParentNotify --> WM_RBUTTONDOWN\n");
          break;
     }
}

 

<비주얼 C++에서 프린트 미리보기 구현>

비주얼 C++ 5.0 으로 다이얼로그에서 프린트가 되는 기능을 구현하고 있는데, 프린트 미리보기를 어떻게 구현해야 할지를 모르겠습니다. 방법을 알려주세요.

미리보기에 대한 함수는 다음과 같습니다.

void 뷰클래스명::OnFilePrintPreview()
{
     CFilePrintPreviewState *pState = new CPrintPreviewState;
     if(!DoPrintPreview(AFX_IDD_PREVIEW_TOOLBAR, this,
          RUNTIME_CLASS(뷰클래스명, pState))
     {
          TRACE0("Error : DoPrintPreview failed");
          AfxMessageBox(AFX_IDP_COMMAND_FAILURE);
          delete pState;
     }
}

 보다 자세한 것을 알고 싶으면 비주얼 C++의 도움말을 참조하세요. "도움말->SEARCH->QUERY" 에서 쿼리(Query) 안을 보면 에디트 박스가 있는데, 거기에서 CPrintPreviewState을 입력하고 엔터(혹은 밑에 있는 Query를 클릭)를 치면 됩니다.

 

<CListCtrl에서 팝업 메뉴 구현>

CListCtrl에서 오른쪽 마우스 버튼을 눌렸을 때 데이터의 수정, 삽입, 삭제를 나타내는 팝업메뉴를 구현하고 싶습니다. 비주얼 C로 프로그래밍하는 것은 처음이라서 방법을 잘 모르겠습니다. 자세한 설명 부탁드립니다.

다음과 같이 해보면 해결할 수 있을 것입니다.

void CMyListCtrl::OnRButtonDown(UINT nFlags, CPoint point)
{
     CMenu menu;

     // 물론 IDR_POPUP이란 메뉴가 리소스 편집기로
     // 작성되 있어야 합니다.
     VERIFY(menu.LoadMenu(IDR_POPUP));

     // 첫 번째 서브메뉴를
     CMenu* pPopup = menu.GetSubMenu(0);
     ASSERT(pPopup != NULL);
     CRect viewRect;
     GetWindowRect(&viewRect);

     // 화면에 출력합니다.
     pPopup->TrackPopupMenu(
          TPM_LEFTALIGN | TPM_RIGHTBUTTON,
          point.x + viewRect.left,
          point.y + viewRect.top,
          this
          );

     CListCtrl::OnRButtonDown(nFlags, point);
}

 

<다이얼로그 에디트 박스에서 값 입력받기>

다이얼로그 박스의 에디트 상자에서 값을 입력받아 부모 윈도우에 직선을 그리는 프로그래밍을 하고 있습니다. 다이얼로그 박스의 에디트 상자에서 입력받은 값을 뷰 클래스의 draw 함수와 연결하고 싶습니다.

질문한 내용의 해결 방법은 의외로 쉽습니다. 우선 다이얼로그 박스를 만들고 값을 입력받는 에디트 박스에 멤버 변수를 설정합니다. 이 과정은 클래스 위저드에 멤버 탭을 누르고 해당 컨트롤을 선택하면 변수를 입력할 수 있습니다. 여기서 만든 다이얼로그가 CInputDlg, 멤버 변수가 m_iVal이고, CView에서 특정 메뉴를 선택했다면 다음과 같이 하면 됩니다.

CView::OnInputValue()
{
     CInputDlg MyDlg;     // 객체를 하나 만듭니다.
     
     MyDlg.m_iVal = 5;    // 초기값을 여기서 설정해도 됩니다.
     
     if(MyDlg.DoModal() == IDOK) {   // 설정이 완료되면
          // 대화 상자의 값을 읽어 들입니다.
          m_iLineWidth = MyDlg.m_iVal;

          //
          // 원하는 작업을 수행합니다.
     }

 

<아스키 값을 Hex 값으로 바꾸는 함수>

아스키 값을 Hex로 바꾸는 함수가 어떤 것인지 궁금합니다.

Hex 문자열을 정수값으로 변환하는 프로그램을 말하는 것 같은데 다음의 루틴을 이용해 보기 바랍니다. 런타임 라이브러리에는 그런 함수가 없는 걸로 알고 있습니다.

#include "math.h"

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

//
UINT StrHexToInt(LPCTSTR str)
{
     return (UINT)StrHexToLong(str);
}

ULONG StrHexToLong(LPCTSTR str)
{
     CString work;
     int nLength;
     ULONG ulRetVal;

     work = str;

     if(strstr(str, "0x") != NULL)
          work = strstr(str, "0x") + 2;

     //
     nLength = work.GetLength() - 1;
     ulRetVal = 0;
     
     work.MakeUpper();

     for(int i=0; i<work.GetLength(); i++, nLength--) {
          switch(work.GetAt(i)) {
          case '0' :
          case '1' :
          case '2' :
          case '3' :
          case '4' :
          case '5' :
          case '6' :
          case '7' :
          case '8' :
          case '9' :
               ulRetVal += (work.GetAt(i) - '0') * (int)pow(16, nLength);
               break;

          case 'A' :
          case 'B' :
          case 'C' :
          case 'D' :
          case 'E' :
          case 'F' :
               ulRetVal += (10 + work.GetAt(i) - 'A') * (int)pow(16, nLength);
               break;

          default :
               return ulRetVal = 0;
          }
     }
     return ulRetVal;
}

십진수로 변환되면 다른 진수로도 충분히 바꿀 수 있을 것입니다.

 

<프린트시 가로 제어에 대해>

프린트 프로그래밍을 하고 있습니다. 그런데 가로로 프린트를 해야 하는데 있어서 문제가 발생했습니다. 코드는 다음과 같습니다.

DEVMODE dm;
memset(&dm, 0, sizeof(dm));
dm.dmSpecVersion = DM_SPECVERSION;
dm.dmSize = sizeof(dm);
dm.dmFields = DM_ORIENTATION;
dm.dmOrientation = DMORIENT_LANDSCAPE;
pDC->ResetDC(&dm);

 이렇게 지정을 했는데 문제는 그 다음 페이지부터 가로로 인쇄가 된다는 것입니다. 그리고 윈도우 NT 환경에서는 이 작업이 진행되지 않습니다. 해결 방법을 알려주세요.

가로 인쇄 제어를 다음과 같은 방법으로 해 보세요.

CDC dc;

CPrintDialog dlg1(FALSE), dlg2(FALSE);

dlg1.GetDefaults();

LPDEVMODE pDM = dlg1.GetDevMode();

// ++
if(pDM == NULL) {
     AfxMessageBox(IDS_NOT_READY_PRINTER, MB_ICONSTOP);
     return;
}

pDM->dmOrientation = DMORIENT_LANDSCAPE;
pDM->dmPrintQuality = 300;
pDM->dmYResolution = 300;

dlg2.m_pd.hDevMode = (HANDLE)pDM;

if(dlg2.DoModal() == IDOK)
     dc.Attach(dlg2.GetPrinterDC());     // PrinterDC를 얻는다.
else
     return;

DOCINFO di;

::ZeroMemory(&di, sizeof(DOCINFO));

di.cbSize = sizeof(DOCINFO);
di.lpszDocName = "Preset Pulse Graph Printing...";

if(dc.StartDoc(&di) > 0) {
     // 필요한 데이터를 출력한다.
}

 

<시스템 폰트 얻기>

내 컴퓨터에 깔려있는 폰트를 얻으려면 어떻게 해야 하나요? 예를 들어서 리스트 박스나 콤보 박스에 내 컴퓨터의 폰트가 모두 보이게 하려면 어떤 함수를 써야 하는지 궁금합니다.

 다음의 함수를 사용하면 됩니다.

int EnumFontFamiliesEx(HDC hdc,        // DC 제어
     LPLOGFONT lpLogFont,                  // 논리적인 폰트정보 포인터
     FONTENUMPROC lpEnumFontFamExProc,   // 콜백함수 포인터
     LPARAM lParam,                            // 애플리케이션 데이터 
     DWORD dwFlags                            // reserved; must be zero
);

 위 함수는 lpLogFont에 지정된 폰트들이 존재하면 lpEnumFontFamExProc에 등록된 CALLBACK 함수를 호출합니다. lpEnumFontFamExProc의 원형은 다음과 같습니다.

 int CALLBACK EnumFontFamExProc(
     ENUMLOGFONTEX *lpelfe,       // 논리적인 폰트 데이터 포인터
     NEWTEXTMETRICEX *lpntme,   // 물리적인 폰트 데이터 포인터
     int FontType,                           // 폰트 형태
     LPARAM  lParam                      // 정의된 애플리케이션 데이터
);

사용자는 이 함수에서 전달되는 폰트의 정보로 리스트 박스에 이름을 등록한다던가 아니면 다른 장소에 정보를 보관하면 됩니다. 이해를 돕기 위해 간단한 상황을 만들어 예를 보여드리겠습니다.

대화 상자에 IDC_LIST1 이라는 리스트 박스가 존재한다.
대화 상자가 열렸을 때 IDC_LIST1 박스에 폰트의 목록을 등록한다.
이때 대화 상자의 클래스명은 CMyFontDlg 이다.

 우선 WM_INITDIALOG 메시지를 처리하는 OnInitDialog 함수를 살펴보겠습니다.

BOOL CMyFontDlg::OnInitDialog()
{
     CDialog::OnInitDialog();

     CListBox *pList = (CListBox *)GetDlgItem(IDC_LIST1);
     
     if(pList != NULL) {
          CMainFrame *pMain = (CMainFrame *)AfxGetMainWnd();
          CView *pView = pMain->GetActiveView();

          CDC *pDC = pView->GetDC();

          LOGFONT lfont;

          lfont.lfCharset = DEFAULT_CHARSET;
          lfont.lfFaceName[0] = '\0';     // NULL 스트링 설정
          lfont.lfPitchAndFamily = 0;        // 모든 언어 선택

          // MyFontCallBack 함수에 lParam 인자로 pList의
          // 포인터를 전달합니다.
          EnumFontFamiliesEx(pDC->m_hDC, &lfont,
               MyFontCallBack,
               (LPARAM)pList,
               0 );

          pView->ReleaseDC(pDC);
     }

     return TRUE;
}

// OnInitDialog에서 EnumFontFamiliesEx 함수의 lParam 값에
// CListBox의 포인터를 넣었으므로 이 함수는 호출되면
// lParam에는 CListBox의 포인터가 전달된다.
// 그리고 계속 폰트 정보를 받으려면 0이 아닌 값을
// 리턴하고, 중지하려면 0을 리턴합니다.

int CALLBACK CMyFontDlg::MyFontCallBack(ENUMLOGFONTEX *lpelfe,
     NEWTEXTMETRICEX *lpntme,
     int FontType,
     LPARAM lParam )
{
     CListBox *pList = (CListBox *)lParam;

     // listbox에 폰트 이름을 넣습니다.
     // lpelfe의 다른 정보를 사용하시려면 도움말을 참조..
     if(pList->AddString(lpelfe->elfLogFont.lfFameName) == LB_ERR)
          return 0;

     return 1;
}

 동작을 살펴보면 만일 시스템에 '굴림체', '바탕체', '돋움체' 등 3개의 폰트가 설치돼 있다고 가정하면 MyFontCallBack 함수는 각 폰트마다 한번씩 호출되니까 총 3번 호출이 됩니다. 폰트에 대한 완전한 정보를 보관하고 싶으면 LOGFONT의 배열을 설정한 후 CallBack 함수가 호출될 때마다 그 정보를 저장하면 됩니다.

 

- the end of this article -