라디오버튼 만들기
#define ID_BUTTON1 100
#define ID_RADIO1 101
#define ID_RADIO2 102
#define ID_RADIO3 103
#define ID_RADIO4 104
#define ID_RADIO5 105
#define ID_RADIO6 106
HINSTANCE g_Inst;
LRESULT CALLBACK MessageHandler(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
//윈도우 창이 생성될때 메세지
case WM_CREATE:
//프로그램 이름, 버튼 이름, 스타일 (자식형태|or연산으로 합침)
CreateWindow(L"button", L"Test", WS_CHILD | WS_VISIBLE, 5, 5, 120, 60, hWnd, (HMENU)ID_BUTTON1, g_Inst,NULL);
CreateWindow(L"button", L"Group1-1", BS_AUTORADIOBUTTON |WS_CHILD | WS_VISIBLE, 5, 70, 120, 30, hWnd, (HMENU)ID_RADIO1, g_Inst, NULL);
CreateWindow(L"button", L"Group1-2", BS_AUTORADIOBUTTON | WS_CHILD | WS_VISIBLE, 5, 100, 120, 30, hWnd, (HMENU)ID_RADIO2, g_Inst, NULL);
CreateWindow(L"button", L"Group1-3", BS_AUTORADIOBUTTON | WS_CHILD | WS_VISIBLE, 5, 130, 120, 30, hWnd, (HMENU)ID_RADIO3, g_Inst, NULL);
CreateWindow(L"button", L"Group2-1", BS_AUTORADIOBUTTON | WS_CHILD | WS_VISIBLE, 150, 70, 120, 30, hWnd, (HMENU)ID_RADIO4, g_Inst, NULL);
CreateWindow(L"button", L"Group2-2", BS_AUTORADIOBUTTON | WS_CHILD | WS_VISIBLE, 150, 100, 120, 30, hWnd, (HMENU)ID_RADIO5, g_Inst, NULL);
CreateWindow(L"button", L"Group2-3", BS_AUTORADIOBUTTON | WS_CHILD | WS_VISIBLE, 150, 130, 120, 30, hWnd, (HMENU)ID_RADIO6, g_Inst, NULL);
break;
라디오 버튼 또한 CreateWindow를 통해 만들어 준다.
다만 어제 만든 버튼과는 다르게 BS_AUTORADIOBUTTON 이라는 스타일도 적용 해주어야한다.
이렇게 잘 동작 하지만 문제가 하나 있다.
클릭했을때 1그룹, 2그룹 따로따로 선택하게 하고 싶은데 이 상태로는 그게 안된다는 것
case WM_CREATE:
//프로그램 이름, 버튼 이름, 스타일 (자식형태|or연산으로 합침)
CreateWindow(L"button", L"Test", WS_CHILD | WS_VISIBLE, 5, 5, 120, 60, hWnd, (HMENU)ID_BUTTON1, g_Inst,NULL);
CreateWindow(L"button", L"Group1-1", BS_AUTORADIOBUTTON |WS_CHILD | WS_VISIBLE | WS_GROUP, 5, 70, 120, 30, hWnd, (HMENU)ID_RADIO1, g_Inst, NULL);
CreateWindow(L"button", L"Group1-2", BS_AUTORADIOBUTTON | WS_CHILD | WS_VISIBLE, 5, 100, 120, 30, hWnd, (HMENU)ID_RADIO2, g_Inst, NULL);
CreateWindow(L"button", L"Group1-3", BS_AUTORADIOBUTTON | WS_CHILD | WS_VISIBLE, 5, 130, 120, 30, hWnd, (HMENU)ID_RADIO3, g_Inst, NULL);
CreateWindow(L"button", L"Group2-1", BS_AUTORADIOBUTTON | WS_CHILD | WS_VISIBLE | WS_GROUP, 150, 70, 120, 30, hWnd, (HMENU)ID_RADIO4, g_Inst, NULL);
CreateWindow(L"button", L"Group2-2", BS_AUTORADIOBUTTON | WS_CHILD | WS_VISIBLE, 150, 100, 120, 30, hWnd, (HMENU)ID_RADIO5, g_Inst, NULL);
CreateWindow(L"button", L"Group2-3", BS_AUTORADIOBUTTON | WS_CHILD | WS_VISIBLE, 150, 130, 120, 30, hWnd, (HMENU)ID_RADIO6, g_Inst, NULL);
break;
답은 간단했다.
버튼 그룹의 첫번째 인스턴스를 생성할때 스타일에 WS_GROUP을 넣어주면 됐다.
이제 그룹별로 아주 잘된다.
그리고 디폴트값으로 항상 한 버튼이 눌려있는것을 원한다면
CreateWindow(L"button", L"Group1-1", BS_AUTORADIOBUTTON |WS_CHILD | WS_VISIBLE | WS_GROUP, 5, 70, 120, 30, hWnd, (HMENU)ID_RADIO1, g_Inst, NULL);
CreateWindow(L"button", L"Group1-2", BS_AUTORADIOBUTTON | WS_CHILD | WS_VISIBLE, 5, 100, 120, 30, hWnd, (HMENU)ID_RADIO2, g_Inst, NULL);
CreateWindow(L"button", L"Group1-3", BS_AUTORADIOBUTTON | WS_CHILD | WS_VISIBLE, 5, 130, 120, 30, hWnd, (HMENU)ID_RADIO3, g_Inst, NULL);
CheckRadioButton(hWnd, ID_RADIO1, ID_RADIO3, ID_RADIO1);
CheckRadioButton 함수로 설정을 해주면 된다.
난 첫번째 버튼과 3번째 버튼 사이에서 첫번째가 디폴트로 눌려있게 했다.
Edit칸과 콤보박스 만들기
CreateWindow(L"edit", NULL, WS_CHILD | WS_VISIBLE | WS_BORDER | ES_AUTOHSCROLL, 5, 170, 400, 300, hWnd, (HMENU)ID_EDIT, g_Inst, nullptr);
{
HWND hCombo = CreateWindow(L"combobox", NULL, WS_CHILD | WS_VISIBLE | CBS_DROPDOWN, 5, 480, 100, 200, hWnd, (HMENU)ID_COMBO, g_Inst, nullptr);
SendMessage(hCombo, CB_ADDSTRING, 0, (LPARAM)L"Menu1");
}
그냥 같은 함수를 써서 버튼과 같이 안의 인자값들만 다 넣어주면 되는데 인자값이 이것저것 너무나 많다..
콤보박스는 하위에 Menu1이라는 메뉴를 넣기위해 핸들러로 받아서 메세지를 넘겨주는 방식이다.
콤보 박스와 메뉴가 전부 다 뜬다.
팝업창 만들기
메뉴 리소스를 추가 했던거 처럼 리소스 추가 - 다이얼로그를 눌러준다.
그러면 이러한 창이 뜨는데, 오른쪽 도구상자에서 이것저것 드래그& 드랍으로 만들어 줄 수 있다.
난 버튼을 하나 가져와서 속성-캡션에서 버튼 이름을 바꿔주었다.
속성창을 찾을 수 없다면 창을 오른쪽 클릭 하면 띄울 수 있다.
메뉴를 눌러 팝업창 띄우기
아까만든 File메뉴의 project를 누르면 팝업창을 띄우게 해보자.
이 다이얼로그도 하나의 윈도우 창이기 때문에 메세지를 받아서 처리해야한다.
INT_PTR CALLBACK DlgHandler(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_CLOSE:
EndDialog(hWnd, IDCANCEL);
case WM_COMMAND:
switch (wParam)
{
case IDOK:
EndDialog(hWnd, IDOK);
break;
case IDCANCEL:
EndDialog(hWnd, IDCANCEL);
break;
}
break;
break;
}
return false;
}
이런식으로 다이얼로그의 메세지를 따로 처리하는 함수를 만들어 줘야한다.
IDOK와 IDCANCEL 은 각각 확인/취소 버튼을 눌렀을때 들어오는 wParam값인데, 다이얼로그를 종료시키도록 코드를 입력했다.
case WM_COMMAND:
switch (wParam)
{
case ID_BUTTON1:
//윈도우의 타이틀바에 글자를 출력
SetWindowText(hWnd, L"버튼 클릭");;
case ID_LOAD_PROJECT:
DialogBox(g_Inst, MAKEINTRESOURCE(IDD_DIALOG1), hWnd, nullptr);
break;
}
break;
그리고 아까 만들어둔 case문에 ID_LOAD_PROJECT를 wParam값으로 받으면 다이얼로그 박스를 실행시켜준다.
메뉴를 만들때와 똑같이 2번째 인자로 MAKEINTRESOURCE함수에 다이얼로그의 정의된 이름을 넣어주면 된다.
잘 작동한다.
팝업창을 띄울때는 모달과 모달리스가 있는데
모달은 위의 코드로 만들면 되고, 팝업창이 생성됐을때 뒤의 부모 프로그램을 제어 할 수 없게된다.
하지만 모달리스 방식은 반대로 뒤의 부모 프로그램을 제어 할 수 있지만 생성 코드가 다르다.
HWND hDlg = CreateDialog(g_Inst, MAKEINTRESOURCE(IDD_DIALOG1),hWnd, DlgHandler);
ShowWindow(hDlg,SW_SHOW);
이런식으로 다이얼로그의 인스턴스를 생성하고 핸들러에 값을 담은다음 ShowWindow함수 인자로 넘겨주면 된다.
윈도우 창에 비트맵 띄우기
아까 다이얼로그의 리소스를 추가 시켜줬던 것 처럼 Bitmap을 클릭후 가져오기를 누른다.
그럼 아까 다이얼로그가 뜬것처럼 가져온 비트맵 이미지가 뜬다.
이제 코드를 작성 해주자.
case WM_PAINT:
{
PAINTSTRUCT ps;
//비긴페인트,앤드페인트는 WM_PAINT에서만 처리 가능
HDC hDC = BeginPaint(hWnd, &ps);
//비트맵 리소스를 가져오고
HBITMAP Image = LoadBitmap(g_Inst, MAKEINTRESOURCE(IDB_BITMAP1));
BITMAP bitMap;
//가져온 비트맵 리소스로부터 비트맵 정보를 가져옴
GetObject(Image, sizeof(BITMAP), &bitMap);
//현재 DC로부터 호환되는 DC를 생성을하고
HDC imageDC = CreateCompatibleDC(hDC);
// 소스 dc를 만드는 과정..
SelectObject(imageDC, Image);
//카피해서 생성한다.
BitBlt(hDC, 0, 0, bitMap.bmWidth, bitMap.bmHeight, imageDC, 0, 0, SRCCOPY);
DeleteDC(imageDC);
EndPaint(hWnd, &ps);
}
사진 하나를 띄우려면 이런 프로세스가 필요 하다는데
처음 윈도우창 만들때처럼 이해가 안간다..
나중에 만들게 되면 파봐야 겠다.
화면에 그림이 잘 출력 되는것을 볼 수 있다.
페인트 할때마다 리소스를 가져오고 비트맵 정보를 가져오는것을 방지 하기 위해 코드를 수정했다.
HINSTANCE g_Inst = NULL;
HBITMAP Image;
BITMAP bitMap;
LRESULT CALLBACK MessageHandler(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch(uMsg)
{
case WM_CREATE:
//비트맵 리소스를 가져오고
Image = LoadBitmap(g_Inst, MAKEINTRESOURCE(IDB_BITMAP1));
//가져온 비트맵 리소스로부터 비트맵 정보를 가져옴
GetObject(Image, sizeof(BITMAP), &bitMap);
break;
case WM_PAINT:
{
PAINTSTRUCT ps;
//비긴페인트,앤드페인트는 WM_PAINT에서만 처리 가능
HDC hDC = BeginPaint(hWnd, &ps);
//현재 DC로부터 호환되는 DC를 생성을하고
HDC imageDC = CreateCompatibleDC(hDC);
// 소스 dc를 만드는 과정..
SelectObject(imageDC, Image);
//카피해서 생성한다.
BitBlt(hDC, 0, 0, bitMap.bmWidth, bitMap.bmHeight, imageDC, 0, 0, SRCCOPY);
DeleteDC(imageDC);
EndPaint(hWnd, &ps);
}
break;
case WM_CLOSE:
PostQuitMessage(0);
break;
}
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
HBITMAP과 BITMAP을 전역변수로 생성하고 창이 생성될때 리소스와 정보를 가져오도록 했다.
훨씬 더 깔끔해졌다.
움직이는 캐릭터 띄우기 #1
일단 위의 이미지를 가져와서 아까 비트맵을 띄웠던 과정과 똑같이 비트맵 리소스를 가져온다.
조금 편하게 작업하기 위해 AnimationSprite라는 클래스를 만들어 주었다.
#pragma once
#include <Windows.h>
class AnimationSprite
{
RECT myDrawRC;
HBITMAP Image;
BITMAP Bitmap;
public:
//생성자
AnimationSprite(HINSTANCE hInst,int intResource);
void Draw(const HDC& hDC);
};
필요한 변수들과 생성자와 함수를 만들고,
생성자를 오른쪽클릭해서 빠른 작업및 리팩토링 - 선언/정의 만들기로 들어간다.
#include "AnimationSprite.h"
AnimationSprite::AnimationSprite(HINSTANCE hInst,int intResource)
{
Image = LoadBitmap(hInst, MAKEINTRESOURCE(intResource));
//가져온 비트맵 리소스로부터 비트맵 정보를 가져옴
GetObject(Image, sizeof(BITMAP), &Bitmap);
myDrawRC.left = 0;
myDrawRC.top = 0;
myDrawRC.right = 54;
myDrawRC.bottom = 94;
}
void AnimationSprite::Draw(const HDC& hDC)
{
HDC imageDC = CreateCompatibleDC(hDC);
SelectObject(imageDC, Image);
BitBlt(hDC, 0, 0, myDrawRC.right - myDrawRC.left,myDrawRC.bottom - myDrawRC.top, imageDC, 0, 0, SRCCOPY);
DeleteDC(imageDC);
}
정의에서는 아까 winmain 메세지 프로시저에 전달하는 함수에서 만들었던 과정들을 생성자와 함수로 처리하기 위해 위와 같이 사이즈를 계산해서 만들어 주었다.
#include <Windows.h>
//스마트 포인터를 위해 메모리 헤더파일 인클루드
#include <memory>
#include "resource.h"
// 아까 만들어둔 클래스 헤더파일도 인클루드
#include "AnimationSprite.h"
HINSTANCE g_Inst = NULL;
//스마트 포인터로 AnimationSprite형 변수를 선언해준다.
std::shared_ptr<AnimationSprite> Character = nullptr;
LRESULT CALLBACK MessageHandler(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch(uMsg)
{
case WM_CREATE:
//아까만든 이미지를 가리키는 Shared_Ptr 생성
//정의해준 매개변수를 넣어준다.
Character = std::make_shared<AnimationSprite>(g_Inst, IDB_BITMAP3);
break;
case WM_PAINT:
{
PAINTSTRUCT ps;
//비긴페인트,앤드페인트는 WM_PAINT에서만 처리 가능
HDC hDC = BeginPaint(hWnd, &ps);
//캐릭터의 Draw함수 호출
Character->Draw(hDC);
EndPaint(hWnd, &ps);
}
break;
case WM_CLOSE:
PostQuitMessage(0);
break;
}
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
메인 스크립트도 위 주석의 설명에 따라 코드를 만들었다.
실행을 해보면 아까 캐릭터의 맨 앞의 모습이 잘려서 잘 나온다.
이번엔 저 보라색 값을 빼주자.
#include "AnimationSprite.h"
//라이브러리 추가
#pragma comment(lib,"Msimg32.lib")
AnimationSprite::AnimationSprite(HINSTANCE hInst,int intResource)
{
Image = LoadBitmap(hInst, MAKEINTRESOURCE(intResource));
//가져온 비트맵 리소스로부터 비트맵 정보를 가져옴
GetObject(Image, sizeof(BITMAP), &Bitmap);
myDrawRC.left = 0;
myDrawRC.top = 0;
myDrawRC.right = 54;
myDrawRC.bottom = 94;
}
void AnimationSprite::Draw(const HDC& hDC)
{
HDC imageDC = CreateCompatibleDC(hDC);
SelectObject(imageDC, Image);
//BitBlt(hDC, 0, 0, myDrawRC.right - myDrawRC.left,myDrawRC.bottom - myDrawRC.top, imageDC, 0, 0, SRCCOPY);
//RGB값을 빼주는 라이브러리 함수
TransparentBlt(hDC, 0, 0, myDrawRC.right - myDrawRC.left, myDrawRC.bottom - myDrawRC.top,
imageDC, 0, 0, myDrawRC.right, myDrawRC.bottom, RGB(255, 0, 255));
DeleteDC(imageDC);
}
라이브러리를 사용해야 한다.
라이브러리를 추가 시키고, TransparentBlt 이라는 함수를 사용해준다.
맨 마지막 보라색RGB값을 넣어주었다.
실행시켜보면 보라색 값이 잘 빠진것을 볼 수 있다.
마치며..
CPP에다 끝없는 WINAPI를 사용하다 보니 모르는것 투성이다.
대충 돌아가는 구조를 알겠지만 혼자 짜보라고 하면 한세월 걸릴것같다.
Direct X로 가는 길을 왜이리 멀고도 험한지..
Direct X로 가는여정 5일차 끝
'프로그래밍 공부 > C++ 프로그래밍' 카테고리의 다른 글
Direct X 7일차💻 DirectX의 시작 (0) | 2022.06.22 |
---|---|
Direct X 6일차💻 <WINAPI> #마지막 (0) | 2022.06.21 |
Direct X 4일차💻 <WINAPI> #2 (0) | 2022.06.19 |
WINAPI 추가정리 (0) | 2022.06.19 |
Direct X 3일차💻 <WINAPI> #1 (0) | 2022.06.19 |
포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!