IT_Programming/MFC · API

새창을 활성화시키기 않고 생성시키기 / 생성한 Class를 간단히 완전제거

JJun ™ 2007. 12. 25. 12:56
LONG

<탭 콘트롤의 크기를 바꾸는 법>

 어떤 창의 크기를 조정하는 것과 같은 방법으로 탭 콘트롤의 크기를 property sheet안에서 재조정할 수 있다. 아래에 나오는 code는 탭 콘트롤의 너비를 100 pixel씩 늘리는 기능을 가지고 있다. 기억해야 할 것은 property page내의 콘트롤들이 아래의 기능에 의하여 위치가 바뀌거나 크기가 재조정되지 않는다는 것이다. 탭 콘트롤의 너비를 줄이는 것은 property page내의 몇몇 콘트롤들을 숨길 수도 있다.

        CRect rectWnd;

        GetTabControl()->GetWindowRect(rectWnd);
        GetTabControl()->SetWindowPos(NULL, 0, 0,
                rectWnd.Width() + 100,  rectWnd.Height(),
                SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);

 

<프로퍼티 시트의 탭에 아이콘 넣기>

Tab control은 이미지 리스트 만들어서 SetImageList로 정해주면 쉽게 쓸 수 있습니다만...

Property Sheet는 이미지 리스트를 정해줄 수가 없더군요.

나름대로 뒤져서 방법은 찾았는데 유감스럽게도 16색 이상은 제대로 나오지 않는 것 같습니다.

CPropertyPage의 멤버 변수 중에 보면 m_psp라는 멤버가 있습니다. 그 구조체의 멤버에 보면 hIcon이 있죠. 거기에 아이콘의 핸들을 지정해주면 됩니다. 그리고 dwFlags라는 멤버에 PSH_USEHICON 값을 추가해줘야 합니다. 다음과 같이 되겠죠?

    m_Tab1.m_psp.hIcon = (아이콘 핸들);
    m_Tab1.m_psp.dwFlags |= PSH_USEHICON;

CPropertyPage m_Tab1을 가지고 있는 CPropertySheet의 생성자에서 AddPage를 하기 전에 해주는게 적당할 것 같습니다.

 

<VC++ 복소수 쓰기>

#include <math.h>
#include <complex>

using namespace std;

typedef complex<double> Complex;

void main(void)
{
        Complex x(0,1); // x = 0 + 1 I

        printf("%g + %g I\n",x);
}

이런식으로 쓰면 됩니다.

 

<대화상자에서 엔터 누르면 다음 컨트롤로>

 대화상자에 에디트 창을 많이 붙여 놓고 입력을 받을 때 엔터를 누르면 다음 에디트 창으로 넘어가게 하고 싶어하시는 분들이 많더군요. 하지만 엔터를 누르면 기본적으로 디폴트 버튼이 눌리게 되죠.

 다음과 같이 해주면 엔터를 탭으로 변환해서 메시지를 처리하기 때문에 다음 컨트롤로 포커스를 옮기게 됩니다.

BOOL COptionDlg::PreTranslateMessage(MSG* pMsg)
{
    if (pMsg->message == WM_KEYDOWN && pMsg->wParam == 13) {
        if (GetDlgItem(IDOK)->m_hWnd != pMsg->hwnd)
            pMsg->wParam = 9;
    }

    return CDialog::PreTranslateMessage(pMsg);
}

 

OK 버튼 위에 있을 때는 그냥 Enter로 작동하겠죠.

음 multiline일 경우에는 엔터를 입력받아야 할테니까 안바꾸도록 하는게 좋을 것 같네요.

 

<더블버퍼링 사용법>

음 두가지 방법이 있는데..

첫방법은 간단한 방법으로 Invalidate(FALSE)로 Draw함수를 부릅니다.

(TRUE)로 하면 화면에 잇는것을 흰색으로 지우고 다시 그림을 그리기 때문에 깜빡이지 않습니다

하지만 이것으로 해결이 않될때에는 더블버퍼링을 써야하죠.

이건 메모리에 우선 그릴것을 다그린후에 모니터에 뿌려줍니다.

