<프로그램 시작시 About박스를 표시하려면>

 MFC 5.0을 사용해 응용 프로그램이 시작할 때 프로그램의 소유권자를 보여주는 About box를 띄우고 싶습니다.   

About박스의 알고리즘을 살펴보면 생각보다 쉽습니다. 먼저 About박스를 생성하려면 다이얼로그를
모들리스(modeless)로 만들어야 합니다. 이렇게 만들어진 About박스는 윈도우가 띄기 전에 자동으로
나타납니다. 이때 사용자가 마우스 클릭하거나 키를 입력하면 프로그램 오브젝트의
PreTranslateMessage 메쏘드가 호출되는데, 이 메쏘드는 윈도우가 About를 제공하는지 체크하고
사용자의 입력이 있으면 About박스를 소멸시킵니다.

만약 사용자가 5초간 기다리면 OnIdle 메쏘드가 호출되고, 윈도우를 소멸시킬 시간이 경과했는지
체크합니다. 이런 식으로 흐름을 잡으면 됩니다. 다음을 참고하세요.

  BOOL CSampleApp::PreTranslateMessage(MSG* pMsg){
      BOOL bResult = CWinApp::PreTranslateMessage(pMsg);
      if (m_splash.m_hwnd != NULL &&
      (pMsg->message ==WM_KEYDOWN ||
      pMsg->message == WM_SYSKEYDOWN ||     
      pMsg->message == WM_LBUTTONDOWN ||
      pMsg->message == WM__RBUTTONDOWN ||
      pMsg->message == WM__MBUTTONDOWN ||
      pMsg->message == WM__NCLBUTTONDOWN ||
      pMsg->message == WM__NCRBUTTONDOWN ||
      pMsg->message == WM__NCMBUTTONDOWN)){
          m_splash.DestoryWindow();
          m_pMainWnd->UpdateWindow();
      }
      return bResult;
  }

 <입력 컨트롤에서 허용하는 문자 제한하기>

MFC 5.0을 사용해 입력 컨트롤에 숫자만 출력하도록 조정하고 싶습니다.  

 CEdit에서 클래스를 파생해 WM_CHAR 메시지를 처리하면 입력 컨트롤에서 특정 문자만 입력받을 수 있습니다. 우선 클래스위저드를 사용해 CEdit에서 파생된 클래스를 생성한 후 대화상자의 클래스에서 멤버 변수를 정의합니다. 그리고 OnInitDialog에서 CWnd::SubclassDlgItem을 호출해 입력 컨트롤을 서브 클래싱하면 됩니다.

  //대화상자 클래스 선언문
  Private :
      CMyEdit m_wndEdit; //새로운 입력 컨트롤의 인스턴스
  //대화상자 클래스 구현인 .CPP 파일
  BOOL CSampleDialog::OnInitDialog(){
     ....
     //입력 컨트롤을 서브 클래싱한다.
      m_wndEdit.SubclassDlgItem(IDC_EDIT, this);
      ...
  }

 이젠 클래스위저드를 사용해 WM_CHAR 메시지를 처리하면 됩니다. 먼저 nChar 인수를 평가해 어떤 동작을 수행할 것인지 지정해야 하는데 선택에 따라 문자가 출력되지 않게, 또는 문자를 그대로 전달하거나 문자를 변경해 전달할 수 있습니다.
 질문처럼 nChar를 숫자로 제한하고 싶다면 CWnd::OnChar에 전달하고, 그렇지 않다면
CWnd::OnChar를 호출하지 않으면 됩니다.

  //숫자만 출력한다.
  void CMyEdit::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags) {
      //nChar가 숫자인지 확인한다.
     if(IsCharAlphaNumeric(TCHAR)nChar) && !IsCharAlpha((TCHAR)nChar))
           CEdit::OnChar(nChar, nRepCnt, nFlags);

더 나아가 문자를 변경하려면 변경된 nChar 인수만을 전달하면 되는데, 이때 절대 CEdite::OnChar를 호출해서는 안됩니다. 왜냐하면 CEdite::OnChar에서 wParam과 lParam을 처음 값으로 채우는 CWnd::Default를 부르기 때문입니다. 따라서 nChar를 변경하고 CWnd::DefWindowProc를 호출합니다. 방법은 다음과 같습니다.

  //모든 문자를 대문자로 만든다.
  void CMyedit::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags){
      //문자를 그대로 변환한다.
      if (IsCharAlpha((TCHAR)nChar))
           nChar = toupper(nChar);
       //기본적인 OnChar 함수를 무시하고, 윈도우 기본 프로시저를 호출한다.
      DefWindowProc(WM_CHAR, nChar, MAKEPARAM(nRepCnt,nFlags))
  }

 

<MFC 확장 DLL이란?>

 DLL을 공부하다 보니 MFC 확장 DLL이라는 용어가 나옵니다. Win32 DLL과의 차이점이 있나요?

 애플리케이션 위저드는 DLL의 코드를 MFC 확장 DLL로 구성하고 있습니다. 이것은 기존 MFC 클래스에서 파생된 재사용 가능한 클래스를 구현한 DLL입니다. 그러므로 사용자는 MFC 클래스를 사용하는 것과 같은 방법으로 확장 DLL을 사용할 수 있습니다.
 다만 확장 DLL에는 몇 가지 제한이 있습니다. 그중 하나가 MFC 응용 프로그램이어야 한다는 것입니다. 가령 다른 언어나 클래스 라이브러리(비주얼 베이직이나 볼랜드의 OWL)로 만든 응용 프로그램에서 사용하려고 DLL을 생성해야 한다면 확장 DLL을 생성할 필요가 전혀 없습니다. 그러나 MFC로 만든 응용 프로그램에는 확장 DLL을 생성해야 합니다.

 

