C & C++/MFC 컨트롤

[List] List 컨트롤 원하는 항목 수정

izen8 2011. 5. 4. 20:08


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

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

EditListCtrl.h
  1. #pragma once   
  2.   
  3.   
  4. // CEditListCtrl   
  5.   
  6. class CEditListCtrl : public CListCtrl   
  7. {   
  8.     DECLARE_DYNAMIC(CEditListCtrl)   
  9.   
  10. public:   
  11.     CEditListCtrl();   
  12.     virtual ~CEditListCtrl();   
  13.   
  14. protected:   
  15.     DECLARE_MESSAGE_MAP()   
  16.   
  17. public:   
  18.     virtual BOOL PreTranslateMessage(MSG* pMsg);   
  19.     afx_msg void OnNMClick(NMHDR *pNMHDR, LRESULT *pResult);   
  20.   
  21.     // 초기화 함수.   
  22.     void Init(void);   
  23.   
  24.     // CEditListCtrl 을 사용하는 Dialog 의 OnOK 함수가 호출될때 호출해야 한다.   
  25.     void OnOK(void);   
  26.   
  27.     // List 값을 바꾼다.   
  28.     BOOL SetText(CString _val, int _row, int _col);   
  29.   
  30. protected:   
  31.     // ListCtrl 을 클릭했을때 nItem 저장될 변수. (Row)   
  32.     int m_nItem;   
  33.   
  34.     // ListCtrl 을 클릭했을때 nSubItem 저장될 변수 (Col);   
  35.     int m_nSubItem;   
  36.   
  37.     // 현재 포커스가 어딘지 확인하는 변수   
  38.     HWND m_hFocus;   
  39.   
  40.     bool m_bInit;   
  41.   
  42.     HWND _GetEditHwnd(void);   
  43.     HWND _GetListHwnd(void);   
  44.     HWND _GetParentHwnd(void);   
  45. };  


