質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.35%
C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

Visual C++

Microsoft Visual C++はWindowsのCとC++の統合開発環境(IDE)であり、コンパイラやデバッガを含んでいます。

Win32 API

Win32 APIはMicrosoft Windowsの32bitプロセッサのOSで動作するAPIです。

Q&A

解決済

1回答

4234閲覧

リストビューで選択している行の選択が解除されないようにするには?

mery

総合スコア27

C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

Visual C++

Microsoft Visual C++はWindowsのCとC++の統合開発環境(IDE)であり、コンパイラやデバッガを含んでいます。

Win32 API

Win32 APIはMicrosoft Windowsの32bitプロセッサのOSで動作するAPIです。

0グッド

0クリップ

投稿2020/12/04 03:44

前提・実現したいこと

リストビューでどこかの行を選択している状態で、リストビュー内のデータが存在しない場所をクリックすると、選択が解除されてしまいます。
(下のプログラムの場合は1行目や2行目を選択した状態で3行目以降をクリックすると選択が解除される)

リストビュー作成時にLVS_SHOWSELALWAYSを追加すれば選択が解除されないようにできると思ったのですが、うまくいきませんでした(クリックした位置はリストビュー内なのでフォーカスが失われているわけではないようです)。

この時に選択が解除されないようにするにはどうすればいいですか?

該当のソースコード

c

1#include <windows.h> 2#include <CommCtrl.h> 3#pragma comment(lib,"comctl32.lib") 4 5LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); 6 7TCHAR szClassName[] = TEXT("myprog"); 8HINSTANCE hinst; 9 10int WINAPI WinMain(_In_ HINSTANCE hCurInst, _In_opt_ HINSTANCE hPrevInst, 11 _In_ LPSTR lpsCmdLine, _In_ int nCmdShow) 12{ 13 14 hinst = hCurInst; 15 16 MSG msg; 17 BOOL bRet; 18 WNDCLASSEX wc; 19 HWND hWnd; 20 ATOM atom; 21 22 wc.cbSize = sizeof(WNDCLASSEX); 23 wc.style = CS_HREDRAW | CS_VREDRAW; 24 wc.lpfnWndProc = WndProc; 25 wc.cbClsExtra = 0; 26 wc.cbWndExtra = 0; 27 wc.hInstance = hCurInst; 28 wc.hIcon = (HICON)LoadImage(NULL, 29 MAKEINTRESOURCE(IDI_APPLICATION), 30 IMAGE_ICON, 31 0, 32 0, 33 LR_DEFAULTSIZE | LR_SHARED); 34 wc.hCursor = (HCURSOR)LoadImage(NULL, 35 MAKEINTRESOURCE(IDC_ARROW), 36 IMAGE_CURSOR, 37 0, 38 0, 39 LR_DEFAULTSIZE | LR_SHARED); 40 wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); 41 wc.lpszMenuName = NULL; 42 wc.lpszClassName = szClassName; 43 wc.hIconSm = (HICON)LoadImage(NULL, 44 MAKEINTRESOURCE(IDI_APPLICATION), 45 IMAGE_ICON, 46 0, 47 0, 48 LR_DEFAULTSIZE | LR_SHARED); 49 50 if ((atom = RegisterClassEx(&wc)) == 0) 51 return FALSE; 52 53 hWnd = CreateWindow(MAKEINTATOM(atom), 54 TEXT("リストビューのテスト"), 55 WS_OVERLAPPEDWINDOW, 56 CW_USEDEFAULT, 57 CW_USEDEFAULT, 58 CW_USEDEFAULT, 59 CW_USEDEFAULT, 60 NULL, 61 NULL, 62 hCurInst, 63 NULL); 64 if (!hWnd) 65 return FALSE; 66 67 ShowWindow(hWnd, nCmdShow); 68 UpdateWindow(hWnd); 69 while ((bRet = GetMessage(&msg, NULL, 0, 0)) != 0) { 70 if (bRet == -1) { 71 break; 72 } else { 73 TranslateMessage(&msg); 74 DispatchMessage(&msg); 75 } 76 } 77 return (int)msg.wParam; 78} 79 80LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp) 81{ 82 83 LVCOLUMN lvcol; 84 LVITEM item; 85 DWORD dwStyle; 86 static HWND hList; 87 88 switch (msg) { 89 case WM_CREATE: 90 InitCommonControls(); 91 hList = CreateWindowEx(0,WC_LISTVIEW,0, 92 WS_CHILD | WS_VISIBLE | LVS_REPORT | LVS_SINGLESEL | LVS_SHOWSELALWAYS, 93 0, 0, 0, 0, 94 hWnd, (HMENU)100, hinst, NULL); 95 96 dwStyle = ListView_GetExtendedListViewStyle(hList); 97 dwStyle |= LVS_EX_FULLROWSELECT | LVS_EX_GRIDLINES; 98 ListView_SetExtendedListViewStyle(hList, dwStyle); 99 100 lvcol.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM; 101 lvcol.fmt = LVCFMT_LEFT; 102 lvcol.cx = 300; 103 lvcol.pszText = TEXT("タイトル1"); 104 lvcol.iSubItem = 0; 105 ListView_InsertColumn(hList,0,&lvcol); 106 107 lvcol.pszText = TEXT("タイトル2"); 108 lvcol.iSubItem = 1; 109 ListView_InsertColumn(hList, 1, &lvcol); 110 111 item.mask = LVIF_TEXT; 112 item.pszText = TEXT("文字列"); 113 item.iItem = 0; 114 item.iSubItem = 0; 115 ListView_InsertItem(hList,&item); 116 117 item.pszText = TEXT("文字列"); 118 item.iItem = 1; 119 item.iSubItem = 0; 120 ListView_InsertItem(hList, &item); 121 122 item.pszText = TEXT("文字列"); 123 item.iItem = 0; 124 item.iSubItem = 1; 125 ListView_SetItem(hList, &item); 126 127 item.pszText = TEXT("文字列"); 128 item.iItem = 1; 129 item.iSubItem = 1; 130 ListView_SetItem(hList, &item); 131 132 break; 133 134 case WM_SIZE: 135 MoveWindow(hList, 0, 0, LOWORD(lp), HIWORD(lp), TRUE); 136 break; 137 138 case WM_DESTROY: 139 PostQuitMessage(0); 140 break; 141 default: 142 return (DefWindowProc(hWnd, msg, wp, lp)); 143 } 144 return 0; 145}

