ListView 컨트롤을 사용하여 SQLite 데이터를 표시하고, 더블클릭 이벤트를 처리하여 해당 셀에 Edit 컨트롤을 배치합니다. 편집 후 Enter 키를 누르면 데이터가 저장되고 Item 문자열이 업데이트되며, ESC 키를 누르면 취소됩니다.
저장 시, 백업된 셀 데이터와 ListView Item에 표시된 데이터를 결합하여 UPDATE 문을 구성한 후 데이터베이스에 저장합니다.
주요 코드는 다음과 같습니다:
SQLView.h
#pragma once
#include "xmdiview.h"
#include "stdio.h"
#include "sqlite3.h"
struct SQLCell
{
int row;
int column;
int length;
int isModified;
TCHAR *text;
};
class CSQLTable
{
public:
CSQLTable(void);
~CSQLTable(void);
int maxRows;
int maxColumns;
int modified;
int selectedColumn;
int selectedRow;
TCHAR tableName[1024];
SQLCell** cells;
int Clear();
};
class CSQLView :
public CXMDIView
{
public:
CSQLView(void);
~CSQLView(void);
XDECLARE_DYNCREATE(CSQLView)
public:
CXWnd* m_pMainFrame;
virtual BOOL PreCreateWindow(WNDCLASSEX &cs);
virtual BOOL PreCreateWindow(MDICREATESTRUCT &mdicreate);
int OnCreate(HWND, UINT, WPARAM, LPARAM);
int OnPaint(HWND, UINT, WPARAM, LPARAM);
int OnSize(HWND, UINT, WPARAM, LPARAM);
int OnChar(HWND, UINT, WPARAM, LPARAM);
int OnClose(HWND, UINT, WPARAM, LPARAM);
int OnNotify(HWND, UINT, WPARAM, LPARAM);
virtual int OnDestroy(HWND, UINT, WPARAM, LPARAM);
int OnExit(HWND, UINT, WPARAM, LPARAM);
int OnTest1(HWND, UINT, WPARAM, LPARAM);
int OnTest2(HWND, UINT, WPARAM, LPARAM);
int ProcessSQL(HWND, UINT, WPARAM, LPARAM);
int ExecuteSQL(HWND, UINT, WPARAM, LPARAM);
int ShowTables(HWND, UINT, WPARAM, LPARAM);
HWND m_hListView;
HWND m_hEditControl;
LV_COLUMN m_listColumn;
LVITEM m_listItem;
HIMAGELIST m_imageList;
HWND CreateListView(HWND parent);
int InitListView(HWND hListView);
int RefreshListView(HWND hListView, int rows, int cols, TCHAR **data);
HTREEITEM m_sqliteNode;
void *pTree;
static LONG m_defaultEditProc;
static LRESULT CALLBACK EditWindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
static TCHAR m_editBuffer[1024];
HWND m_hToolbar;
HWND CreateToolbar(HWND hWnd);
sqlite3 *db;
char *errorMsg;
int resultCode;
char queryBuffer[1024];
static int QueryCallback(void *NotUsed, int argc, char **argv, char **azColName);
int OnSaveData(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
int OpenDatabase(HWND hWnd);
int EnumerateTables(HWND hWnd);
int RunSQLQuery(TCHAR* sqlQuery);
int RunSQLCommand(TCHAR* sqlCommand);
CSQLTable m_currentTable;
int RunSQLQuery(TCHAR* sqlQuery, CSQLTable &table);
int UpdateListViewFromTable(CSQLTable &table);
XDECLARE_MESSAGE_MAP()
};
SQLView.cpp
#include "StdAfx.h"
#include "SQLView.h"
#include "sqlite3.h"
#include "main.h"
#include "PrjTree.h"
CSQLTable::CSQLTable(void)
{
maxRows = 0;
maxColumns = 0;
}
CSQLTable::~CSQLTable(void)
{
Clear();
}
int CSQLTable::Clear()
{
int i, j;
modified = 0;
selectedColumn = 0;
selectedRow = 0;
if (maxRows == 0) return 0;
for (i = 0; i < maxRows; i++)
{
for (j = 0; j < maxColumns; j++)
{
delete[] cells[i][j].text;
}
delete[] cells[i];
}
delete[] cells;
maxRows = 0;
maxColumns = 0;
return 1;
}
XIMPLEMENT_DYNCREATE(CSQLView, CXMDIView)
XBEGIN_MESSAGE_MAP(CSQLView, CXMDIView)
XON_MESSAGE(WM_CREATE, OnCreate)
XON_MESSAGE(WM_SIZE, OnSize)
XON_MESSAGE(WM_DESTROY, OnDestroy)
XON_MESSAGE(WM_COMMAND, OnCommand)
XON_MESSAGE(WM_CHAR, OnChar)
XON_MESSAGE(WM_CLOSE, OnClose)
XON_MESSAGE(WM_NOTIFY, OnNotify)
XON_COMMAND(IDM_TVIEW_TEST_TEST1, OnTest1)
XON_COMMAND(IDM_TVIEW_TEST_TEST2, OnTest2)
XON_COMMAND(IDM_SQLVIEW_SQL, ProcessSQL)
XON_COMMAND(IDM_SQLVIEW_EXEC, ExecuteSQL)
XON_COMMAND(IDM_SQLVIEW_TABLES, ShowTables)
XON_COMMAND(IDM_SQLVIEW_SAVETABLES, OnSaveData)
XEND_MESSAGE_MAP()
CSQLView::CSQLView(void)
{
wcscpy_s(szWindowClass, _T("XSQLView"));
wcscpy_s(szTitle, _T("XSQLView"));
m_hWnd = NULL;
}
CSQLView::~CSQLView(void)
{
}
TCHAR CSQLView::m_editBuffer[1024] = _T("");
LONG CSQLView::m_defaultEditProc = 0;
LRESULT CALLBACK CSQLView::EditWindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
HWND listView;
HWND parentView;
static CSQLTable *currentTable = NULL;
int row, col;
LVITEM item;
TCHAR buffer[1024];
switch (message)
{
case WM_CREATE:
break;
case WM_ACTIVATE:
ShowWindow(hWnd, SW_HIDE);
break;
case WM_SIZE:
break;
case WM_COMMAND:
switch (wParam)
{
case EN_CHANGE:
break;
case 0:
currentTable = (CSQLTable *)lParam;
break;
}
break;
case WM_CHAR:
switch (wParam)
{
case '\r':
GetWindowText(hWnd, buffer, 1024);
ShowWindow(hWnd, SW_HIDE);
listView = GetParent(hWnd);
parentView = GetParent(listView);
row = currentTable->selectedRow;
col = currentTable->selectedColumn;
item.mask = LVIF_TEXT;
item.iItem = row - 1;
item.iSubItem = col;
item.pszText = buffer;
ListView_SetItem(listView, &item);
SendMessage(parentView, WM_COMMAND, IDM_TVIEW_TEST_TEST2, (LPARAM)hWnd);
break;
case '\n':
break;
case 0x1b:
ShowWindow(hWnd, SW_HIDE);
break;
default:
break;
}
break;
default:
break;
}
return CallWindowProc((WNDPROC)m_defaultEditProc, hWnd, message, wParam, lParam);
}
HWND CSQLView::CreateListView(HWND parent)
{
DWORD style = WS_TABSTOP | WS_CHILD | WS_BORDER | WS_VISIBLE | LVS_REPORT;
HWND hListView = CreateWindow(WC_LISTVIEW, _T("ListView"),
style,
0, 0, 0, 0,
parent,
(HMENU)IDC_VIEW_LISTVIEW, m_hInst, NULL);
ListView_SetExtendedListViewStyle(hListView, LVS_EX_FULLROWSELECT | LVS_EX_GRIDLINES);
return hListView;
}
BOOL CSQLView::PreCreateWindow(WNDCLASSEX &cs)
{
return TRUE;
}
BOOL CSQLView::PreCreateWindow(MDICREATESTRUCT &mdicreate)
{
m_hMenu = LoadMenu(m_hInst, TEXT("MDIMENUTESTVIEW"));
m_hMenuWindow = GetSubMenu(m_hMenu, TEST_MENU_POS);
return TRUE;
}
int CSQLView::OnCreate(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
m_hListView = CreateListView(hWnd);
m_hEditControl = CreateWindow(TEXT("edit"), NULL, WS_CHILD | WS_BORDER,
0, 0, 0, 0,
m_hListView, (HMENU)0, m_hInst, NULL);
m_defaultEditProc = SetWindowLong(m_hEditControl, GWL_WNDPROC, (LONG)EditWindowProc);
m_hToolbar = CreateToolbar(hWnd);
if (OpenDatabase(hWnd))
{
PRINT(_T("\r\n test.db 열기 성공!"));
}
EnumerateTables(hWnd);
return 0;
}
int CSQLView::OnPaint(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
HDC hdc;
PAINTSTRUCT ps;
RECT rect;
hdc = BeginPaint(hWnd, &ps);
EndPaint(hWnd, &ps);
return 0;
}
int CSQLView::OnSize(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
int cxClient = LOWORD(lParam);
int cyClient = HIWORD(lParam);
MoveWindow(m_hListView, 0, 25, cxClient, cyClient - 25, TRUE);
MoveWindow(m_hToolbar, 0, 0, 0, 0, TRUE);
return DefMDIChildProc(hWnd, message, wParam, lParam);
}
int CSQLView::OnChar(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
return DefMDIChildProc(hWnd, message, wParam, lParam);
}
int CSQLView::OnDestroy(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
m_hWnd = NULL;
sqlite3_close(db);
return 0;
}
int CSQLView::OnExit(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
DestroyWindow(hWnd);
return 0;
}
int CSQLView::OnClose(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
return DefMDIChildProc(hWnd, message, wParam, lParam);
}
int CSQLView::OnTest1(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
PRINT(_T("\r\n Test1!"));
return 0;
}
int CSQLView::OnTest2(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
LV_COLUMN column;
LVITEM item;
TCHAR buffer[1024];
int row = m_currentTable.selectedRow - 1;
int col = m_currentTable.selectedColumn;
column.mask = LVCF_TEXT;
column.iSubItem = 0;
column.pszText = _T("*번호");
ListView_SetColumn(m_hListView, 0, &column);
item.mask = LVIF_TEXT;
item.iItem = row;
item.iSubItem = 0;
swprintf(buffer, _T("*%4d"), row + 1);
item.pszText = buffer;
ListView_SetItem(m_hListView, &item);
return 0;
}
int CSQLView::ProcessSQL(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
TCHAR sqlText[1024];
HWND inputBox = ((CMain*)m_pMainFrame)->m_pPBottom->m_pStrBox->m_hWndEditMsg;
GetWindowText(inputBox, sqlText, 1024);
PRINT(_T("\r\n SQL 실행: %s"), sqlText);
m_currentTable.Clear();
RunSQLQuery(sqlText, m_currentTable);
UpdateListViewFromTable(m_currentTable);
return 0;
}
int CSQLView::ExecuteSQL(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
TCHAR sqlText[1024];
HWND inputBox = ((CMain*)m_pMainFrame)->m_pPBottom->m_pStrBox->m_hWndEditMsg;
GetWindowText(inputBox, sqlText, 1024);
PRINT(_T("\r\n 명령 실행: %s"), sqlText);
RunSQLCommand(sqlText);
return 0;
}
int CSQLView::ShowTables(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
PRINT(_T("\r\nsqlite_master 조회 중..."));
m_currentTable.Clear();
RunSQLQuery(_T("select * from sqlite_master;"), m_currentTable);
UpdateListViewFromTable(m_currentTable);
return 0;
}
int CSQLView::OnSaveData(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
int i, j;
int columnCount, rowCount;
TCHAR tempBuffer[1024] = _T("");
TCHAR updateSQL[1024] = _T("");
TCHAR cellText[1024] = _T("");
if (m_currentTable.maxRows == 0)
{
PRINT(_T("\r\n테이블에 데이터가 없습니다."));
return 0;
}
if (m_currentTable.modified == 0)
{
PRINT(_T("\r\n저장할 변경 사항이 없습니다."));
return 0;
}
columnCount = m_currentTable.maxColumns;
rowCount = m_currentTable.selectedRow;
swprintf(updateSQL, _T("UPDATE TTest SET "));
i = rowCount;
j = 0;
ListView_GetItemText(m_hListView, i - 1, j + 1, cellText, 1024);
swprintf(tempBuffer, _T("%s=%s "), m_currentTable.cells[0][j].text, cellText);
wcscat(updateSQL, tempBuffer);
for (j = 1; j < columnCount; j++)
{
ListView_GetItemText(m_hListView, i - 1, j + 1, cellText, 1024);
swprintf(tempBuffer, _T(",%s=%s "), m_currentTable.cells[0][j].text, cellText);
wcscat(updateSQL, tempBuffer);
}
j = 0;
wcscat(updateSQL, _T("WHERE "));
swprintf(tempBuffer, _T("%s=%s "), m_currentTable.cells[0][j].text, m_currentTable.cells[i][j].text);
wcscat(updateSQL, tempBuffer);
for (j = 1; j < columnCount; j++)
{
swprintf(tempBuffer, _T("and %s=%s "), m_currentTable.cells[0][j].text, m_currentTable.cells[i][j].text);
wcscat(updateSQL, tempBuffer);
}
wcscat(updateSQL, _T(";"));
PRINT(updateSQL);
RunSQLCommand(updateSQL);
return 0;
}
HWND CSQLView::CreateToolbar(HWND hWnd)
{
TBBUTTON buttons[] = {
{ 0, IDM_SQLVIEW_SQL, TBSTATE_ENABLED, TBSTYLE_BUTTON, 0L, 0},
{ 1, IDM_SQLVIEW_EXEC, TBSTATE_ENABLED, TBSTYLE_BUTTON, 0L, 0},
{ 2, IDM_SQLVIEW_TABLES, TBSTATE_ENABLED, TBSTYLE_BUTTON, 0L, 0},
{ 3, IDM_SQLVIEW_SAVETABLES, TBSTATE_ENABLED, TBSTYLE_BUTTON, 0L, 0},
{ 0, 0, TBSTATE_ENABLED, TBSTYLE_SEP, 0L, -1},
{ 4, IDM_SQLVIEW_OPENTABLES, TBSTATE_ENABLED, TBSTYLE_BUTTON, 0L, 0},
{ 5, IDM_TEST_TEST3, TBSTATE_ENABLED, TBSTYLE_BUTTON, 0L, 0},
{ 6, IDM_TEST_TEST3, TBSTATE_ENABLED, TBSTYLE_BUTTON, 0L, 0},
{ 0, 0, TBSTATE_ENABLED, TBSTYLE_SEP, 0L, -1},
{ 7, IDM_TEST_TEST3, TBSTATE_ENABLED, TBSTYLE_BUTTON, 0L, 0},
{ 8, IDM_TEST_TEST3, TBSTATE_ENABLED, TBSTYLE_BUTTON, 0L, 0},
{ 9, IDM_TEST_TEST3, TBSTATE_ENABLED, TBSTYLE_BUTTON, 0L, 0},
{ 10, IDM_TEST_TEST3, TBSTATE_ENABLED, TBSTYLE_BUTTON, 0L, 0},
{ 11, IDM_TEST_TEST3, TBSTATE_ENABLED, TBSTYLE_BUTTON, 0L, 0},
{ 12, IDM_TEST_TEST3, TBSTATE_ENABLED, TBSTYLE_BUTTON, 0L, 0},
{ 13, IDM_TEST_TEST3, TBSTATE_ENABLED, TBSTYLE_BUTTON, 0L, 0},
{ 14, IDM_TEST_TEST3, TBSTATE_ENABLED, TBSTYLE_BUTTON, 0L, 0},
{ 15, IDM_TEST_TEST3, TBSTATE_ENABLED, TBSTYLE_BUTTON, 0L, 0},
{ 16, IDM_TEST_TEST3, TBSTATE_ENABLED, TBSTYLE_BUTTON, 0L, 0},
{ 17, IDM_EXIT, TBSTATE_ENABLED, TBSTYLE_BUTTON, 0L, 0}
};
HINSTANCE hLib = (HINSTANCE)GetWindowLong(hWnd, GWL_HINSTANCE);
HWND hToolbar = CreateWindowEx(0, TOOLBARCLASSNAME, _T(""),
WS_VISIBLE | WS_CHILD | TBSTYLE_TOOLTIPS | TBSTYLE_FLAT,
0, 0, 0, 0,
hWnd, (HMENU)IDC_MAINTOOLSBAR, m_hInst, NULL);
if (!hToolbar)
return NULL;
SendMessage(hToolbar, TB_BUTTONSTRUCTSIZE, (WPARAM)sizeof(TBBUTTON), 0);
SendMessage(hToolbar, TB_SETEXTENDEDSTYLE, 0, (LPARAM)(DWORD)(TBSTYLE_EX_DRAWDDARROWS));
HIMAGELIST imageList;
imageList = ImageList_Create(16, 16, ILC_COLOR32 | ILC_MASK,
sizeof(buttons) / sizeof(buttons[0]), sizeof(buttons) / sizeof(buttons[0]));
for (unsigned int i = 0; i <= sizeof(buttons) / sizeof(buttons[0]); i++)
{
HICON hIcon = LoadIcon(hLib, MAKEINTRESOURCE(IDI_ICON1 + i));
ImageList_AddIcon(imageList, hIcon);
DestroyIcon(hIcon);
}
SendMessage(hToolbar, TB_SETIMAGELIST, 0, (LPARAM)imageList);
SendMessage(hToolbar, TB_SETBITMAPSIZE, 0, (LPARAM)MAKELONG(16, 16));
SendMessage(hToolbar, TB_AUTOSIZE, 0, 0);
SendMessage(hToolbar, TB_ADDBUTTONS, 16, (LONG)&buttons);
return hToolbar;
}
int CSQLView::QueryCallback(void *NotUsed, int argc, char **argv, char **azColName)
{
int i;
for (i = 0; i < argc; i++)
{
PRINT(_T("%s = %s\n"), azColName[i], argv[i] ? argv[i] : "NULL");
}
PRINT(_T("\r\n"));
return 0;
}
int CSQLView::OpenDatabase(HWND hWnd)
{
resultCode = sqlite3_open("test.db", &db);
if (resultCode)
{
PRINT(_T("\r\n<오류>데이터베이스 열기 실패: %s\n"), sqlite3_errmsg(db));
sqlite3_close(db);
return -1;
}
return 1;
}
int CSQLView::EnumerateTables(HWND hWnd)
{
m_currentTable.Clear();
RunSQLQuery(_T("select * from sqlite_master limit 100;"), m_currentTable);
UpdateListViewFromTable(m_currentTable);
HWND treeHandle;
HTREEITEM sqlNode, dbNode, itemHandle;
CPrjTree *tree = ((CMain*)m_pMainFrame)->m_pPLeft->m_pPrjTree;
treeHandle = tree->m_hWndTree;
sqlNode = tree->m_hNodeSQLite3;
dbNode = tree->m_hNodeSQLite3DB;
dbNode = tree->InsertTreeNode(treeHandle, sqlNode, _T("Test1.db"));
for (int i = 1; i < m_currentTable.maxRows; i++)
{
itemHandle = tree->InsertTreeNode(treeHandle, dbNode, m_currentTable.cells[i][2].text);
}
itemHandle = tree->InsertTreeNode(treeHandle, dbNode, _T("Table2"));
SendMessage(treeHandle, TVM_EXPAND, TVE_EXPAND, (LPARAM)dbNode);
return 1;
}
int CSQLView::RunSQLQuery(TCHAR* sqlQuery, CSQLTable &table)
{
char narrowBuffer[1024];
wcstombs(narrowBuffer, sqlQuery, 1024);
char **resultSet;
int rows = 0, cols = 0;
resultCode = sqlite3_get_table(db, narrowBuffer, &resultSet, &rows, &cols, &errorMsg);
if (resultCode != SQLITE_OK)
{
if (NULL != errorMsg)
{
TCHAR wideBuffer[1024];
mbstowcs(wideBuffer, errorMsg, 1024);
PRINT(_T("\r\n<오류>SQL 오류: %s"), wideBuffer);
sqlite3_free(errorMsg);
}
PRINT(_T("\r\n<완료>select 성공!"));
}
table.maxColumns = cols;
table.maxRows = rows + 1;
table.cells = new SQLCell *[rows + 1];
for (int i = 0; i < rows + 1; i++)
{
table.cells[i] = new SQLCell[cols];
for (int j = 0; j < cols; j++)
{
if (resultSet[i * cols + j] != NULL)
{
int len = strlen(resultSet[i * cols + j]) + 1;
table.cells[i][j].length = len;
table.cells[i][j].text = new TCHAR[len];
mbstowcs(table.cells[i][j].text, resultSet[i * cols + j], 1024);
}
else
{
table.cells[i][j].length = 5;
table.cells[i][j].text = new TCHAR[5];
mbstowcs(table.cells[i][j].text, "NULL", 1024);
}
}
}
sqlite3_free_table(resultSet);
return 1;
}
int CSQLView::UpdateListViewFromTable(CSQLTable &table)
{
TCHAR numberBuffer[10];
LV_COLUMN column;
LVITEM item;
SendMessage(m_hListView, LVM_DELETEALLITEMS, 0, 0);
HWND header = (HWND)SendMessage(m_hListView, LVM_GETHEADER, 0, 0);
int oldCols = SendMessage(header, HDM_GETITEMCOUNT, 0, 0);
oldCols--;
for (; oldCols >= 0; oldCols--)
SendMessage(m_hListView, LVM_DELETECOLUMN, oldCols, 0);
column.mask = LVCF_TEXT | LVCF_WIDTH;
int dataRows = table.maxRows;
int dataCols = table.maxColumns + 1;
column.cx = 40;
column.iSubItem = 0;
column.pszText = _T("번호");
ListView_InsertColumn(m_hListView, 0, &column);
column.cx = 100;
for (int i = 1; i < dataCols; i++)
{
column.iSubItem = i;
column.pszText = table.cells[0][i - 1].text;
ListView_InsertColumn(m_hListView, i, &column);
}
ListView_SetItemCount(m_hListView, dataRows + 2);
item.mask = LVIF_TEXT;
for (int i = 1; i < dataRows; i++)
{
item.iItem = i - 1;
item.iSubItem = 0;
swprintf(numberBuffer, _T("%4d"), i - 1);
item.pszText = numberBuffer;
ListView_InsertItem(m_hListView, &item);
for (int j = 1; j < dataCols; j++)
{
item.iSubItem = j;
item.pszText = table.cells[i][j - 1].text;
ListView_SetItem(m_hListView, &item);
}
}
return 1;
}
int CSQLView::RunSQLCommand(TCHAR* sqlCommand)
{
char narrowBuffer[1024];
wcstombs(narrowBuffer, sqlCommand, 1024);
resultCode = sqlite3_exec(db, narrowBuffer, QueryCallback, 0, &errorMsg);
if (resultCode != SQLITE_OK)
{
TCHAR wideBuffer[1024];
mbstowcs(wideBuffer, errorMsg, 1024);
PRINT(_T("\r\n<오류>SQL 오류: %s"), wideBuffer);
sqlite3_free(errorMsg);
return -1;
}
PRINT(_T("\r\n 명령 실행 완료"));
return 1;
}
int CSQLView::OnNotify(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
RECT rect;
int itemIndex;
int subItemIndex;
TCHAR textBuffer[1024];
LPNMHDR notifyHeader = (LPNMHDR)lParam;
if (notifyHeader->hwndFrom == m_hEditControl)
{
PRINT(_T("\r\nEdit 컨트롤 알림 코드=%x"), notifyHeader->code);
}
if (notifyHeader->hwndFrom == m_hListView)
{
switch (notifyHeader->code)
{
case NM_DBLCLK:
itemIndex = ((LPNMITEMACTIVATE)lParam)->iItem;
subItemIndex = ((LPNMITEMACTIVATE)lParam)->iSubItem;
if (subItemIndex == 0) break;
ListView_GetSubItemRect(m_hListView, itemIndex, subItemIndex, LVIR_BOUNDS, &rect);
PRINT(_T("\r\n 더블클릭: %d %d - [%d %d : %d %d]"), itemIndex, subItemIndex, rect.left, rect.top, rect.right, rect.bottom);
ListView_GetItemText(m_hListView, itemIndex, subItemIndex, textBuffer, 1024);
MoveWindow(m_hEditControl, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, TRUE);
SetWindowText(m_hEditControl, textBuffer);
ShowWindow(m_hEditControl, SW_SHOW);
m_currentTable.selectedColumn = subItemIndex;
m_currentTable.selectedRow = itemIndex + 1;
SendMessage(m_hEditControl, WM_COMMAND, 0, (LPARAM)&m_currentTable);
break;
case NM_CLICK:
ShowWindow(m_hEditControl, SW_HIDE);
break;
default:
break;
}
}
return DefMDIChildProc(hWnd, message, wParam, lParam);
}
C++로 프레임워크를 구성하면 애플리케이션 개발이 더 편리해집니다. 화려한 인터페이스를 구현할 때 C 언어로는 코드 구성이 더 어려울 수 있습니다.
유니코드 프로그래밍의 경우, 인터페이스 함수에서 문자열 변환이 필요하여 다소 번거로울 수 있습니다.