第108章 リストビュー項目の削除と編集


今回は、リストビュー項目の削除と編集について解説します。

項目を選択しておき「編集」「項目の削除」で項目を削除することができます。 また、項目はコントロールキーを押しながらクリックすることで複数選択する ことができます。(クリックして最初の項目を選択して、シフトを押しながら 他の項目をクリックすると最初の項目から今の項目までを連続して選択できます。) 複数の項目を選択した場合、「項目の削除」で一度に削除できます。


項目をクリックして選択して、もう一度クリックすると「編集状態」となり ここに自由に入力できます。


では、早速プログラムを見てみましょう。 今回はダイアログボックスはありません。リソースはメニューだけです。

// listvw03.rcの一部 自前で作る人は参考にしてください ///////////////////////////////////////////////////////////////////////////// // // Menu // MYMENU MENU DISCARDABLE BEGIN POPUP "ファイル(&F)" BEGIN MENUITEM "終了(&X)", IDM_END END POPUP "編集(&E)" BEGIN MENUITEM "項目の削除(&D)", IDM_DEL END END

別にどうということもないメニューリソースです。

// listvw03.cpp #define STRICT #define ID_LISTVIEW 100 #include <windows.h> #include <commctrl.h> #include "resource.h" LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); BOOL InitApp(HINSTANCE); BOOL InitInstance(HINSTANCE, int); void SetInitialData(HWND); char szClassName[] = "listvw03";//ウィンドウクラス HINSTANCE hInst; //インスタンスハンドル(保存) HWND hList; //リストビューのハンドル int WINAPI WinMain(HINSTANCE hCurInst, HINSTANCE hPrevInst, LPSTR lpsCmdLine, int nCmdShow) { MSG msg; if (!InitApp(hCurInst)) return FALSE; if (!InitInstance(hCurInst, nCmdShow)) return FALSE; while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return msg.wParam; }

今回はダイアログボックス関係のマクロを使わないのでwindowsx.hは不要です。 コモンコントロールを使うときはcommctrl.hを忘れないでください。 また、プロジェクトにcomctl32.libを参加させるのを忘れないでください。 あとは、いつもの通りです。

//ウィンドウ・クラスの登録 BOOL InitApp(HINSTANCE hInst) { WNDCLASSEX wc; wc.cbSize = sizeof(WNDCLASSEX); wc.style = CS_HREDRAW | CS_VREDRAW; wc.lpfnWndProc = WndProc;//プロシージャ名 wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = hInst;//インスタンス wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); wc.lpszMenuName = "MYMENU";//メニュー名 wc.lpszClassName = (LPCSTR)szClassName; wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION); return (RegisterClassEx(&wc)); }

いつもと同じですが、メニュー名の指定を忘れないでください。

//ウィンドウの生成 BOOL InitInstance(HINSTANCE hInstance, int nCmdShow) { HWND hWnd; hInst = hInstance;//グローバル変数に保存 hWnd = CreateWindow(szClassName, "猫でもわかるリストビュー",//タイトルバーにこの名前が表示されます WS_OVERLAPPEDWINDOW,//ウィンドウの種類 CW_USEDEFAULT, //X座標 CW_USEDEFAULT, //Y座標 CW_USEDEFAULT, //幅 CW_USEDEFAULT, //高さ NULL, //親ウィンドウのハンドル、親を作るときはNULL NULL, //メニューハンドル、クラスメニューを使うときはNULL hInst, //インスタンスハンドル NULL); if (!hWnd) return FALSE; ShowWindow(hWnd, nCmdShow); UpdateWindow(hWnd); return TRUE; }

いつもと同じですが、簡単のためにインスタンスハンドルをグローバル変数に コピーしています。