그럼 그리는 과정에서 깜빡이는것을 없앨수 있죠..

 

방법에는

OnDraw함수에 보면 pDC가 잇습니다.

pDC->BitBlt(....)하면 비트맵을 찍을 수 있죠.. 여기서는 비트맵으로 설명하겠습니다..

    CDC MemDC;
    CBitmap MemBit;
    CDC WhiteDC;
    CBitmap WhiteBit;

   
 // 우선 이렇게 선언을 해줍니다. MemDC는 메모리에 그리기 위한 DC입니다.
    // MemBit는 비트맵을 저장해줄 변수입니다. 여기서는 도화지를 준비하는거랑 같죠.
   // WhiteDC와 ,WhiteBit는 그려줄 비트맵과 DC입니다.

 

그다음에..

    CClientDC dc(this);

    WhiteDC.CreateCompatibleDC(&dc);
    WhiteBit.LoadBitmap(IDB_WHITE);
    WhiteDC.SelectObject(&WhiteBit);

    MemDC.CreateCompatibleDC(&dc);

    MemBit.CreateCompatibleBitmap(&dc, 30 * 19 + 1, 30 * 19 + 1);
    MemDC.SelectObject(&MemBit);
   
    
// 여기서는 꼬옥 한번만 불려주게 합니다.. 쉬운 방법으로는 onCreate함수에 넣는거죠
    // 첫번째 줄은  WhiteDC를 호환성있게 해줍니다. CClientDC와 호환성있게 해주는거죠
    // 두번째 줄은 비트맵 로드하는겁니다. 매개변수는 리소스 아이디입니다.
    // 셋째줄은 DC에서 비트맵을 선택하는겁니다.
    // 넷째줄은 또 호환성 있게 합니다(MemDC,WhiteDC는 CDC입니다)

    // 다섯째줄은 비트맵을 만드는데.. 도화지를 까는겁니다. 첫매개변수는 &dc해주면 되
    // 고요 두번째와 세번째는 크기입니다. x값과.y값이죠
    //그리고 다시 DC에서 비트맵을 선택해줍니다.

 

 그런다음.. 이제부터 그릴 일이 있으면

    MemDC->BitBlt(...)

하면 됩니다. 그러면 메모리에만 그려지게 되죠.. 그리고 메모리에 다그렸다면 onDraw함수에서 마지막에

    pDC->BitBlt(...)

로 하면 화면에 그려집니다. BitBlt 함수는 MSDN 참조하시고요.

 

<디렉토리 만들기(서브 폴더 포함)>

사용법 makedir ("폴더 이름");

리턴값 TRUE(성공), FALSE(실패)

 

#include < direct.h >
#include < stdio.h >
#include < errno.h >

int mymkdir(LPCTSTR dirname)
{
    int ret=0;
#ifdef WIN32
    ret = mkdir(dirname);
#else
    #ifdef unix
        ret = mkdir (dirname,0775);
    #else
        #ifdef __TURBOC__
            ret = mkdir (dirname);
        #endif
    #endif
#endif

    return ret;
}

int makedir (LPCTSTR newdir)
{
     char *buffer ;
     char *p;
     int  len = strlen(newdir);  

     if (len <= 0)
          return 0;

     buffer = (char*)malloc(len+1);
     strcpy(buffer,newdir);

     if (buffer[len-1] == '/')
          buffer[len-1] = '\0';
     
     if (mymkdir(buffer) == 0) {
          free(buffer);
          return 1;
    }
    p = buffer+1;

    while (1) {
         char hold;

         while(*p && *p != '\\' && *p != '/')
              p++;
         hold = *p;

         *p = 0;
         if ((mymkdir(buffer) == -1) && (errno == ENOENT)) {
               printf("couldn't create directory %s\n",buffer);
               free(buffer);
               return 0;
         }  // if

         if (hold == 0)
               break;
        *p++ = hold;
    } // while

    free(buffer);
    return 1;
} // mkdir()

 