補足情報(FW/ツールのバージョンなど)

Microsoft Visual Studio Community 2019
Microsoft Visual C++ 2019 C言語
WIN32 ユニコードビルド Windows10

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

リストビューでどこかの行を選択している状態で、リストビュー内のデータが存在しない場所をクリックすると、選択が解除されてしまいます。

この動きは恐らくリストビューの既定の動作なので、ウィンドウスタイルの変更などでは対処できません。このような既定の動作を変えたい場合は、SetWindowSubclass APIを使ってウィンドウのサブクラス化を行います。

コントロール基礎 / 新しいサブクラス化 - EternalWindows

以下は、ご提示のコードでWM_CREATEでのリストビューのサブクラス化、WM_DESTROYでのサブクラス化の解除の処理を追加した例です。

C

1... 2 switch (msg) { 3 case WM_CREATE: 4 InitCommonControls(); 5 hList = CreateWindowEx(0,WC_LISTVIEW,0, 6 WS_CHILD | WS_VISIBLE | LVS_REPORT | LVS_SINGLESEL | LVS_SHOWSELALWAYS, 7 0, 0, 0, 0, 8 hWnd, (HMENU)100, hinst, NULL); 9... 10 11 ListView_SetItem(hList, &item); 12 13 /* ListViewの初期化が終わった後、サブクラス化 */ 14 { 15 BOOL succeeded = SetWindowSubclass(hList, SubclassProc, 101, 0); 16 if (!succeeded) { 17 char msg[256]; 18 wsprintfA(msg, "SetWindowSubclass error=%ld\n", GetLastError()); 19 OutputDebugStringA(msg); 20 } 21 } 22 break; 23 24... 25 26 case WM_DESTROY: 27 /* ウィンドウ終了時にでも、サブクラス化を解除 */ 28 RemoveWindowSubclass(hList, SubclassProc, 101); 29 PostQuitMessage(0); 30 break;

そして、サブクラス化したリストビュー用のウィンドウプロシージャが以下、です。マウスによる左クリックでリストビューの要素を選択するので、WM_LBUTTONDOWNメッセージの受信時にLVM_HITTESTでリストビュー上のどの部分をクリックしたかを判定し、要素が無い部分であればWM_LBUTTONDOWNを本プロシージャで処理済みとして終了することで、リストビューの既定の処理を横取りします。

ListView_HitTest macro - Microsoft Docs
LVM_HITTEST message - 同上

C

1LRESULT CALLBACK SubclassProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData) 2{ 3 switch (uMsg) { 4 5 case WM_LBUTTONDOWN: 6 case WM_LBUTTONDBLCLK: 7 { 8 int x = LOWORD(lParam); 9 int y = HIWORD(lParam); 10 11 char msg[256]; 12 wsprintfA(msg, "%s: x=%d, y=%d\n", 13 uMsg == WM_LBUTTONDOWN ? "WM_LBUTTONDOWN" : "WM_LBUTTONDBLCLK", 14 x, y); 15 16 OutputDebugStringA(msg); 17 18 LVHITTESTINFO info = { 0 }; 19 info.pt.x = x; 20 info.pt.y = y; 21 22 //SendMessage(hWnd, LVM_HITTEST, 0, (LPARAM)(LPVOID)&info); 23 ListView_HitTest(hWnd, &info); 24 wsprintfA(msg, "flags=0x%X, item=%d\n", info.flags, info.iItem); 25 OutputDebugStringA(msg); 26 if ((info.flags & LVHT_NOWHERE) != 0) { 27 // LVHT_NOWHERE: クライアント領域ではあるが要素上ではない。 28 29 // 要素無しの領域なので、メッセージ処理済みとして返す。 30 return 0; 31 } 32 } 33 break; 34 35 default: 36 break; 37 } 38 39 return DefSubclassProc(hWnd, uMsg, wParam, lParam); 40} 41

マウスの左ダブルクリックでも選択解除されてしまっていたので、WM_LBUTTONDBLCLKのハンドリングも同様に行っています。WPARAM wParamLPARAM lParamに渡されてくる値はWM_LBUTTONDOWNと同じなので、処理自体も同じになっています。OutputDebugStringAによるデバッグ出力もコード上に加えているので、実際にどう動いているのか確認してみてください。更に、リストビューの要素の選択操作に関しては本回答にあたって軽く確認した限りなので、もしかしたらWM_LBUTTONDOWNWM_LBUTTONDBLCLK以外にも処理をしなければいけないかもしれません。その点はご注意ください。

投稿2020/12/04 18:35

dodox86

総合スコア9256

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

mery

2020/12/05 06:12

回答ありがとうございました。教えていただいたコードにcase WM_RBUTTONDOWN:とcase WM_RBUTTONDBLCLK:を追加し、右クリック時の処理も同じにすることで期待していた結果になりました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.35%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問