问题
In a Listview I am looking to have a marquee-able empty space of about 20 pixels above the first row of items and below the last row of items. Also to the left of the left-most items and the right of the right-most items.
I am setting the item positions manually by using LVM_SETITEMPOSITION. Trying to place them at an offset of 20 pixels doesn't seem to work.
I have supplied a simple code sample below. For some reason intially there actually is a marquee-able empty space to the top but once you scroll down and back up this space is gone.
#include <Windows.h>
#include <CommCtrl.h>
#include <gdiplus.h>
#include <string>
#include <codecvt>
#pragma comment(lib,"comctl32.lib")
#pragma comment(lib,"gdiplus.lib")
::HINSTANCE g_hInstance;
// controls
::HWND g_hMainWindow;
::HWND g_hListview;
::HWND g_hButton;
// img list(s)
::HIMAGELIST g_hImageListNormal;
::HWND CreateBasicWindow();
::HWND CreateListview(::HWND hParentWnd, ::HIMAGELIST hImageList);
::HWND CreateButton(::HWND hParentWnd);
LRESULT CALLBACK WindowProcedure(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
void Listview_insertItem(::HWND hListview, std::string text, int imageIndex);
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, char* szCmdLine, int iCmdShow) {
g_hInstance = hInstance;
g_hMainWindow = CreateBasicWindow();
g_hListview = CreateListview(g_hMainWindow, g_hImageListNormal);
g_hButton = CreateButton(g_hMainWindow);
::ShowWindow(g_hListview, SW_SHOW);
::UpdateWindow(g_hListview);
::ShowWindow(g_hButton, SW_SHOW);
::UpdateWindow(g_hButton);
::ShowWindow(g_hMainWindow, SW_SHOW);
::UpdateWindow(g_hMainWindow);
for (int i = 0; i < 5; i++) {
Listview_insertItem(g_hListview, "item", 0);
}
MSG msg = { 0 };
while (true) {
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
if (msg.message == WM_QUIT) {
return 1;
}
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
return 1;
}
::HWND CreateBasicWindow() {
::WNDCLASS wndClass = { };
wndClass.cbClsExtra = 0;
wndClass.cbWndExtra = 0;
wndClass.hbrBackground = (HBRUSH)::GetStockObject(GRAY_BRUSH);
wndClass.hCursor = ::LoadCursor(NULL, IDC_ARROW);
wndClass.hIcon = ::LoadIcon(NULL, IDC_ICON);
wndClass.hInstance = g_hInstance;
wndClass.lpfnWndProc = WindowProcedure;
wndClass.lpszClassName = "Window1";
wndClass.lpszMenuName = NULL;
wndClass.style = CS_HREDRAW | CS_VREDRAW/* | CS_OWNDC | CS_PARENTDC*/;
if (!::RegisterClass(&wndClass)) {
return 0;
}
::RECT rect;
rect.left = 100;
rect.right = 900;
rect.top = 100;
rect.bottom = 900;
::HWND hWnd = ::CreateWindowEx(0,
"Window1",
"Window1",
WS_OVERLAPPEDWINDOW,
rect.left,
rect.top,
rect.right - rect.left,
rect.bottom - rect.top,
NULL,
0,
g_hInstance,
NULL);
return hWnd;
}
::HWND CreateListview(::HWND hParentWnd, ::HIMAGELIST hImageList) {
::RECT rect;
rect.left = 100;
rect.right = 500;
rect.top = 100;
rect.bottom = 500;
::HWND hWnd = ::CreateWindowEx(0,
WC_LISTVIEW,
WC_LISTVIEW,
WS_CHILD | LVS_ICON,
rect.left,
rect.top,
rect.right - rect.left,
rect.bottom - rect.top,
hParentWnd,
0,
g_hInstance,
NULL);
::SIZE iconSize;
iconSize.cx = 100;
iconSize.cy = 100;
// Create imagelist
hImageList = ::ImageList_Create(iconSize.cx, iconSize.cy, ILC_COLOR32 | ILC_MASK, 0, 0);
// Bind imagelist to listview
ListView_SetImageList(hWnd, hImageList, 0);
std::string imageFileLocation = "image.bmp";
// Load Image
::HBITMAP hBitmap = (::HBITMAP)LoadImage(NULL, imageFileLocation.c_str(), IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE | LR_DEFAULTSIZE);
if (!hBitmap)
::MessageBox(hWnd, std::string("Image " + imageFileLocation + " not found.").c_str(), "Error", MB_OK);
// Add image to imagelist
::ImageList_Add(hImageList, hBitmap, NULL);
// Set icon spacing
ListView_SetIconSpacing(hWnd, iconSize.cx + 16, iconSize.cy + 22);
// enable double buffering and oneclick activate for convenience
::SendMessage(hWnd, LVM_SETEXTENDEDLISTVIEWSTYLE, LVS_EX_DOUBLEBUFFER, LVS_EX_DOUBLEBUFFER);
::SendMessage(hWnd, LVM_SETEXTENDEDLISTVIEWSTYLE, LVS_EX_ONECLICKACTIVATE, LVS_EX_ONECLICKACTIVATE);
return hWnd;
}
::HWND CreateButton(::HWND hParentWnd) {
::RECT rect;
rect.left = 100;
rect.right = 200;
rect.top = 50;
rect.bottom = 100;
::HWND hWnd = ::CreateWindowEx(0,
WC_BUTTON,
WC_BUTTON,
WS_CHILD,
rect.left,
rect.top,
rect.right - rect.left,
rect.bottom - rect.top,
hParentWnd,
0,
g_hInstance,
NULL);
::SendMessage(hWnd, WM_SETTEXT, 0, (LPARAM)"Insert Item");
return hWnd;
}
void Listview_insertItem(::HWND hListview, std::string text, int imageIndex) {
unsigned int mask = LVIF_TEXT | LVIF_IMAGE;
::LVITEM W32LvItem = { 0 };
W32LvItem.mask = mask;
W32LvItem.iItem = INT_MAX;
W32LvItem.iSubItem = 0;
W32LvItem.pszText = (char*)text.c_str();
W32LvItem.cchTextMax = text.length();
W32LvItem.iImage = imageIndex;
// NOTE: LVM_INSERTITEM nor ListView_InsertItem can be used to insert a subitem
int newItemIndex = ::SendMessage(hListview, LVM_INSERTITEM, 0, (::LPARAM)&W32LvItem);
// UPDATE ALL ITEM POSITIONS
::RECT lvClientRect;
::GetClientRect(hListview, &lvClientRect);
::SIZE lvClientSize;
lvClientSize.cx = lvClientRect.right - lvClientRect.left;
lvClientSize.cy = lvClientRect.bottom - lvClientRect.top;
::SIZE itemSize;
itemSize.cx = 50 + 16;
itemSize.cy = 50 + 22;
::DWORD spacing = ListView_GetItemSpacing(hListview, false);
::SIZE itemSpacing;
itemSpacing.cx = LOWORD(spacing);
itemSpacing.cy = HIWORD(spacing);
int itemIndex = 0;
while (itemIndex != -1) {
// calculate how many items fit in a row
int iItemsPerRow = lvClientSize.cx / (itemSize.cx + itemSpacing.cx);
// calculate in which row, and in which place within that row this icon should be placed
int itemRowNb = (int)std::ceil(itemIndex / iItemsPerRow);
int itemColumnNb = (itemIndex < iItemsPerRow) ? itemIndex : itemIndex % iItemsPerRow;
int topOffset = 20;
// calculate destination x/y
int x = lvClientRect.left + itemColumnNb * (itemSize.cx + itemSpacing.cx);
int y = lvClientRect.top + topOffset + itemRowNb * (itemSize.cy + itemSpacing.cy);
// move item to calculated position
ListView_SetItemPosition(hListview, itemIndex, x, y);
// get next item
itemIndex = ListView_GetNextItem(hListview, itemIndex, LVNI_ALL);
}
}
LRESULT CALLBACK WindowProcedure(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) {
switch (msg) {
case WM_COMMAND:
{
switch (HIWORD(wParam)) {
case BN_CLICKED:
{
int itemCount = ListView_GetItemCount(g_hListview);
Listview_insertItem(g_hListview, "item" + std::to_string(itemCount), 0);
break;
}
}
break;
}
}
return DefWindowProc(hWnd, msg, wParam, lParam);
}
来源:https://stackoverflow.com/questions/62708112/win32-want-marquee-able-empty-space-in-a-listview-iconview