<프린터 포트 제어방법>

 Window 환경에서의 프린터 제어는 GDI를 사용한 방법을 권장하고있죠. 즉, 예전의 도스에서처럼 바이트 단위 문자출력이나 제어코드의 출력의 방법을 권장하고 있지 않다는 말입니다. 이것은 프린터라는 자원이 공유자원이고, 실제로, GDI를 사용한 출력이 상당히 좋은 품질의 결과를 제공하기 때문입니다. 하지만 경우에 따라서는 님과 같이 하드웨어들의 제어를 위해서 프린터를 직접제어가 필요할 때가 있는데, 이러한 경우에는 파일 제어함수를 사용하시면 됩니다. 윈도우즈 3.1 환경에서의 파일 입출력은 장치의 제어에 대한 지원이 없었는데, (실제 3.1에서는 직렬/병렬 포트의 제어시에 OpenComm과 같은 함수를 사용했었습니다.) Win32 에서는 유닉스와 같이 모든 장치들을 파일처럼 처리할 수 있도록 지원하고 있습니다. 물론 내부 구조는 다릅니다.

 이를 사용한 프린터 포트 직접 제어를 예제를 간단히 작성하였는데 다음과 같습니다.

 

#include <windows.h>

void main()
{
    HANDLE handle;
 
    handle = CreateFile("lpt1:",  GENERIC_READ : GENERIC_WRITE, 0, NULL, OPEN_ALWAYS,
                        FILE_ATTRIBUTE_NORMAL, NULL);
 
    if(handle==INVALID_HANDLE_VALUE)
       printf("can not open lpt1 !");
    else
    {
       DWORD dwWritten;
       WriteFile(handle, "Print some message to printer using CreateFile\n\f",
                48, &dwWritten, NULL);
       CloseHandle(handle);
    }

}

위 소스는 비주얼 C/C++ 의 BIN 디렉토리에 있는 VCVARS32.BAT 를 먼저 실행시키고, cl.exe를 사용하여서 컴파일과 링크를 하신 후 실행시키시면 됩니다. 물론 윈도우즈 스타일로 하셔도 상관없습니다.   

 

 <SDI에서 뷰 전환하기>

SDI 프로그램에서 뷰를 서로 전환하는 방법입니다. 여기서의 예제는 4개의 뷰를 만들어서 전환하는 것을 보여주고 있는데, 필요에 따라 뷰의 갯수를 조절하면 됩니다.

 

CMainFrame 헤더파일에서

     public:
          // 현재 뷰가 어떤것인지 나타내는 변수
          short CurrentView;
          enum eView { MAIN = 0 , USER = 1 , BOOK = 2 , WORK = 3 };
          void SwitchView(eView);


CMainFrame CPP파일에서

void CMainFrame::SwitchView(eView nView)
{
     CView* pOldActiveView = GetActiveView();

     // nView가 없다면 NULL
     CView* pNewActiveView = (CView*) GetDlgItem(nView);

     if(CurrentView == nView)
// 같은 뷰일 때
         return;

      if(pNewActiveView == NULL) {
              switch (nView) {
              case MAIN:
                   pNewActiveView = (CView*) new CLibraryView;
                   break;

              case USER:
                   pNewActiveView = (CView*) new CUserView;
                   break;

              case BOOK:
                   pNewActiveView = (CView*) new CBookView;
                   break;

              case WORK:
                    pNewActiveView = (CView*) new CWorkView;
                    break;
              }

              CCreateContext context;
              context.m_pCurrentDoc = pOldActiveView->GetDocument();
              pNewActiveView->Create (NULL , NULL ,
                   WS_BORDER | WS_CHILD , CFrameWnd::rectD efault ,
                   this, nView , &context);

              pNewActiveView -> onInitialUpdate();
      }

      SetActiveView ( pNewActiveView);
      pNewActiveView->ShowWindow(SW_SHOW);
      pOldActiveView->ShowWindow(SW_HIDE);

      if(pOldActiveView->GetRuntimeClass() == RUNTIME_CLASS(CLibraryView) )
           pOldActiveView->SetDlgCtrlID(MAIN);

      else if(pOldActiveView->GetRuntimeClass() == RUNTIME_CLASS(CUserView) )
           pOldActiveView->SetDlgCtrlID(USER);

      else if(pOldActiveView->GetRuntimeClass() == RUNTIME_CLASS(CBookView) )
           pOldActiveView->SetDlgCtrlID(BOOK);

      else if(pOldActiveView->GetRuntimeClass() == RUNTIME_CLASS(CWorkView) )
           pOldActiveView->SetDlgCtrlID(WORK);

      pNewActiveView->SetDlgCtrlID(AFX_IDW_PANE_FIRST);

      RecalcLayout();
      CurrentView = nView; // 현재 뷰를 설정한 뷰로 대입시킨다.
 } 

 

 - The end of this article -

