ListView 셀 데이터 수정 후 SQLite 데이터베이스에 업데이트하기

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 언어로는 코드 구성이 더 어려울 수 있습니다.

유니코드 프로그래밍의 경우, 인터페이스 함수에서 문자열 변환이 필요하여 다소 번거로울 수 있습니다.

태그: ListView SQLite Win32 api Edit

6월 14일 16:17에 게시됨