본문 바로가기

MFC

[MFC] Edit(수정) 가능 ListCtrl(ListControl)


출처 :: http://mnlt.tistory.com/131




ListControl은 MFC에서 굉장히 유용
히 쓰이는 컨트롤입니다.
해당 컨트롤에서 직접 원하는 위치를 즉석 수정하기 위한 컨트롤을 만들었습니다.

아마 그냥 유용하게 쓰일듯 싶기도 해서 포스팅.

//EditListCtrl.h

#pragma once


// CEditListCtrl

class CEditListCtrl : public CListCtrl
{
	DECLARE_DYNAMIC(CEditListCtrl)

public:
	CEditListCtrl();
	virtual ~CEditListCtrl();

protected:
	DECLARE_MESSAGE_MAP()

public:
	virtual BOOL PreTranslateMessage(MSG* pMsg);
	afx_msg void OnNMClick(NMHDR *pNMHDR, LRESULT *pResult);

	// 초기화 함수.
	void Init(void);

	// CEditListCtrl 을 사용하는 Dialog 의 OnOK 함수가 호출될때 호출해야 한다.
	void OnOK(void);

	// List 값을 바꾼다.
	BOOL SetText(CString _val, int _row, int _col);

protected:
	// ListCtrl 을 클릭했을때 nItem 저장될 변수. (Row)
	int m_nItem;

	// ListCtrl 을 클릭했을때 nSubItem 저장될 변수 (Col);
	int m_nSubItem;

	// 현재 포커스가 어딘지 확인하는 변수
	HWND m_hFocus;

	bool m_bInit;

	HWND _GetEditHwnd(void);
	HWND _GetListHwnd(void);
	HWND _GetParentHwnd(void);
};

//EditListCtrl.cpp

// EditListCtrl.cpp : 구현 파일입니다.
//

#include "stdafx.h"
#include "TestApp.h"
#include "EditListCtrl.h"


// CEditListCtrl

IMPLEMENT_DYNAMIC(CEditListCtrl, CListCtrl)

CEditListCtrl::CEditListCtrl()
: m_nItem(0), m_nSubItem(0)
, m_bInit(false)
, m_hFocus((HWND)0)
{

}

CEditListCtrl::~CEditListCtrl()
{
}

BEGIN_MESSAGE_MAP(CEditListCtrl, CListCtrl)
	ON_NOTIFY_REFLECT(NM_CLICK, &CEditListCtrl::OnNMClick)
END_MESSAGE_MAP()




// CEditListCtrl 메시지 처리기입니다.

BOOL CEditListCtrl::PreTranslateMessage(MSG* pMsg)
{
	if(m_hFocus != (HWND)0)
	{
		HWND hFocus = ::GetFocus();

		// Edit 가 Focus를 잃으면.
		if(m_hFocus != hFocus)
		{
			OnOK();
		}
	}
	

	return CListCtrl::PreTranslateMessage(pMsg);
}

void CEditListCtrl::Init(void)
{
	if(m_bInit) return;

	SetExtendedStyle(LVS_EX_FULLROWSELECT | LVS_EX_GRIDLINES);
	::ShowWindow(_GetEditHwnd(), SW_HIDE);

	m_bInit = true;
}


// Return 키가 눌렸을때 호출하기 위한..
void CEditListCtrl::OnOK(void)
{
	CWnd* pWnd = GetFocus();

	if(_GetEditHwnd() == pWnd->GetSafeHwnd() ||
		_GetEditHwnd() == m_hFocus)
	{
		// EditCtrl로 부터 Text를 얻어온다.
		wchar_t cText[255];
		ZeroMemory(cText, 255);
		::GetWindowText(_GetEditHwnd(), cText, 255);
		CString cStrText(cText);

		// 얻어온 텍스트를 ListCtrl 에 적용시킨다
		if(SetText(cStrText, m_nItem, m_nSubItem) == FALSE)
			return;

		// EditCtrl의 Focus를 죽인다.
		::SendMessage(_GetEditHwnd(), WM_KILLFOCUS, 0, 0);
		::ShowWindow(_GetEditHwnd(), SW_HIDE);
		::SetFocus(pWnd->GetSafeHwnd());

		// ListCtrl의 SelectionMark를 지정한다.
		SetSelectionMark(m_nItem);
	}

	m_hFocus = (HWND)0;
}