//ウィンドウプロシージャ LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp) { int id; int nItem; LV_DISPINFO *lvinfo; char buf[64]; static HWND hEdit; switch (msg) { case WM_COMMAND: switch (LOWORD(wp)) { case IDM_END: SendMessage(hWnd, WM_CLOSE, 0, 0); break; case IDM_DEL: while(1) { nItem = ListView_GetNextItem( hList, -1, LVNI_ALL | LVNI_SELECTED); if (nItem == -1) break; ListView_DeleteItem(hList, nItem); } break; } break; case WM_NOTIFY: if ((int)wp == ID_LISTVIEW) { lvinfo = (LV_DISPINFO *)lp; switch (lvinfo->hdr.code) { case LVN_BEGINLABELEDIT: hEdit = ListView_GetEditControl(hList); break; case LVN_ENDLABELEDIT: GetWindowText(hEdit, buf, sizeof(buf)); ListView_SetItemText(hList, lvinfo->item.iItem, 0, buf); break; } } break; case WM_CREATE: InitCommonControls(); hList = CreateWindowEx(0, WC_LISTVIEW, "", WS_CHILD | WS_VISIBLE | LVS_REPORT | LVS_EDITLABELS, 0, 0, 0, 0, hWnd, (HMENU)ID_LISTVIEW, hInst, NULL); SetInitialData(hList); break; case WM_SIZE: MoveWindow(hList, 0, 0, LOWORD(lp), HIWORD(lp), TRUE); break; case WM_CLOSE: id = MessageBox(hWnd, "終了してもよいですか", "終了確認", MB_YESNO | MB_ICONQUESTION); if (id == IDYES) { DestroyWindow(hWnd); } break; case WM_DESTROY: PostQuitMessage(0); break; default: return (DefWindowProc(hWnd, msg, wp, lp)); } return 0; }

さて、肝心のプロシージャです。

メニューから「項目の削除」(IDM_DEL)が選択されたときは ListView_GetNextItemマクロを使って選択された項目を調べ ListView_DeleteItemマクロで項目を削除しています。

int ListView_GetNextItem( HWND hwnd, int iStart, UINT flags );

このマクロで検索をします。
hwndはリストビューのハンドルです。

iStartは検索開始の項目の番号です。一番最初の項目から 検索するときは-1を指定します。この番号の項目自体は 検索対象にならないので0を指定すると最初の項目は検索されません。

flagsは検索対象となる項目の状態と指定項目との位置関係を指定します。
LVNI_ABOVEは指定項目の上方にある項目を検索します。
LVNI_ALLは指定項目より順に検索します。
LVNI_BELOWは指定項目より下方を検索します。
LVNI_TOLEFTは左にある項目を検索します。
LVNI_TORIGHTは右にある項目を検索します。

LVNI_CUTはLVIS_CUT状態に設定されている項目を探します。
LVNI_DROPHILITEDはLVIS_DROPHILITED状態にある項目を探します。
LVNI_FOCUSEDはLVIS_FOCUSED状態にある項目を探します。
LVNI_SELECTEDはLVIS_SELECTED状態にある項目を探します。

戻り値は項目番号です。該当するものが無いときは−1を返します。

ここでは、LVNI_ALL | LVNI_SELECTEDを指定すればよいことがわかります。

BOOL ListView_DeleteItem( HWND hwnd, int iItem );

リストビューから項目を削除するマクロです。

hwndはリストビューのハンドルです。

iItemは項目番号です。

成功したらTRUE, 失敗したらFALSEを返します。

これで、項目の削除はできるようになりました。次に項目の編集です。 これは、通知メッセージを処理しなくてはいけません。
通知メッセージは今までも何回か出てきました。 たとえばツールチップ(第65章参照) のところで出てきました。コントロール(という名の子供ウィンドウ) に起こったことは本来コントロールのプロシージャにメッセージが送られるはずです。 ところがコントロールのプロシージャは隠されているので直接メッセージを 処理するわけにはいきません。そこで、何かおもしろいことが起こった場合 その親にも「こういうおもしろいことが起こったぞ」というメッセージが 届けば便利です。これが「通知メッセージ」です。

