태터데스크 관리자

도움말
닫기
적용하기   첫페이지 만들기

태터데스크 메시지

저장하였습니다.

책의 608 페이지 에 있는 내용으로

프로젝트명은 RedrawDemo로  작성한다.
 

윈도우의 깜박임을 제거하는 최선의 방법은 더블 버퍼링 이다.

더블 버퍼링은 화면 DC에 직접 출력하는 것이 아닌 화면 DC와 호환이 되는 메모리 DC를 만들어서 모든 그리기 작업이 끝난 값을 집어넣어둔 후 메모리 DC의  내용을 화면 DC로 복사하는 기법이다.

 이렇게 하면 화면의 크기를 변경하더라도 흰색 깜박임이 발생하지 않게 된다.[흰색으로 초기화 시키는게 아닌 메모리 DC의 내용을 가져오기만 하기 때문에.]

전에 쓴 OnPaint()함수의 코드에서 CPaintDC dc(this); 를 CBufferDC dc(this); 로 변경하고 CBufferDC 클래스를 프로젝트에 인클루드 하면 간단히 더블 버퍼링을 구현할 수 있다. 

라는데 솔까 소스 보니까 개뻥이다.

왜냐하면 해당 클래스를 만들어야하고

헤더파일과 cpp파일까지 만들어서 집어넣어줘야 컴파일이 되기 때문.[안그러면 에러남]

아래는 BufferDC.h의 내용이다.

class CBufferDC : public CDC  
{

private:
CBufferDC() { }
CBufferDC(const CBufferDC &src) { }
CBufferDC& operator=(const CBufferDC &src) { }

protected:
BOOL Attach(HDC hDC);
HDC Detach();

private:
CWnd* m_pParent; //대상 윈도우에 대한 포인터
CDC* m_pTarget; //대상 윈도우 DC에 대한 포인터
PAINTSTRUCT m_PaintStruct;
CRect m_RcClient, m_RcWindow; //대상 윈도우의 크기 정보

CDC m_MemoryDC; //버퍼 DC
CBitmap m_MemoryBmp, *m_pOldMemoryBmp; //버퍼링을 위한 비트맵


여기에 주석이 안달린 곳.

PAINTSTRUCT 라는 구조체는 화면을 그리기 위한 정보를 담는 구조체로
Win32 API 프로그래밍 방식에서 볼 수 있다.

MFC에서는 WM_PAINT 메시지가 전달되었을 때 OnPaint 함수를 호출하고 CPaintDC클래스 객체를 생성해서 그리기를 수행하는것이 일반적이나 

Win32 API에서는 ::BeginPaint() 함수를 호출하여 DC핸들(이게 바로 HDC)을 얻고 이 핸들로 화면에 그리기 작업을 수행한다. DC핸들을 사용한 후에는 ::EndPaint()함수를 호출해서 핸들을 반환한다.

MFC에서는 객체가 소멸되는 시점에 소멸자가 호출되어 이런 처리를 한다.


m_MemoryBmp와 m_pOldMemoryBmp 멤버는 CBufferDc 클래스 객체를 생성할 때 내부적으로 비트맵을 동적으로 생성하여 이를 선택[SelectObject()] 하게 된다.
따라서 CBufferDc클래스 객체에 그리기를 수행했다면 그것은 선택한 비트맵에 그려지게 된다.


다음은 BufferDC.cpp의 내용 중 일부이다

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
CBufferDC::CBufferDC(CWnd *pParent)
    : m_pParent(pParent)
{
ASSERT(pParent);

//여기부터
m_pTarget = m_pParent->BeginPaint(&m_PaintStruct);
m_pParent->GetClientRect(&m_RcClient);
m_pParent->GetWindowRect(&m_RcWindow);            //대상 윈도우에 대한 정보를 수집한다.


m_MemoryDC.CreateCompatibleDC(m_pTarget); //대상 윈도우에 대한 DC를 생성한다.

       //여기부터
m_MemoryBmp.CreateBitmap(m_RcClient.Width(), m_RcClient.Height(), 
m_MemoryDC.GetDeviceCaps(PLANES),
m_MemoryDC.GetDeviceCaps(BITSPIXEL), 0);
m_pOldMemoryBmp = m_MemoryDC.SelectObject(&m_MemoryBmp);
//대상 DC에 대한 메모리 비트맵을 생성하여 Select 한다.
 


Attach(m_MemoryDC); //메모리 버퍼에 Attach한다.
}


여기에서 핵심적으로 사용된 코드인 CreateBitmap 함수의 원형은 다음과 같다.