BOOL CEditListCtrl::SetText(CString _val, int _row, int _col)
{
	return SetItemText(_row, _col, _val);
}

void CEditListCtrl::OnNMClick(NMHDR *pNMHDR, LRESULT *pResult)
{
	Invalidate();

	LPNMITEMACTIVATE pNMITEM = (LPNMITEMACTIVATE) pNMHDR;
	m_nItem = pNMITEM->iItem;
	m_nSubItem = pNMITEM->iSubItem;

	// 선택된 지점이 올바르지 않을시...
	if(m_nSubItem == 0 || m_nSubItem == -1 || m_nItem == -1)
		return;

	// 선택된 SubItem의 Text를 CEdit 에 넣어주기 위한 변수
	CString cStrText = GetItemText(m_nItem, m_nSubItem);

	CRect rtSubItem, rtListCtrl, rtDlg;
	// SubItem 의 Rect를 얻어온다.
	if(GetSubItemRect(pNMITEM->iItem, pNMITEM->iSubItem, LVIR_BOUNDS, rtSubItem) == FALSE)
		return;

	// ListControl 의 Window Bounds 를 얻어온다.
	::GetWindowRect(_GetListHwnd(), &rtListCtrl);

	// Parent Dialog 의 Windows Bounds 를 얻어온다.
	::GetWindowRect(_GetParentHwnd(), &rtDlg);

	// Dialog에 위치한 ListCtrl의 left & top 위치를 구한다.
	int nThisLeft = rtListCtrl.left - rtDlg.left;
	int nThisTop = rtListCtrl.top - rtDlg.top;

	// EditCtrl의 위치를 변경해준다.
	::SetWindowPos(
		_GetEditHwnd(), 
		HWND_TOP,
		nThisLeft + rtSubItem.left + 7,
		nThisTop + rtSubItem.top + 4,
		rtSubItem.Width() - 7,
		rtSubItem.Height() - 4,
		NULL);

	// EditCtrl Show
	::ShowWindow(_GetEditHwnd(), SW_SHOW);
	::SetFocus(_GetEditHwnd());

	// SubItem 위치에 외각선을 그려준다.
	::Rectangle(::GetDC(_GetListHwnd()), rtSubItem.left, rtSubItem.top - 1, rtSubItem.right, rtSubItem.bottom);

	// EditCtrl 에 선택한 List의 Text 를 넣어준다.
	::SetWindowText(_GetEditHwnd(), cStrText);

	// 삽입한 Text의 마지막에 포커스를 맞춰주고 Mark한다.
	int nSel = cStrText.GetLength();
	::SendMessage(_GetEditHwnd(), EM_SETSEL, LOWORD(nSel), HIWORD(nSel));

	// Focus를 잃었을 경우 ListCtrl에 적용시키기 위해 저장
	m_hFocus = _GetEditHwnd();
	Invalidate();

	*pResult = 0;
}


HWND CEditListCtrl::_GetEditHwnd(void)
{
	return ::GetDlgItem(GetParent()->GetSafeHwnd(), IDC_EDIT_CAM_ATTR);
}

HWND CEditListCtrl::_GetListHwnd(void)
{
	return GetSafeHwnd();
}

HWND CEditListCtrl::_GetParentHwnd(void)
{
	return GetParent()->GetSafeHwnd();
}

//이 컨트롤을 가지는 Dialog Class의 PreTranslateMessage 부분 혹은 OnOK 부분 (Return키 작동시를 위한)에
//현재 포커스가 ListCtrl 혹은 EditCtrl 일 경우 CEditListCtrl 의 OnOK 함수를 호출해야 합니다.

CTestDlg::OnOK()
{
	int id = GetFocus()->GetDlgCtrlID();
	if(id == IDC_LIST_EDITLISTCTRL || id == IDC_EDIT_EDITLISTCTRL)
		m_editListCtrl.OnOK()
}