도서 관리 프로그램 – 8. 변경 대화상자 정의[MFC]

변경 대화상자

유튜브에 무료 동영상 강의를 업로드하였습니다.

ModifyDialog.h

변경 대화상자는 CDialogEx 기반 클래스에서 파생합니다. 이 외에도 도서 추가 이벤트와 삭제 이벤트를 수신하기 위한 이벤트 핸들러를 기반으로 구현 약속합니다.

class ModifyDialog : public CDialogEx,
	public AddBookEventHandler,
	public RemoveBookEventHandler
public:
	virtual void AddedBook(Book* book);
	virtual void RemovedBook(int bno);

컨트롤과 매핑하는 변수를 캡슐화합니다.

private:
	CListCtrl list_book;
	CStatic pic_img;
	COleDateTime pubdate;
	CString content;
	CString image;
	CEdit edit_cont;

초기화 과정의 OnInitDialog 메서드를 재정의합니다.

	virtual BOOL OnInitDialog();

IDC_EDIT_CONTENT 컨트롤에서 엔터 키를 입력 처리하기 위해 PreTranslateMessage 메서드를 재정의합니다.

	virtual BOOL PreTranslateMessage(MSG* pMsg);

IDC_LIST_BOOK 선택항목 변경 처리기와 세 개의 버튼 클릭 처리기를 캡슐화합니다.

	afx_msg void OnLvnItemchangedListBook(NMHDR* pNMHDR, LRESULT* pResult);
	afx_msg void OnBnClickedButtonCancel();
	afx_msg void OnBnClickedButtonImg();
	afx_msg void OnBnClickedButtonSub();

다음은 ModifyDialog.h 파일의 전체 코드 내용입니다.

#pragma once
#include "afxdialogex.h"
#include "BookEventHandler.h"
#include "BookManager.h"
// ModifyDialog 대화 상자

class ModifyDialog : public CDialogEx,
	public AddBookEventHandler,
	public RemoveBookEventHandler
{
	DECLARE_DYNAMIC(ModifyDialog)

public:
	ModifyDialog(CWnd* pParent = nullptr);   // 표준 생성자입니다.
	virtual ~ModifyDialog();

// 대화 상자 데이터입니다.
#ifdef AFX_DESIGN_TIME
	enum { IDD = IDD_DIALOG_MODIFY };
#endif

protected:
	virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV 지원입니다.

	DECLARE_MESSAGE_MAP()
private:
	CListCtrl list_book;
	CStatic pic_img;
	COleDateTime pubdate;
	CString content;
	CString image;
	CEdit edit_cont;
public:
	virtual void AddedBook(Book* book);
	virtual void RemovedBook(int bno);
	virtual BOOL OnInitDialog();
	virtual BOOL PreTranslateMessage(MSG* pMsg);
	afx_msg void OnLvnItemchangedListBook(NMHDR* pNMHDR, LRESULT* pResult);
	afx_msg void OnBnClickedButtonCancel();
	afx_msg void OnBnClickedButtonImg();
	afx_msg void OnBnClickedButtonSub();
};

ModifyDialog.cpp

이제 소스 코드를 구현합시다.

AddedBook

도서 추가 이벤트를 수신하여 처리하는 이벤트 핸들러입니다.

추가한 도서 번호를 얻어온 후에 리스트에 항목 추가합니다.

void ModifyDialog::AddedBook(Book* book)
{
	int bno = book->GetNo();
	CString title = book->GetTitle();
	wchar_t nbuf[256] = TEXT("");
	wsprintf(nbuf, TEXT("%d"), bno);
	int i = list_book.GetItemCount();
	list_book.InsertItem(i, nbuf);
	list_book.SetItemText(i, 1, title);
}

RemovedBook

도서 삭제 이벤트를 수신하여 처리하는 이벤트 핸들러입니다.

IDC_LIST_BOOK 컨트롤에서 삭제한 도서 번호에 대응하는 항목를 제거합니다.

void ModifyDialog::RemovedBook(int bno)
{
	LVITEM li = { 0 };
	wchar_t buf[256] = TEXT("");
	int cnt = list_book.GetItemCount();
	for (int i = 0; i < cnt; i++)
	{
		li.iItem = i;
		li.iSubItem = 0;
		li.mask = LVIF_TEXT;
		li.pszText = buf;
		li.cchTextMax = 256;
		list_book.GetItem(&li);
		if (bno == _wtoi(buf))
		{
			list_book.DeleteItem(i);
			return;
		}
	}
}

OnInitDialog

IDC_LIST_BOOK 컨트롤에 도서 번호와 제목 항목을 추가합니다.

	list_book.InsertColumn(0, TEXT("No"), 0, 50);
	list_book.InsertColumn(1, TEXT("제목"), 0, 140);

도서 관리자 개체에 도서 추가 및 삭제 이벤트 핸들러를 등록합니다.

	BookManager& bm = BookManager::GetBookManager();
	bm.AddABEventHandler(this);
	bm.AddRBEventHandler(this);

전체 도서 번호 목록을 얻어온 후에 리스트에 항목 추가합니다.

	bm.GetBookNoList(&nlist);
	NIter seek = nlist.begin();
	NIter end = nlist.end();
	wchar_t nbuf[256]=TEXT("");
	Book* book = 0;
	for (int i=0; seek != end; ++seek,i++)
	{
		wsprintf(nbuf, TEXT("%d"), (*seek));
		list_book.InsertItem(i, nbuf);
		book = bm.GetBook(*seek);
		list_book.SetItemText(i, 1, book->GetTitle());
	}

다음은 OnInitDialog 메서드의 코드 내용입니다.