第65章でWM_NOTIFYについて解説しています のでもう一度確認してみてください。

typedef struct tagLV_DISPINFO { NMHDR hdr; LV_ITEM item; } LV_DISPINFO;

この構造体はWM_NOTIFYメッセージのlParamとしてやってきます。 hdrはNMHDR構造体です。 この構造体については第65章を参照してください
さて、この構造体のhdrメンバのcodeメンバに処理しなくてはならないメッセージが含まれ ています。
具体的には、LVN_BEGINLABELEDITとLVN_ENDLABELEDITメッセージです。 前者で編集時に出てくるエジットコントロールのウィンドウハンドルを取得します。 後者でエジットコントロールからテキストを取得して、項目に設定します。 従って

case WM_NOTIFY: if ((int)wp == ID_LISTVIEW) { lvinfo = (LV_DISPINFO *)lp; switch (lvinfo->hdr.code) { case LVN_BEGINLABELEDIT: hEdit = ListView_GetEditControl(hList); break; case LVN_ENDLABELEDIT: GetWindowText(hEdit, buf, sizeof(buf)); ListView_SetItemText(hList, lvinfo->item.iItem, 0, buf); break; } } break;

というような感じになります。lParamからLV_DISP構造体を取得してこの中のhdrメンバの codeメンバを調べるということになります。

VOID WINAPI ListView_SetItemText( HWND hwnd, int i, int iSubItem, LPCSTR pszText );

このマクロは見ての通りで特に説明は必要ないでしょう。プログラムでは iにlvinfo->item.iItemを指定している点に注意してください。

WM_CREATEメッセージが来たら、リストビューを作って 自作関数のSetInitialData関数を呼んでいます。 実は、このサンプルプログラムは前章のプログラムに継ぎ足して 作る予定でしたが、テストランが非常に面倒なので 最初からデータをリストビューに書き込んでしまうようにしました。 (テストランする度にデータを手書きで入力していたのでは大変です。)

// 初期データ表示用 void SetInitialData(HWND hList) { LV_COLUMN lvcol; LV_ITEM item; lvcol.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM; lvcol.fmt = LVCFMT_LEFT; lvcol.cx = 100; lvcol.pszText = "名前"; lvcol.iSubItem = 0; ListView_InsertColumn(hList, 0, &lvcol); lvcol.cx = 200; lvcol.pszText = "住所"; lvcol.iSubItem = 1; ListView_InsertColumn(hList, 1, &lvcol); item.mask = LVIF_TEXT; item.pszText = "粂井康孝"; item.iItem = 0; item.iSubItem = 0; ListView_InsertItem(hList, &item); item.pszText = "北海道旭川市"; item.iItem = 0; item.iSubItem = 1; ListView_SetItem(hList, &item); item.pszText = "粂井ひとみ"; item.iItem = 1; item.iSubItem = 0; ListView_InsertItem(hList, &item); item.pszText = "東京都千代田区"; item.iItem = 1; item.iSubItem = 1; ListView_SetItem(hList, &item); item.pszText = "粂井志麻"; item.iItem = 2; item.iSubItem = 0; ListView_InsertItem(hList, &item); item.pszText = "北海道中富良野町"; item.iItem = 2; item.iSubItem = 1; ListView_SetItem(hList, &item); item.pszText = "粂井櫻都"; item.iItem = 3; item.iSubItem = 0; ListView_InsertItem(hList, &item); item.pszText = "北海道深川市"; item.iItem = 3; item.iSubItem = 1; ListView_SetItem(hList, &item); return; }

これは、初期データ表示用の関数です。 さて、前章のプログラムにこの章と同じような機能を継ぎ足してみてください。
[SDK第2部 Index] [総合Index] [Previous Chapter] [Next Chapter]

Update Feb/14/1998 By Y.Kumei
当ホーム・ページの一部または全部を無断で複写、複製、 転載あるいはコンピュータ等のファイルに保存することを禁じます。