[MFC] 창 커스텀 기본

spring·2020년 11월 9일
0

###Frame
아래의 함수들은 CFrameWnd 또는 CFrameWndEx를 상속받은 클래스의 메소드에서 이루어 진다.
######1. 시스템 버튼[최소화/최대화/닫기]
OnCreate 에서 아래의 함수 호출.

this->ModifyStyle(WS_SYSMENU, 0);

######2. 메뉴 제거
PreCreateWindow에 아래 코드 추가

BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs){  
    /*...*/
    if (cs.hMenu!=NULL) { 
       ::DestroyMenu(cs.hMenu);
       cs.hMenu = NULL;
    }
     return TRUE;
}

######3.프레임 덧칠하기
OnNcPaint 에서 테두리의 영역을 정의해 색칠한다.
아래의 그림은 윈도우 프레임영역(CFrameWnd::OnNcPaint()가 그려야 하는
영역, 이 함수의 CWindowDC는 사실 모든 윈도우 영역을 그릴수 있음.)

void CMainFrame::OnNcPaint() {
	CRect rect_window;
	this->GetWindowRect(&rect_window);
	rect_window.NormalizeRect();
	rect_window.OffsetRect(-rect_window.left, -rect_window.top);
	//Fixed value
	CBrush brush;
	brush.CreateSolidBrush(RGB(127, 127, 127));
	
	CRect rect_window_top = rect_window;
	CRect rect_window_bottom = rect_window;
	CRect rect_window_left = rect_window;
	CRect rect_window_right = rect_window;
	rect_window_top.bottom = GetSystemMetrics(SM_CYCAPTION) + GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYSIZEFRAME);
	rect_window_bottom.top = rect_window_bottom.bottom - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYSIZEFRAME);
	rect_window_left.right = GetSystemMetrics(SM_CXFRAME) + GetSystemMetrics(SM_CXSIZEFRAME);
	rect_window_right.left = rect_window_right.right - GetSystemMetrics(SM_CXFRAME) - GetSystemMetrics(SM_CXSIZEFRAME);

	ncpaint->FillRect(rect_window_top,&brush);
	ncpaint->FillRect(rect_window_bottom, &brush);
	ncpaint->FillRect(rect_window_left, &brush);
	ncpaint->FillRect(rect_window_right, &brush);
}

이 함수를 사용하려면 OnNcActivate 함수에서 무조건 호출해줘야 한다.

BOOL CMainFrame::OnNcActivate(BOOL bActive) {
	this->OnNcPaint();
	return TRUE;
}

######4. 둥근 테두리 제거
위의 방식으로 색을 덧칠하면 이상하게 윈도우 윗부분만 둥글다(Window7의 프레임을 덧칠한것이기 때문)

이를 해결하고자 OnSize 메소드에서 SetWindowRgn 함수를 호출한다.
마지막 두 인자를 0으로 주면 직사각형의 창이 생성된다.

void CMainFrame::OnSize(UINT nType, int cx, int cy) {
	CFrameWnd::OnSize(nType, cx, cy);
	CRect rect_window;
	this->GetWindowRect(&rect_window);
	rect_window.NormalizeRect();
	rect_window.OffsetRect(-rect_window.left, -rect_window.top);

	CRgn rgn;
	rgn.CreateRoundRectRgn(0, 0, rect_window.Width(), rect_window.Height(),10,10);
	this->SetWindowRgn(static_cast<HRGN>(rgn.GetSafeHandle()), TRUE);
}

######6. 시스템 버튼 그리기
확장성과 종속성방지를 위해 리소스에 있는 비트맵을 사용한다.
역시 OnNcPaint 함수에서 그린다.

std::vector<CRect> m_sysbtn;  //추후에 상자 정보를 알기위해 멤버변수로 정의.
void CMainFrame::OnNcPaint() {
	//프레임 그리기 생략
	std::vector<UINT> bitmap_resource = {IDB_FRAME_CLOSE,IDB_FRAME_MAXIMIZE,IDB_FRAME_MINIMIZE};
	int padding = 4;
	CSize btn_sz(rect_window_top.Height() - padding * 2, rect_window_top.Height() - padding * 2);
	CPoint btn_pt(rect_window.right - rect_window_right.Width()-btn_sz.cx,
				   rect_window.top + padding);
	this->m_sysbtn.clear();
	CDC cdc;
	cdc.CreateCompatibleDC(ncpaint);
	for (size_t i = 0; i < bitmap_resource.size(); i++) {
		BITMAP bmp;
		CBitmap cbmp, *old_cbmp = nullptr;
		cbmp.LoadBitmap(bitmap_resource[i]);
		cbmp.GetBitmap(&bmp);
		old_cbmp = cdc.SelectObject(&cbmp);
		ncpaint->TransparentBlt(btn_pt.x, btn_pt.y,btn_sz.cx ,btn_sz.cy, &cdc, 0, 0,bmp.bmWidth,bmp.bmHeight, RGB(255, 255, 255));

		this->m_sysbtn.push_back(CRect(btn_pt.x,btn_pt.y,btn_pt.x+btn_sz.cx,btn_pt.y+btn_sz.cy));
		btn_pt.x -= btn_sz.cx +5;
		
		cdc.SelectObject(old_cbmp);
	}
}

######7. 마우스 추적 활성화
이 함수는 마우스가 창 밖을 나갔을때 호출된다. Hover된 버튼을 해지하거나 할때 사용된다.

void CMainFrame::OnNcMouseMove(UINT nHitTest, CPoint point) {
	// TODO: 여기에 메시지 처리기 코드를 추가 및/또는 기본값을 호출합니다.
	TRACKMOUSEEVENT tme = { 0 };
	tme.cbSize = sizeof(tme);
	tme.hwndTrack = m_hWnd;
	tme.dwFlags = TME_LEAVE | TME_NONCLIENT;
	tme.dwHoverTime = 0;
	TrackMouseEvent(&tme);
	CFrameWnd::OnNcMouseMove(nHitTest, point);
}
void CMainFrame::OnNcMouseLeave() {
	//Hover 기능의 해지 등등...
	CFrameWnd::OnNcMouseLeave();
}

https://stackoverflow.com/questions/521147/the-curious-problem-of-the-missing-wm-nclbuttonup-message-when-a-window-isnt-ma

profile
Researcher & Developer @ NAVER Corp | Designer @ HONGIK Univ.

0개의 댓글