<캡션 외에 다른 곳을 클릭해 윈도우를 이동하려면>

 대개의 응용 프로그램의 경우 윈도우의 캡션바 영역을 마우스로 눌러 이동하는데, 다른 곳을 클릭해서는 옮길 수 없나요?

 윈도우는 마우스 위치를 결정할 필요가 있을 때 윈도우에 WM_NCHITTEST 메시지를 전달합니다. 따라서 마우스가 창의 캡션에 있는 것처럼 인식하도록 윈도우를 속이면 간단히 해결할 수 있습니다. 먼저 클래스위저드를 사용해 WM_NCHITTEST 메시지를 처리하고, 기본 클래스 함수를 호출합니다. 만약 함수가 HTCLIENT를 반환해 마우스가 클라이언트 영역에 있다고 일러주면, 윈도우는 마우스 위치를 캡션으로 인식해 HTCAPTION의 값을 돌려줍니다. 코드는 다음과 같습니다.

  UINT CSampleDialog::ONNcHitTest(CPoint point) {
     UINT nHitTest = CDialog::OnNcHitTest(point);
     return(nHitTest == HTCLIENT)?HTCAPTION : nHitTest;
  }

하지만 이 방법은 2가지 단점을 갖고 있습니다. 하지만 클라이언트 영역을 더블클릭할 때 윈도우가 최대화되어 있어야 한다는 것과 뷰를 갖고 있는 프레임 윈도우에서는 허용되지 않는다는 것입니다. 이외에도 사용자가 왼쪽 마우스 단추를 누를 때 감지해 캡션바를 클릭한 것처럼 인식하게 프레임 윈도우를 속이는 방법도 있습니다. 이런 경우 클래스위저드를 사용해 뷰에서 WM_LBUTTONDOWN 메시지를 처리하고, 프레임 윈도우에 단추의 누름 상태를 HTCAPTION으로 지정해 WM_NCBUTTON 메시지를 전달합니다.

  void CSampleView::OnLButtonDown(UINT nFlag, CPoint point) {
     CView ::OnButtonDown(nFlags, point);
     // 사용자가 캡션을 클릭한 것처럼 프레임 윈도우를 속인다.  
     GetParent()->PostMessage(WM_NCLBUTTONDOWN, HTCAPTION)
  }

좀더 응용하면 대화상자와 대화상자 기반의 응용 프로그램에서도 이 방법을 사용할 수 있습니다. 단, CWnd::GetParent에 대한 호출이 필요하지 않습니다. 다음을 참고하세요.

  void CSampleDialog::OnLButtonDown(UINT nFlag, CPoint point) {
      CDialog::OnLButtonDown(nFlag, point)   
     // 사용자가 캡션을 클릭한 것처럼 인식되게 끔 대화상자를 속입니다.  
     PostMessage(WM_NCLBUTTONDOWN, HTCAPTION,
         MAKEPRAM(point.x, point.y));
  }

<줄 단위로 끊기는 CEditView를 만들려면>

 MFC로 에디터를 작성하면 노트패드와 같이 <Enter>키를 치지 않는 이상 줄이 끊기지 않습니다. 화면 영역을 벗어날 경우 다음 줄로 넘어가는 에디터를 만들고 싶습니다.

 먼저 ES_AUTOHSCROLL과 WS_HSCROLL 스타일 비트를 취소합니다. 그런 다음 CWnd::PreCreateWindow를 오버라이딩하고 CREATESTRUCT 구조체를 변경하면 됩니다. 다만 주의할 점은 CEditView::PreCreateWindow는 cs.style 이 설정돼 있으므로 변경하려면 기본 클래스의 함수를 호출한 후 cs.style 을 바꿔야 합니다.

//입력 뷰에서 다음 줄로 끊어 출력하는 기능을 지원한다.
BOOL bResult =
        CEditView::PreCreateWindow(cs);
cs.style &= (ES_AUTOHSCROLL | WS_HSCROLL);
return Result;

 실행시 이 기능을 전환(toggle)하는 것은 더욱 복잡합니다. 현재 윈도우의 자료를 저장해야 하고, 윈도우를 파괴하고 정확한 스타일의 새로운 윈도우를 생성한 후 자료를 복구할 필요가 있기 때문입니다. 비주얼 C++과 함께 제공되는 슈퍼 패드 예제에서 CPadView::SetWordWrap 메쏘드를 참고하면 이해하기 쉬울 것입니다.

 

<상태바와 툴바의 포인터 얻기>

 MFC로 상태바와 툴바를 조작하는 프로그램을 하다보니 이들 포인터가 필요한 경우가 많습니다. 어디서든 얻을 수 없을까요?

 프레임웍은 기본적으로 메인 프레임의 하위 윈도우로 상태바와 툴바를 생성합니다. 상태바는 AFX_IDW_STATUS_BAR 라는 식별자를, 툴바는 AFX_IDW_TOOLBAR 라는 식별자를 가집니다. 다음은 AfxGetMainWnd 와 함께 CWnd:: GetDescendantWindow를 호출해 이러한 하위 윈도우에 대한 포인터를 얻는 방법으로 질문한 독자와 같은 경우 요긴하게 쓸 수 있습니다.

// 상태바에 대한 포인터를 얻는다.
CStatusBar* pStatusBar =
      (CStatusBar*)AfxGetMainWnd() ->
   GetDescendantWindow(AFX_IDW_ATATUS_BAR);
// 툴바에 대한 포인터를 얻는다.
CToolBar* pToolBar =
       (CToolBar *)AfxGetMainWnd() ->
   GetDescendantWindow(AFX_IDW_TOOLBAR);