EditListCtrl.cpp
  1. // EditListCtrl.cpp : 구현 파일입니다.   
  2. //   
  3.   
  4. #include "stdafx.h"   
  5. #include "TestApp.h"   
  6. #include "EditListCtrl.h"   
  7.   
  8.   
  9. // CEditListCtrl   
  10.   
  11. IMPLEMENT_DYNAMIC(CEditListCtrl, CListCtrl)   
  12.   
  13. CEditListCtrl::CEditListCtrl()   
  14. : m_nItem(0), m_nSubItem(0)   
  15. , m_bInit(false)   
  16. , m_hFocus((HWND)0)   
  17. {   
  18.   
  19. }   
  20.   
  21. CEditListCtrl::~CEditListCtrl()   
  22. {   
  23. }   
  24.   
  25. BEGIN_MESSAGE_MAP(CEditListCtrl, CListCtrl)   
  26.     ON_NOTIFY_REFLECT(NM_CLICK, &CEditListCtrl::OnNMClick)   
  27. END_MESSAGE_MAP()   
  28.   
  29.   
  30.   
  31.   
  32. // CEditListCtrl 메시지 처리기입니다.   
  33.   
  34. BOOL CEditListCtrl::PreTranslateMessage(MSG* pMsg)   
  35. {   
  36.     if(m_hFocus != (HWND)0)   
  37.     {   
  38.         HWND hFocus = ::GetFocus();   
  39.   
  40.         // Edit 가 Focus를 잃으면.   
  41.         if(m_hFocus != hFocus)   
  42.         {   
  43.             OnOK();   
  44.         }   
  45.     }   
  46.        
  47.   
  48.     return CListCtrl::PreTranslateMessage(pMsg);   
  49. }   
  50.   
  51. void CEditListCtrl::Init(void)   
  52. {   
  53.     if(m_bInit) return;   
  54.   
  55.     SetExtendedStyle(LVS_EX_FULLROWSELECT | LVS_EX_GRIDLINES);   
  56.     ::ShowWindow(_GetEditHwnd(), SW_HIDE);   
  57.   
  58.     m_bInit = true;   
  59. }   
  60.   
  61.   
  62. // Return 키가 눌렸을때 호출하기 위한..   
  63. void CEditListCtrl::OnOK(void)   
  64. {   
  65.     CWnd* pWnd = GetFocus();   
  66.   
  67.     if(_GetEditHwnd() == pWnd->GetSafeHwnd() ||   
  68.         _GetEditHwnd() == m_hFocus)   
  69.     {   
  70.         // EditCtrl로 부터 Text를 얻어온다.   
  71.         wchar_t cText[255];   
  72.         ZeroMemory(cText, 255);   
  73.         ::GetWindowText(_GetEditHwnd(), cText, 255);   
  74.         CString cStrText(cText);   
  75.   
  76.         // 얻어온 텍스트를 ListCtrl 에 적용시킨다   
  77.         if(SetText(cStrText, m_nItem, m_nSubItem) == FALSE)   
  78.             return;   
  79.   
  80.         // EditCtrl의 Focus를 죽인다.   
  81.         ::SendMessage(_GetEditHwnd(), WM_KILLFOCUS, 0, 0);   
  82.         ::ShowWindow(_GetEditHwnd(), SW_HIDE);   
  83.         ::SetFocus(pWnd->GetSafeHwnd());   
  84.   
  85.         // ListCtrl의 SelectionMark를 지정한다.   
  86.         SetSelectionMark(m_nItem);   
  87.     }   
  88.   
  89.     m_hFocus = (HWND)0;   
  90. }   
  91.   
  92. BOOL CEditListCtrl::SetText(CString _val, int _row, int _col)   
  93. {   
  94.     return SetItemText(_row, _col, _val);   
  95. }   
  96.   
  97. void CEditListCtrl::OnNMClick(NMHDR *pNMHDR, LRESULT *pResult)   
  98. {   
  99.     Invalidate();   
  100.   
  101.     LPNMITEMACTIVATE pNMITEM = (LPNMITEMACTIVATE) pNMHDR;   
  102.     m_nItem = pNMITEM->iItem;   
  103.     m_nSubItem = pNMITEM->iSubItem;   
  104.   
  105.     // 선택된 지점이 올바르지 않을시...   
  106.     if(m_nSubItem == 0 || m_nSubItem == -1 || m_nItem == -1)   
  107.         return;   
  108.   
  109.     // 선택된 SubItem의 Text를 CEdit 에 넣어주기 위한 변수   
  110.     CString cStrText = GetItemText(m_nItem, m_nSubItem);   
  111.   
  112.     CRect rtSubItem, rtListCtrl, rtDlg;   
  113.     // SubItem 의 Rect를 얻어온다.   
  114.     if(GetSubItemRect(pNMITEM->iItem, pNMITEM->iSubItem, LVIR_BOUNDS, rtSubItem) == FALSE)   
  115.         return;   
  116.   
  117.     // ListControl 의 Window Bounds 를 얻어온다.   
  118.     ::GetWindowRect(_GetListHwnd(), &rtListCtrl);   
  119.   
  120.     // Parent Dialog 의 Windows Bounds 를 얻어온다.   
  121.     ::GetWindowRect(_GetParentHwnd(), &rtDlg);   
  122.   
  123.     // Dialog에 위치한 ListCtrl의 left & top 위치를 구한다.   
  124.     int nThisLeft = rtListCtrl.left - rtDlg.left;   
  125.     int nThisTop = rtListCtrl.top - rtDlg.top;   
  126.   
  127.     // EditCtrl의 위치를 변경해준다.   
  128.     ::SetWindowPos(   
  129.         _GetEditHwnd(),    
  130.         HWND_TOP,   
  131.         nThisLeft + rtSubItem.left + 7,   
  132.         nThisTop + rtSubItem.top + 4,   
  133.         rtSubItem.Width() - 7,   
  134.         rtSubItem.Height() - 4,   
  135.         NULL);   
  136.   
  137.     // EditCtrl Show   
  138.     ::ShowWindow(_GetEditHwnd(), SW_SHOW);   
  139.     ::SetFocus(_GetEditHwnd());   
  140.   
  141.     // SubItem 위치에 외각선을 그려준다.   
  142.     ::Rectangle(::GetDC(_GetListHwnd()), rtSubItem.left, rtSubItem.top - 1, rtSubItem.right, rtSubItem.bottom);   
  143.   
  144.     // EditCtrl 에 선택한 List의 Text 를 넣어준다.   
  145.     ::SetWindowText(_GetEditHwnd(), cStrText);   
  146.   
  147.     // 삽입한 Text의 마지막에 포커스를 맞춰주고 Mark한다.   
  148.     int nSel = cStrText.GetLength();   
  149.     ::SendMessage(_GetEditHwnd(), EM_SETSEL, LOWORD(nSel), HIWORD(nSel));   
  150.   
  151.     // Focus를 잃었을 경우 ListCtrl에 적용시키기 위해 저장   
  152.     m_hFocus = _GetEditHwnd();   
  153.     Invalidate();   
  154.   
  155.     *pResult = 0;   
  156. }   
  157.   
  158.   
  159. HWND CEditListCtrl::_GetEditHwnd(void)   
  160. {   
  161.     return ::GetDlgItem(GetParent()->GetSafeHwnd(), IDC_EDIT_CAM_ATTR);   
  162. }   
  163.   
  164. HWND CEditListCtrl::_GetListHwnd(void)   
  165. {   
  166.     return GetSafeHwnd();   
  167. }   
  168.   
  169. HWND CEditListCtrl::_GetParentHwnd(void)   
  170. {   
  171.     return GetParent()->GetSafeHwnd();   
  172. }  



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

  1. CTestDlg::OnOK()   
  2. {   
  3.     int id = GetFocus()->GetDlgCtrlID();   
  4.     if(id == IDC_LIST_EDITLISTCTRL || id == IDC_EDIT_EDITLISTCTRL)   
  5.         m_editListCtrl.OnOK()   
  6. }  


이정도가 되겠습니다