유튜브에 무료 동영상 강의를 업로드하였습니다.
MyRegDialog.h
등록 대화상자와 매핑하는 클래스는 MyRegDialog로 정할게요.
class MyRegDialog : public CDialogEx
이전 글에서 메인 대화상자는 등록 대화상자의 종료를 알기 위해 이벤트 핸들러를 등록하였습니다.
등록 대화상자에는 이를 기억할 멤버를 캡슐화합니다.
EndDialogEventHandler* eeh;
종료 이벤트 핸들러를 등록하는 메서드로 캡슐화합니다.
public:
void AddEndedEventHandler(EndDialogEventHandler *eeh);
OnCancel 메서드를 재정의합시다.
virtual void OnCancel();
컨트롤과 매핑하는 변수를 멤버로 캡슐화합니다.
int bno;
CString title;
CString content;
CString image;
CDateTimeCtrl dtp;
CMonthCalCtrl mc;
COleDateTime date1; //Date-Time Control과 매핑
COleDateTime date2; //Calendar Control과 매핑
CStatic pic_con;
CEdit edit_cont;
세 개의 버튼과 Date-Timer 컨트롤, Calendar 컨트롤을 선택 변경하였을 때 처리기를 캡슐화합니다.
afx_msg void OnDtnDatetimechangeDatetimepicker1(NMHDR* pNMHDR, LRESULT* pResult);
afx_msg void OnMcnSelchangeMonthcalendar1(NMHDR* pNMHDR, LRESULT* pResult);
afx_msg void OnBnClickedButtonImg();
afx_msg void OnBnClickedButtonSub();
afx_msg void OnBnClickedButtonCancel();
OnInitDialog와 PreTranslateMessage를 재정의합니다.
virtual BOOL OnInitDialog();
virtual BOOL PreTranslateMessage(MSG* pMsg);
컨트롤 상태를 초기화하는 메시지와 Picture Control에 이미지를 설정하는 메서드를 캡슐화합시다. 이 두 개의 메서드는 다른 클래스에서 호출해서 사용하지 않습니다.
private:
void InitControls();
void SetImage();
다음은 MyRegDialog.h 전체 소스 코드 내용입니다.
#pragma once
#include "afxdialogex.h"
// MyRegDialog 대화 상자
#include "EndDialogEventHandler.h"
class MyRegDialog : public CDialogEx
{
EndDialogEventHandler* eeh;
DECLARE_DYNAMIC(MyRegDialog)
public:
MyRegDialog(CWnd* pParent = nullptr); // 표준 생성자입니다.
virtual ~MyRegDialog();
// 대화 상자 데이터입니다.
#ifdef AFX_DESIGN_TIME
enum { IDD = IDD_DIALOG_REG };
#endif
public:
void AddEndedEventHandler(EndDialogEventHandler *eeh);
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 지원입니다.
DECLARE_MESSAGE_MAP()
virtual void OnCancel();
private:
int bno;
CString title;
CString content;
CString image;
CDateTimeCtrl dtp;
CMonthCalCtrl mc;
COleDateTime date1;
COleDateTime date2;
CStatic pic_con;
CEdit edit_cont;
public:
afx_msg void OnDtnDatetimechangeDatetimepicker1(NMHDR* pNMHDR, LRESULT* pResult);
afx_msg void OnMcnSelchangeMonthcalendar1(NMHDR* pNMHDR, LRESULT* pResult);
afx_msg void OnBnClickedButtonImg();
afx_msg void OnBnClickedButtonSub();
afx_msg void OnBnClickedButtonCancel();
public:
virtual BOOL OnInitDialog();
virtual BOOL PreTranslateMessage(MSG* pMsg);
private:
void InitControls();
void SetImage();
};
MyRegDialog.cpp
생성자에서 종료 이벤트 핸들러를 기억하는 eeh 멤버를 0으로 초기화합니다.
MyRegDialog::MyRegDialog(CWnd* pParent /*=nullptr*/)
: CDialogEx(IDD_DIALOG_REG, pParent)
, title(_T(""))
, content(_T(""))
, bno(0)
, date1(COleDateTime::GetCurrentTime())
, date2(COleDateTime::GetCurrentTime())
{
eeh = 0;
}
종료 이벤트 핸들러를 등록하는 메서드를 정의합시다.
void MyRegDialog::AddEndedEventHandler(EndDialogEventHandler* eeh)
{
this->eeh = eeh;
}
OnCancel 메서드에서는 종료 이벤트 핸들러가 있으면 호출합니다.
void MyRegDialog::OnCancel()
{
// TODO: 여기에 특수화된 코드를 추가 및/또는 기본 클래스를 호출합니다.
if (eeh)
{
eeh->EndedDialog();//나 죽음을 통보한다.
}
CDialogEx::OnCancel();
}
Date-Time Control과 Calendar Control의 선택 변경 시 서로 동기화해 줍시다.
void MyRegDialog::OnDtnDatetimechangeDatetimepicker1(NMHDR* pNMHDR, LRESULT* pResult)
{
LPNMDATETIMECHANGE pDTChange = reinterpret_cast<LPNMDATETIMECHANGE>(pNMHDR);
// TODO: 여기에 컨트롤 알림 처리기 코드를 추가합니다.
*pResult = 0;
UpdateData();
mc.SetCurSel(date1);
}
void MyRegDialog::OnMcnSelchangeMonthcalendar1(NMHDR* pNMHDR, LRESULT* pResult)
{
LPNMSELCHANGE pSelChange = reinterpret_cast<LPNMSELCHANGE>(pNMHDR);
// TODO: 여기에 컨트롤 알림 처리기 코드를 추가합니다.
*pResult = 0;
UpdateData();
dtp.SetTime(date2);
}
이미지 버튼을 클릭하면 파일 대화상자를 열어서 선택한 경로를 image에 설정하여 이미지 설정 메서드를 호출합니다.
void MyRegDialog::OnBnClickedButtonImg()
{
// TODO: 여기에 컨트롤 알림 처리기 코드를 추가합니다.
CFileDialog* fod = new CFileDialog(TRUE, 0);
INT_PTR re = fod->DoModal();
if (re != IDOK)
{
return;
}
image = fod->GetPathName();
SetImage();
}
이미지 설정 메서드에서는 경로로 CImage 개체를 Load합니다.
예외가 발생하면 미리 약속한 디폴트 이미지를 로딩합니다.
Picture Control의 DC를 얻어온 후 StretchBlt를 이용하여 컨트롤의 크기에 맞게 복사합니다.
void MyRegDialog::SetImage()
{
CImage cimg;
try
{
cimg.Load(image);
}
catch (...)
{
image = TEXT("noimage.bmp");
cimg.Load(image);
}
CDC* cdc = pic_con.GetDC();
RECT rt;
pic_con.GetClientRect(&rt);
cimg.StretchBlt(*cdc, 0, 0, rt.right, rt.bottom, SRCCOPY);
}
제출 버튼을 클릭하였을 때 이미지가 없으면 image를 디폴트 이미지로 설정합니다.
Book Manager 개체를 통해 도서 추가를 요청한 후 컨트롤을 초기 상태로 설정합니다.
void MyRegDialog::OnBnClickedButtonSub()
{
// TODO: 여기에 컨트롤 알림 처리기 코드를 추가합니다.
UpdateData();//컨트롤=>변수
if (image == TEXT(""))
{
image = TEXT("noimage.bmp");
}
BookManager& bm = BookManager::GetBookManager();
if (bm.AddBook(title, content, image, date1) == FALSE)
{
MessageBox(TEXT("추가 실패"));
}
InitControls();
}
컨트롤 초기화에서는 컨트롤과 매핑한 멤버 필드를 초기화하는 작업과 다음에 부여할 도서 번호를 얻어와서 설정하는 부분을 수행합니다.
void MyRegDialog::InitControls()
{
title = TEXT("");
content = TEXT("");
image = TEXT("noimage.bmp");
SetImage();
UpdateData(FALSE);//변수=>컨트롤
BookManager& bm = BookManager::GetBookManager();
int bno = bm.GetNextBookNo();
SetDlgItemInt(IDC_STATIC_BNO, bno, FALSE);
}
OnInitDialog 메서드에서는 컨트롤 초기화 메서드를 호출합니다.
BOOL MyRegDialog::OnInitDialog()
{
CDialogEx::OnInitDialog();
// TODO: 여기에 추가 초기화 작업을 추가합니다.
InitControls();
return TRUE; // return TRUE unless you set the focus to a control
// 예외: OCX 속성 페이지는 FALSE를 반환해야 합니다.
}
PreTranslateMessage 메서드를 정의합시다.
여기에서 이 메서드를 재정의하는 것은 edit 컨트롤에서 엔터 키를 수용하기 위해서입니다.
메시지가 WM_KEYDOWN이고 wParam이 VK_RETURN일 때 현재 포커스가 Edit 컨트롤에 있다면 마지막 위치에 개행을 추가해 줍니다.
BOOL MyRegDialog::PreTranslateMessage(MSG* pMsg)
{
// TODO: 여기에 특수화된 코드를 추가 및/또는 기본 클래스를 호출합니다.
if (pMsg->message == WM_KEYDOWN && pMsg->wParam == VK_RETURN)
{
if (GetFocus() == GetDlgItem(IDC_EDIT_CONTENT))
{
int len = edit_cont.GetWindowTextLength();
edit_cont.SetSel(len,len);
edit_cont.ReplaceSel(TEXT("\r\n"));
}
}
return CDialogEx::PreTranslateMessage(pMsg);
}
Cancel버튼 클릭 처리기에서는 컨트롤 상태를 초기화시킵니다.
void MyRegDialog::OnBnClickedButtonCancel()
{
// TODO: 여기에 컨트롤 알림 처리기 코드를 추가합니다.
InitControls();
}
다음은 MyRegDialog.cpp 전체 소스 코드 내용입니다.
// MyRegDialog.cpp: 구현 파일
//
#include "pch.h"
#include "도서 관리 프로그램.h"
#include "afxdialogex.h"
#include "MyRegDialog.h"
#include "BookManager.h"
// MyRegDialog 대화 상자
IMPLEMENT_DYNAMIC(MyRegDialog, CDialogEx)
MyRegDialog::MyRegDialog(CWnd* pParent /*=nullptr*/)
: CDialogEx(IDD_DIALOG_REG, pParent)
, title(_T(""))
, content(_T(""))
, bno(0)
, date1(COleDateTime::GetCurrentTime())
, date2(COleDateTime::GetCurrentTime())
{
eeh = 0;
}
MyRegDialog::~MyRegDialog()
{
}
void MyRegDialog::DoDataExchange(CDataExchange* pDX)
{
CDialogEx::DoDataExchange(pDX);
DDX_Control(pDX, IDC_DATETIMEPICKER1, dtp);
DDX_Control(pDX, IDC_MONTHCALENDAR1, mc);
DDX_Text(pDX, IDC_EDIT_TITLE, title);
DDX_Text(pDX, IDC_EDIT_CONTENT, content);
DDX_Text(pDX, IDC_STATIC_BNO, bno);
DDX_DateTimeCtrl(pDX, IDC_DATETIMEPICKER1, date1);
DDX_MonthCalCtrl(pDX, IDC_MONTHCALENDAR1, date2);
DDX_Control(pDX, IDC_STATIC_IMG, pic_con);
DDX_Control(pDX, IDC_EDIT_CONTENT, edit_cont);
}
BEGIN_MESSAGE_MAP(MyRegDialog, CDialogEx)
ON_NOTIFY(DTN_DATETIMECHANGE, IDC_DATETIMEPICKER1, &MyRegDialog::OnDtnDatetimechangeDatetimepicker1)
ON_NOTIFY(MCN_SELCHANGE, IDC_MONTHCALENDAR1, &MyRegDialog::OnMcnSelchangeMonthcalendar1)
ON_BN_CLICKED(IDC_BUTTON_IMG, &MyRegDialog::OnBnClickedButtonImg)
ON_BN_CLICKED(IDC_BUTTON_SUB, &MyRegDialog::OnBnClickedButtonSub)
ON_BN_CLICKED(IDC_BUTTON_CANCEL, &MyRegDialog::OnBnClickedButtonCancel)
END_MESSAGE_MAP()
// MyRegDialog 메시지 처리기
void MyRegDialog::AddEndedEventHandler(EndDialogEventHandler* eeh)
{
this->eeh = eeh;
}
void MyRegDialog::OnCancel()
{
// TODO: 여기에 특수화된 코드를 추가 및/또는 기본 클래스를 호출합니다.
if (eeh)
{
eeh->EndedDialog();//나 죽음을 통보한다.
}
CDialogEx::OnCancel();
}
void MyRegDialog::OnDtnDatetimechangeDatetimepicker1(NMHDR* pNMHDR, LRESULT* pResult)
{
LPNMDATETIMECHANGE pDTChange = reinterpret_cast<LPNMDATETIMECHANGE>(pNMHDR);
// TODO: 여기에 컨트롤 알림 처리기 코드를 추가합니다.
*pResult = 0;
UpdateData();
mc.SetCurSel(date1);
}
void MyRegDialog::OnMcnSelchangeMonthcalendar1(NMHDR* pNMHDR, LRESULT* pResult)
{
LPNMSELCHANGE pSelChange = reinterpret_cast<LPNMSELCHANGE>(pNMHDR);
// TODO: 여기에 컨트롤 알림 처리기 코드를 추가합니다.
*pResult = 0;
UpdateData();
dtp.SetTime(date2);
}
void MyRegDialog::OnBnClickedButtonImg()
{
// TODO: 여기에 컨트롤 알림 처리기 코드를 추가합니다.
CFileDialog* fod = new CFileDialog(TRUE, 0);
INT_PTR re = fod->DoModal();
if (re != IDOK)
{
return;
}
image = fod->GetPathName();
SetImage();
}
void MyRegDialog::SetImage()
{
CImage cimg;
try
{
cimg.Load(image);
}
catch (...)
{
image = TEXT("noimage.bmp");
cimg.Load(image);
}
CDC* cdc = pic_con.GetDC();
RECT rt;
pic_con.GetClientRect(&rt);
cimg.StretchBlt(*cdc, 0, 0, rt.right, rt.bottom, SRCCOPY);
}
void MyRegDialog::OnBnClickedButtonSub()
{
// TODO: 여기에 컨트롤 알림 처리기 코드를 추가합니다.
UpdateData();//컨트롤=>변수
if (image == TEXT(""))
{
image = TEXT("noimage.bmp");
}
BookManager& bm = BookManager::GetBookManager();
if (bm.AddBook(title, content, image, date1) == FALSE)
{
MessageBox(TEXT("추가 실패"));
}
InitControls();
}
void MyRegDialog::InitControls()
{
title = TEXT("");
content = TEXT("");
image = TEXT("noimage.bmp");
SetImage();
UpdateData(FALSE);//변수=>컨트롤
BookManager& bm = BookManager::GetBookManager();
int bno = bm.GetNextBookNo();
SetDlgItemInt(IDC_STATIC_BNO, bno, FALSE);
}
BOOL MyRegDialog::OnInitDialog()
{
CDialogEx::OnInitDialog();
// TODO: 여기에 추가 초기화 작업을 추가합니다.
InitControls();
return TRUE; // return TRUE unless you set the focus to a control
// 예외: OCX 속성 페이지는 FALSE를 반환해야 합니다.
}
BOOL MyRegDialog::PreTranslateMessage(MSG* pMsg)
{
// TODO: 여기에 특수화된 코드를 추가 및/또는 기본 클래스를 호출합니다.
if (pMsg->message == WM_KEYDOWN && pMsg->wParam == VK_RETURN)
{
if (GetFocus() == GetDlgItem(IDC_EDIT_CONTENT))
{
int len = edit_cont.GetWindowTextLength();
edit_cont.SetSel(len,len);
edit_cont.ReplaceSel(TEXT("\r\n"));
}
}
return CDialogEx::PreTranslateMessage(pMsg);
}
void MyRegDialog::OnBnClickedButtonCancel()
{
// TODO: 여기에 컨트롤 알림 처리기 코드를 추가합니다.
InitControls();
}