BOOL ModifyDialog::OnInitDialog()
{
	__super::OnInitDialog();

	// TODO:  여기에 추가 초기화 작업을 추가합니다.
	list_book.InsertColumn(0, TEXT("No"), 0, 50);
	list_book.InsertColumn(1, TEXT("제목"), 0, 140);
	BookManager& bm = BookManager::GetBookManager();
	bm.AddABEventHandler(this);
	bm.AddRBEventHandler(this);
	NList nlist;
	bm.GetBookNoList(&nlist);
	NIter seek = nlist.begin();
	NIter end = nlist.end();
	wchar_t nbuf[256]=TEXT("");
	Book* book = 0;
	for (int i=0; seek != end; ++seek,i++)
	{
		wsprintf(nbuf, TEXT("%d"), (*seek));
		list_book.InsertItem(i, nbuf);
		book = bm.GetBook(*seek);
		list_book.SetItemText(i, 1, book->GetTitle());
	}

	return TRUE;  // return TRUE unless you set the focus to a control
	// 예외: OCX 속성 페이지는 FALSE를 반환해야 합니다.
}

OnLvnItemchangedListBook와 OnBnClickedButtonCancel

IDC_LIST_BOOK 컨트롤 선택 항목 변경 처리기에서 할 작업과 취소 버튼 클릭 처리기에서 할 작업은 같습니다.

둘 다 IDC_LIST_BOOK 컨트롤의 선택 항목을 얻어와서 도서 정보를 컨트롤에 설정하는 작업입니다.

void ModifyDialog::OnLvnItemchangedListBook(NMHDR* pNMHDR, LRESULT* pResult)
{
	LPNMLISTVIEW pNMLV = reinterpret_cast<LPNMLISTVIEW>(pNMHDR);
	// TODO: 여기에 컨트롤 알림 처리기 코드를 추가합니다.
	*pResult = 0;
	OnBnClickedButtonCancel();
}


void ModifyDialog::OnBnClickedButtonCancel()
{
	// TODO: 여기에 컨트롤 알림 처리기 코드를 추가합니다.
	POSITION pos = list_book.GetFirstSelectedItemPosition();
	int index = list_book.GetNextSelectedItem(pos);
	if (index == -1)
	{
		return;
	}
	LVITEM li = { 0 };
	li.iItem = index;
	li.iSubItem = 0;
	wchar_t nbuf[256] = TEXT("");
	li.mask = LVIF_TEXT;
	li.pszText = nbuf;
	li.cchTextMax = 256;
	list_book.GetItem(&li);
	int no = _wtoi(nbuf);
	BookManager& bm = BookManager::GetBookManager();
	Book* book = bm.GetBook(no);
	content = book->GetContent();
	pubdate = book->GetPubdate();
	CImage cimg;
	image = book->GetImage();
	cimg.Load(image);
	CDC* cdc = pic_img.GetDC();
	RECT rt;
	pic_img.GetClientRect(&rt);
	cimg.StretchBlt(*cdc, 0, 0, rt.right, rt.bottom, SRCCOPY);
	UpdateData(0);
}

OnBnClickedButtonImg

이미지 버튼을 클릭하였을 때 처리기입니다.

파일 열기 대화상자를 열어서 선택한 이미지로 Picture Control에 StretchBlt 합니다.

void ModifyDialog::OnBnClickedButtonImg()
{
	// TODO: 여기에 컨트롤 알림 처리기 코드를 추가합니다.
	CFileDialog* fod = new CFileDialog(TRUE, 0);
	INT_PTR re = fod->DoModal();
	if (re != IDOK)
	{
		return;
	}
	image = fod->GetPathName();
	CImage cimg;
	cimg.Load(image);

	CDC* cdc = pic_img.GetDC();
	RECT rt;
	pic_img.GetClientRect(&rt);
	cimg.StretchBlt(*cdc, 0, 0, rt.right, rt.bottom, SRCCOPY);
}

OnBnClickedButtonSub

IDC_BUTTON_SUB 버튼 클릭 처리기입니다.

IDC_LIST_BOOK 컨트롤의 선택 항목을 얻어와서 해당 도서 번호를 알아냅니다.

그리고 컨트롤에 입력한 내용으로 도서 관리자에게 변경을 요청합니다.

void ModifyDialog::OnBnClickedButtonSub()
{
	// TODO: 여기에 컨트롤 알림 처리기 코드를 추가합니다.
	UpdateData();
	POSITION pos = list_book.GetFirstSelectedItemPosition();
	int index = list_book.GetNextSelectedItem(pos);
	if (index == -1)
	{
		return;
	}
	LVITEM li = { 0 };
	li.iItem = index;
	li.iSubItem = 0;
	wchar_t nbuf[256] = TEXT("");
	li.mask = LVIF_TEXT;
	li.pszText = nbuf;
	li.cchTextMax = 256;
	list_book.GetItem(&li);
	int no = _wtoi(nbuf);
	BookManager& bm = BookManager::GetBookManager();	
	bm.ModifyBook(no, content, image, pubdate);

}

PreTranslateMessage

IDC_EDIT_CONTENT 컨트롤이 엔터 키 입력을 처리하기 위해 재정의한 메서드입니다.

메시지가 WM_KEYDOWN이고 wParam이 VK_RETURN일 때 개행을 추가합니다.

BOOL ModifyDialog::PreTranslateMessage(MSG* pMsg)
{
	// TODO: 여기에 특수화된 코드를 추가 및/또는 기본 클래스를 호출합니다.
	if (pMsg->message == WM_KEYDOWN && pMsg->wParam == VK_RETURN)
	{
		int len = edit_cont.GetWindowTextLength();
		edit_cont.SetSel(len, len);
		edit_cont.ReplaceSel(TEXT("\r\n"));
	}
	return __super::PreTranslateMessage(pMsg);
}