  BOOL CreateBitmap(
int nWidth,
int nHeight,         //뻔한 너비 높이.
UINT nPlanes,      //비트맵의 색상 플래인[면]
UINT nBitcount,      //한 픽셀을 표시할 때 사용되는 비트 수        두 값 모두 CDC클래스의 GetDeviceCaps()메서드를 이용해서 알아내면 됨
const void* lpBits// 이 인자는 생성할 비트맵이 저장된 주소이며 이 값이 NULL이면 초기화되지 않은 상태로 비트맵이 만들어짐 = 검은색 비트맵
);


다음으로는

생성자보다 중요하다는 소멸자 코드.




CBufferDC::~CBufferDC()
{

m_pTarget->BitBlt(
m_PaintStruct.rcPaint.left,
m_PaintStruct.rcPaint.top, 
m_PaintStruct.rcPaint.right - m_PaintStruct.rcPaint.left, 
m_PaintStruct.rcPaint.bottom - m_PaintStruct.rcPaint.top, 
&m_MemoryDC,
m_PaintStruct.rcPaint.left,
m_PaintStruct.rcPaint.top, SRCCOPY);
//메모리 DC의 내용을 대상 윈도우에 출력한다.
//내부적으로 비트맵에 출력한 것이므로 해당 비트맵을 1:1로 복사한다.


m_MemoryDC.SelectObject(m_pOldMemoryBmp);
m_pParent->EndPaint(&m_PaintStruct);

Detach();
}

소멸자의 코드를 보면 CBitmap 클래스 객체를 선택한 메모리 DC의 내용을 연결된 화면 DC에 SRCCOPY 모드로 출력한 후 자원을 반납하고 처리를 완료한다.

화면 DC에 직접 그리는 것도 좋지만 중간 단계를 거쳐서 출력하면 출력 전 과정[흰색으로 지우는]이 화면에 나타나지 않으므로 깜박임이 없어지게 된다.

WMERASEBKGND 메시지 핸들러 함수를 등록하여 아무런 처리도 하지 않도록 수정하고 OnPaint()함수에서 CBufferDC 클래스를 활용하여 더블 버퍼링을 구현한다면 깜빡임을 완벽히 해결할 수 있다. 



스샷 올려봐야 의민 없지만.. 어차피 윈도우 사이즈 변경하면서 깜빡임의 유무를 보는거인지라.;

만드는 방법은 전의 글에서 ReDrawDemo에 

Buffer.cpp와 .h를 만들어서 속에 코드까지 다 입력을 해야한다[책에선 무책임하게도 코드를 알려주기보다 그냥 인터넷에서 찾으라고 한다.]

고로 그냥 책의 소스를 그대로 보고 이해하는게 더 빠를것이다.






이건 코드파일

 

저작자 표시 비영리
신고
Posted by 졸가메
이번 챕터에 있는 더블버퍼링 을 공부하기 위한 챕터 18 공부.

응용 프로그램에서 화면의 크기를 조정할 때 대부분 화면 상 깜박임이 발생한다.

메인 프레임 윈도우 일부가 그러는 것은 어쩔 수 없다고 하더라도. 클라이언트 뷰 윈도우가 통째로 깜빡이면

그리 좋은 모습은 아니라 개선을 해야하는 필수 대상이라고 한다. 뭐 본적은 없지만 .

WM_ERASEBKGND 메시지에 대하여 알아야 한다.

--------- 화면 그리기의 구조 -------

이 메시지는. WM_PAINT 메시지와 더불어 윈도우를 다시 그릴 때 발생하는 메시지 중 하나이다. 윈도우가 클라이언트 영역과 비 클라이언트 영역으로 구별되어 있고, 각각을 그리도록 하는 메시지 는
WM_PAINT 와 WM_ERASEBKGND  이다. 그러나 클라이언트 영역에서는 WM_PAINT 메시지보다 WM_ERASEBKGND 메시지가 먼저 발생한다.

그리고 이 WM_ERASEBKGND 는 윈도우의 배경을 그리는 코드를 실행하며. 일반적으로 윈도우의 배경이 흰색이므로

흰색으로 배경을 칠하게 되는 것이다.[이 때에 깜박임이 발생한다]

따라서 배경을 그리는 작업이 불필요하다고 생각되면 WM_ERASEBKGND  메시지 핸들러 함수를 등록한 후 상위 클래스의 멤버를 호출하지 않고 반환하도록 코드를 아래와 같이  수정하면 깜박임이 줄어들게 된다.[많이 티가 나게 줄어드는것은 아니다.]

=======================================================
BOOL CRedrawDemoView::OnEraseBkgnd(CDC* pDC)
{
return TRUE;
// return CView::OnEraseBkgnd(pDC);
}

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

RedrawWindow의 원형보기

더보기

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


예제코드 출력화면


파일 업로드.






------------ 눈속임의 미학 ------------

배경이 투명한 

윈도우의 구현.


// CTransparentDemoView 메시지 처리기

BOOL CTransparentDemoView::OnEraseBkgnd(CDC* pDC)
{
CRect Rect;
GetClientRect(&Rect);
pDC->FillSolidRect(&Rect, RGB(255, 255, 255));

CImage ImageBackground;
ImageBackground.LoadFromResource(AfxGetInstanceHandle(), IDB_Background);
ImageBackground.BitBlt(pDC->m_hDC, 0, 0);

return TRUE;
// return CView::OnEraseBkgnd(pDC);
}

이 코드의 핵심은 OnEraseBkgnd () 메시지 핸들러 함수의 인자로 전달받은
DC에 이미지를 로드하여 출력하는 코드이다. 이 이미지는 자식 윈도우와 함께 사용할
이미지가 된다.

다음 코드는 클라이언트 뷰 윈도우의 자식 윈도우인 CTranspartentWnd 클래스의
OnEraseBkgnd()함수이다.
 

BOOL CTransparentWnd::OnEraseBkgnd(CDC* pDC) { CRect Rect, ParentRect;//여기부터 GetClientRect(&Rect); GetParent()->GetClientRect(&ParentRect);// 자식 윈도우의 왼쪽 위가 부모 윈도우 기준으로 어디인지 계산하는 코드 CPoint ptLeftTop = CPoint(0, 0); ClientToScreen(&ptLeftTop); GetParent()->ScreenToClient(&ptLeftTop); CDC MemDC;//여기부터 CBitmap Bmp; MemDC.CreateCompatibleDC(NULL); Bmp.CreateBitmap(ParentRect.Width(), ParentRect.Height(), MemDC.GetDeviceCaps(PLANES), MemDC.GetDeviceCaps(BITSPIXEL), NULL); CBitmap* pOldBmp = MemDC.SelectObject(&Bmp);//메모리 DC에 적절한 CBitmap 클래스 객체를 생성하여 선택하는 코드 GetParent()->SendMessage(WM_ERASEBKGND, (WPARAM)MemDC.m_hDC);// 부모 윈도우에 WM_ERASEBKGND 메시지를 보냄 pDC->BitBlt(0, 0, Rect.Width(), Rect.Height(), &MemDC, ptLeftTop.x, ptLeftTop.y, SRCCOPY);//화면 출력. MemDC.SelectObject(pOldBmp); return TRUE; // return CWnd::OnEraseBkgnd(pDC); }

위의 코드대로 입력된 출력화면은 다음과 같다.



이런 식으로 자식 윈도우가 출력되면서 앞에서 부모 윈도우의 출력을 가리게 된다.

이 코드에서 리턴 트루 위쪽의 코드를 모두 주석화 하면.

아래와 같이 가리는 출력이 아닌 겹치는 출력이 된다.



파일 업로드



팁 : 해당 코드를 찾고싶을때는 클래스 뷰 를 사용해서 해당 함수를 찾는것이 빠르다.
저작자 표시 비영리
신고
Posted by 졸가메
1. 외형구도. 메뉴와 도구 모음 간의 연동.

2. 버튼 클릭시 다른 메뉴 버튼 비활성화 처리를 IF문으로 해보려다가  실패.

3. 타 프로그램의 소스를 보며 IF문이 아닌 int값과 switch 문 등을 이용하는것을 발견.

목표를 그대로 따라하여 작성하는 식으로 공부할 예정.


완성화면은 저러함.

보다시피 지금 목표로 삼는 예제도 기본적 MFC 프로그램 에다가 덧붙여서 만든것을 알 수 있음.

[도움말 이후에 메뉴를 넣었다던가. 하는 것들.]

목표 파일 업로드.[웹에거 검색한 것. 비영리 목적으로 퍼왔으니 저작권 문제는 없다고 봄..]


그나저나 매번 느끼는거지만 무료로 배포하려고 블로그에 올리는 사람들이 있는 반면

만얼마~2만 얼마까지 돈받고 파는 소스도 있음. 개인적인 생각으론 전혀 돈내고 살 필요가 없다고 봄.

올리는 이유중 하나는 그렇게 구매 하지 않아도 되기에 조금이라도 검색하는 사람들이 받아갔으면 하는 생각에.

그나저나 지껏도 아닌데 존내 생색내네! 라면 할말없음. ㅎ!

여튼 코드파일 업로드.

코드파일 은 역시 2008로 변환한 파일들이며 속에 있는 ReadMe.txt 도 포함되어있으므로 읽어보는게 도움이 될듯.





저작자 표시 비영리
신고

'공부' 카테고리의 다른 글

그림판 만들기 프로젝트.  (2) 2011.12.09
흔한 비쥬얼 스튜디오 2008 의 팁  (0) 2011.11.28
xml이란?  (0) 2009.07.13
데이타 베이스 시험공부!  (0) 2009.04.24
오늘의 프로그래밍 언어 이론 과제  (0) 2009.03.12
Posted by 졸가메
지금까지 사용한 메뉴 코드는 내부적으로 TrackPopupMenu()함수를 가지고 만들어진 것이라고 한다.

이 함수의 원형은 다음과 같다.

 BOOL TrackPopupMenu(

UINT nFlags,        //메뉴 윈도우의 위치, 에니메이션효과 등 동작특성을 결정

int x, int y,           //출력할 좌표 **반드시 스크린 기준
CWnd* pWnd,      // WM_COMMAND 메시지를 전송할 윈도우 객체의 주소.

LPCRECT lpRect = NULL    //이값은 현재 사용하지않음.

);


다음으로는 속성값들을 정리한 내용이다

더보기



마우스 우클릭으로 메뉴 띄우기.

CCommandDemoView 에 OnRButtonDown 메시지 핸들러를 추가하고 다음과 같이 입력한다.


void CCommandDemoView::OnRButtonDown(UINT nFlags, CPoint point)
{

CMenu* pMainMenu = AfxGetMainWnd()->GetMenu();
//CMainFrame 윈도우의 메뉴에 대한 포인터를 가져온다.
CMenu* pSubMenu = pMainMenu->GetSubMenu(4);
//메인 메뉴중에서 4번 인덱스의 서브메뉴에 대한 포인터를 가져온다.

CPoint ptMenu = point;//스크린 기준으로 좌표 변환.
ClientToScreen(&ptMenu);

pSubMenu->TrackPopupMenu(TPM_LEFTBUTTON | TPM_LEFTALIGN, ptMenu.x, ptMenu.y, AfxGetMainWnd());
//WM_COMMAND 메시지가 메인 프레임 윈도우에 전달되도록 팝업 메뉴 윈도우를 출력한다.
//속성은 보다시피 왼클릭으로 내부 메뉴 클릭  지정 위치[마우스의 위치] 의 좌측정렬.
CView::OnRButtonDown(nFlags, point);
}

출력시 화면 


화면에 서브메뉴 라는게 추가되어있는 이유는 그냥 추가했기 때문.

다른 메뉴를 만들듯이 만들면 된다.

특이한점은 서브 메뉴 와 마찬가지로 하위 메뉴가 있는 메뉴의 경우

ID가 없다는 것.[클릭 자체가 되지 않기 때문이다.]

그러니까 그림으로 표현하면 이렇게된다.



이런 식으로 이해하면 편하다.





 

저작자 표시 비영리
신고
Posted by 졸가메
다른 메뉴를 만들듯이

일시 정지 메뉴를 만들고

일시정지 메뉴를 만들기 전에 구분선을 하나 넣어본다.[생성시에 메뉴에 다 한글로 써있으니 설명이 필요치 않다.]




m_bRunning 을 넣었듯이 m_bPause 도 넣어준다.

역시 마찬가지로 시작 종료 메뉴를 만든것처럼 

void CMainFrame::OnMenuPause()와

void CMainFrame::OnUpdateMenuPause(CCmdUI *pCmdUI)

를 추가하고 다음과 같이 입력한다.




==========================================================
void CMainFrame::OnMenuPause()
{
if(m_bPause) AfxMessageBox(_T("다시 시작합니다."));
else AfxMessageBox(_T("일시 정지합니다."));


m_bPause = !m_bPause;
}

void CMainFrame::OnUpdateMenuPause(CCmdUI *pCmdUI)
{
if(m_bPause) pCmdUI->SetText(_T("다시 시작"));
else pCmdUI->SetText(_T("일시 정지"));
}
==========================================================






클릭할때마다 메시지가 뜨고 다시 시작과 일시 정지가 토글된다.

여기서 체크 메뉴 방식으로 바꾸는 방법인 SetCheck() 함수를 이용하면 다음과 같이 만들 수 있다.


void CMainFrame::OnUpdateMenuPause(CCmdUI *pCmdUI)
{
// if(m_bPause) pCmdUI->SetText(_T("다시 시작"));
// else pCmdUI->SetText(_T("일시 정지"));
if(m_bPause) pCmdUI->SetCheck(TRUE);
else pCmdUI->SetCheck(FALSE);

}


이런식으로 하면 다시 시작 대신 일시 정지의 왼측에 체크마크가 생기게 된다.

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

단축키는 어떻게 만드는가. 

상태표시줄 처럼 만드는 것인가?      No.

상태 표시줄 만들듯이 만들면 메뉴에 쓰이는 것만 바뀌게됨.

물론 이 방법대로 해서 어느 단축키를 눌러야 해당 메뉴의 단축키인지를 알리기는 해야함.

단축키를 만드는 방법은 액셀레이터 테이블 에서 정의하여 만듬.

가는 방법은 다음과 같음.

 
 마지막 줄의 Menu_Pause가 없는게 당연하다.  가장 아랫줄의 빈 칸을 눌러서 새로운 단축키를 생성해야 한다.

 파일 업로드.

저작자 표시 비영리
신고
Posted by 졸가메