ARTICLE

<새창을 활성화시키기 않고 생성시키기>

보통의 팝업윈도우를 생성할 때에는 일반적인 방법으로는 팝업 윈도우가 생성되면서 그 윈도우가 활성화 되고 포커스가 가게 됩니다. 가끔 이러한 현상을 원하지 않을 때가 있을수 있습니다.

그럴땐 이렇게 해주면 됩니다. 우선 1. WS_VISIBLE속성 없이 창을 생성합니다.

그뒤 2. SetWindowPos로 활성화 없이 창을 보이게 하면 됩니다.

 

간단한 코드 입니다.

m_pPopUpWnd->Create(m_hWnd, rc, NULL,
     WS_POPUP, m_clrBackColor, m_crShadow,
     m_crHilight, m_pFont, this);

m_pPopUpWnd->SetWindowPos(HWND_NOTOPMOST ,
     0, 0, 0, 0, SWP_NOSIZE | SWP_NOZORDER
     | SWP_NOMOVE | SWP_NOACTIVATE | SWP_SHOWWINDOW);

 

 

<생성한 Class를 간단히 완전제거>

 간단한 예로 설명을 시작하자면, 우선 ClassWizard나 Workspace창에서 CTest라는 Class를 추가했다고 합시다. 그런데 CTest의 Parent Class를 잘못 선택해서 생성시켜버렸을 때 어떻게 하시겠습니까? 더군다나 동일한 이름을 고수해야만 하는 경우라면...

물론 생성된 Source code를 변경해도 되지만 그리 쉽게 되지않는 경우도 있습니다.

이럴때 혹은 기타의 경우 생성된 클래스를 삭제한다는 것은 쉽지 않습니다.

FileView에서 Test.h와 Test.cpp만 삭제하면 되지 않느냐고요?

물론 그렇게 하면 Workspace에서 빠지지만 동일한 이름으로 class를 다시 만들수는 없을 겁니다.

왜냐하면 *.clw라는 화일에 그 클래스에 관한 정보가 고스란히 남아있기 때문이죠. 제가 한때 어렵게

알아낸 방법은 *.clw화일을 편집하는 거였는데 상당히 번거롭고 귀찮더라구요. 그래서 제가 쓰는 방법을 소개합니다.

(1) FileView에서 관련 화일을 지운다. (위의 예로 치면, Test.cpp와 Test.h)

(2) 작업중인 Project Folder로 가서 Test.cpp와 Test.h 그리고 *.clw 화일 3개를 모두 지운다.

(3) ClassWizard를 실행시킨다. 그러면 *.clw화일이 삭제 되었기 때문에 클래스 정보를 알 수 없다는

     메세지가 나올것이다.

(4) 그리고 연달아 나오는 다이얼로그 박스에서 가장 아래에 있는 ListBox의 모든 내용을 선택해서

     삭제하고 Add All버튼을 누르면 새로운 *.clw화일이 생성되면서 CTest가 빠진 모든 클래스 정보가

     재구축된다.

(5) 그리고 자신이 원하는 작업(CTest의 재생성)을 하면 된다.

 

 

[↓ Click! 탭 콘트롤의 크기를 바꾸는 법 / 프로퍼티 시트의 탭에 아이콘 넣기 /

              VC++ 복소수 쓰기 / 대화상자에서 엔터 누르면 다음 컨트롤로 /

              더블버퍼링 사용법 / 디렉토리 만들기(서브 폴더 포함) / 프린터 포트 제어방법 /

              SDI에서 뷰 전환하기 ]