If I adapt an old MS sample (TreeViewDragAndDropV6.exe), by fixing a few bugs, I don't see any flickering (Windows 10 22H2) :
#include <windows.h>
#include <tchar.h>
#include <commctrl.h>
#pragma comment (lib, "comctl32")
#include <gdiplus.h>
using namespace Gdiplus;
#pragma comment(lib, "gdiplus")
#include <Urlmon.h> // URLDownloadToCacheFile
#pragma comment (lib, "Urlmon")
#include <shlwapi.h> // SHCreateStreamOnFile
#pragma comment (lib, "shlwapi")
#pragma comment(linker,"\"/manifestdependency:type='win32' \
name='Microsoft.Windows.Common-Controls' version='6.0.0.0' \
processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
HINSTANCE hInst;
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
int nWidth = 600, nHeight = 600;
#define IDC_STATIC 10
#define IDC_BUTTON 11
#define IDC_TV 12
HWND hWnd = NULL;
void FillTreeView(HWND hWndTreeView);
void TreeView_OnBeginDrag(HWND hWndTV, LPNMTREEVIEW pnmtv);
HTREEITEM _hItemDragged = NULL;
BOOL _bLeftDragging = FALSE;
HTREEITEM _hItemOld = NULL;
BOOL IsChild(HWND hWndTV, HTREEITEM hChild, HTREEITEM hParent);
HTREEITEM CopyItemBranch(HWND hWndTV, HTREEITEM hTreeItemFrom, HTREEITEM hTreeItemTo);
int APIENTRY wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPWSTR lpCmdLine, _In_ int nCmdShow)
{
GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR gdiplusToken;
GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
hInst = hInstance;
WNDCLASSEX wcex =
{
sizeof(WNDCLASSEX), CS_HREDRAW | CS_VREDRAW, WndProc, 0, 0, hInst, LoadIcon(NULL, IDI_APPLICATION),
LoadCursor(NULL, IDC_ARROW), (HBRUSH)(COLOR_WINDOW + 1), NULL, TEXT("WindowClass"), NULL,
};
if (!RegisterClassEx(&wcex))
return MessageBox(NULL, TEXT("Cannot register class !"), TEXT("Error"), MB_ICONERROR | MB_OK);
int nX = (GetSystemMetrics(SM_CXSCREEN) - nWidth) / 2, nY = (GetSystemMetrics(SM_CYSCREEN) - nHeight) / 2;
hWnd = CreateWindowEx(0, wcex.lpszClassName, TEXT("Test TreeView Drag&Drop"), WS_OVERLAPPEDWINDOW, nX, nY, nWidth, nHeight, NULL, NULL, hInst, NULL);
if (!hWnd)
return MessageBox(NULL, TEXT("Cannot create window !"), TEXT("Error"), MB_ICONERROR | MB_OK);
ShowWindow(hWnd, SW_SHOWNORMAL);
UpdateWindow(hWnd);
MSG msg;
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
GdiplusShutdown(gdiplusToken);
return (int)msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
static HWND hWndButton = NULL, hWndStatic = NULL;
static HWND hWndTV;
int wmId, wmEvent;
switch (message)
{
case WM_CREATE:
{
hWndTV = CreateWindowEx(0, WC_TREEVIEW, TEXT(""),
WS_VISIBLE | WS_CHILD | WS_BORDER | TVS_HASLINES |
//TVS_HASBUTTONS | TVS_LINESATROOT | TVS_CHECKBOXES | WS_EX_CLIENTEDGE,
TVS_HASBUTTONS | TVS_LINESATROOT | WS_EX_CLIENTEDGE,
10, 10, 400, 542, hWnd, (HMENU)IDC_TV, hInst, NULL);
WCHAR wsURL[MAX_PATH] = TEXT("https://i.ibb.co/4NdT6hX/flowers.png");
WCHAR wsFilename[MAX_PATH];
HRESULT hr = URLDownloadToCacheFile(NULL, wsURL, wsFilename, ARRAYSIZE(wsFilename), 0x0, NULL);
if (SUCCEEDED(hr))
{
IStream* pStream = NULL;
hr = SHCreateStreamOnFile(wsFilename, STGM_READ | STGM_SHARE_DENY_WRITE, &pStream);
if (SUCCEEDED(hr))
{
HBITMAP hBitmap = NULL;
Gdiplus::Bitmap* pBitmap = new Gdiplus::Bitmap(pStream);
if (pBitmap)
{
pBitmap->GetHBITMAP(Gdiplus::Color(255, 255, 255), &hBitmap);
HIMAGELIST hImageList = ImageList_Create(32, 32, ILC_COLORDDB | ILC_MASK, 43, 1);
ImageList_AddMasked(hImageList, hBitmap, RGB(255, 0, 255));
DeleteObject(hBitmap);
TreeView_SetImageList(hWndTV, hImageList, TVSIL_NORMAL);
FillTreeView(hWndTV);
}
}
}
// hWndStatic = CreateWindowEx(0, TEXT("Static"), TEXT(""), WS_CHILD | WS_VISIBLE | SS_BITMAP, 10, 10, 200, 200, hWnd, (HMENU)IDC_STATIC, hInst, NULL);
// hWndButton = CreateWindowEx(0, L"Button", L"Click", WS_CHILD | WS_VISIBLE | BS_PUSHLIKE, 400, 60, 60, 32, hWnd, (HMENU)IDC_BUTTON, hInst, NULL);
return 0;
}
break;
case WM_COMMAND:
{
wmId = LOWORD(wParam);
wmEvent = HIWORD(wParam);
switch (wmId)
{
case IDC_BUTTON:
{
if (wmEvent == BN_CLICKED)
{
Beep(1000, 10);
}
}
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
}
break;
case WM_NOTIFY:
{
LPNMHDR pnmh = (LPNMHDR)lParam;
switch (pnmh->code)
{
case TVN_BEGINDRAG:
{
TreeView_OnBeginDrag(hWndTV, (LPNMTREEVIEW)pnmh);
}
break;
default:
return false;
}
return true;
}
break;
case WM_MOUSEMOVE:
{
if (_bLeftDragging)
{
HTREEITEM hItem;
POINT pt;
GetCursorPos(&pt);
MapWindowPoints(HWND_DESKTOP, hWndTV, (LPPOINT)&pt, 1);
TVHITTESTINFO tvhti = { 0 };
tvhti.pt = pt;
hItem = TreeView_HitTest(hWndTV, &tvhti);
if (hItem != NULL)
{
if (hItem != _hItemOld || _hItemOld == NULL)
{
ImageList_DragShowNolock(FALSE);
TreeView_SelectDropTarget(hWndTV, hItem);
ImageList_DragShowNolock(TRUE);
_hItemOld = hItem;
}
}
ImageList_DragMove(pt.x, pt.y);
if (IsChild(hWndTV, hItem, _hItemDragged) || hItem == _hItemDragged)
SetCursor(LoadCursor(NULL, IDC_NO));
else
SetCursor(LoadCursor(NULL, IDC_ARROW));
SCROLLINFO si = { 0 };
si.cbSize = sizeof(si);
si.fMask = SIF_PAGE | SIF_POS | SIF_RANGE;
if (GetScrollInfo(hWndTV, SB_VERT, &si))
{
if ((tvhti.flags == TVHT_BELOW) && (si.nPos < (si.nMax - max((int)si.nPage - 1, 0))))
{
SendMessage(hWndTV, WM_VSCROLL, MAKELONG(SB_LINEDOWN, 0), 0L);
InvalidateRect(hWndTV, NULL, TRUE);
UpdateWindow(hWndTV);
}
if ((tvhti.flags == TVHT_ABOVE) && (si.nPos > si.nMin))
{
SendMessage(hWndTV, WM_VSCROLL, MAKELONG(SB_LINEUP, 0), 0L);
InvalidateRect(hWndTV, NULL, TRUE);
UpdateWindow(hWndTV);
}
}
}
}
break;
case WM_LBUTTONUP:
{
if (_bLeftDragging)
{
HTREEITEM hItemTarget;
ImageList_EndDrag();
ReleaseCapture();
SetCursor(LoadCursor(NULL, IDC_ARROW));
_bLeftDragging = false;
hItemTarget = TreeView_GetDropHilight(hWndTV);
ImageList_DragShowNolock(FALSE);
TreeView_SelectDropTarget(hWndTV, NULL);
ImageList_DragShowNolock(TRUE);
if (hItemTarget != NULL)
{
//TVITEM tvItem = { 0 };
//tvItem.mask = TVIF_PARAM | TVIF_HANDLE;
//tvItem.hItem = hItemTarget;
//TreeView_GetItem(hWndTV, &tvItem);
//tvItem = { 0 };
//tvItem.mask = TVIF_PARAM | TVIF_HANDLE;
//tvItem.hItem = _hTreeItemDragged;
//TreeView_GetItem(hWndTV, &tvItem);
CopyItemBranch(hWndTV, _hItemDragged, hItemTarget);
_hItemOld = NULL;
}
}
}
break;
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hDC = BeginPaint(hWnd, &ps);
EndPaint(hWnd, &ps);
}
break;
case WM_DESTROY:
{
PostQuitMessage(0);
return 0;
}
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
void TreeView_OnBeginDrag(HWND hWndTV, LPNMTREEVIEW pnmtv)
{
HIMAGELIST himlDrag;
HTREEITEM hItem = pnmtv->itemNew.hItem;
himlDrag = TreeView_CreateDragImage(hWndTV, hItem);
ImageList_BeginDrag(himlDrag, 0, 0, 0);
ImageList_DragEnter(hWndTV, pnmtv->ptDrag.x, pnmtv->ptDrag.y);
//ImageList_DragEnter(hWndTV, 0, 0);
SetCursor(LoadCursor(NULL, IDC_ARROW));
SetCapture(hWnd);
ImageList_Destroy(himlDrag);
_bLeftDragging = true;
_hItemDragged = hItem;
return;
}
void FillTreeView(HWND hWndTreeView)
{
WCHAR wsText[MAX_PATH];
TV_ITEM tvi;
TVINSERTSTRUCT tvins;
HTREEITEM hPrev = NULL;
for (int i = 0; i < 10; i++)
{
tvi.mask = TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE;
wsprintf(wsText, TEXT("Item Number %d"), i);
tvi.pszText = wsText;
tvi.cchTextMax = MAX_PATH;
tvi.iImage = i;
tvi.iSelectedImage = i;
tvins.hInsertAfter = TVI_LAST;
tvins.item = tvi;
tvins.hParent = TVI_ROOT;
hPrev = TreeView_InsertItem(hWndTreeView, &tvins);
tvins.hParent = hPrev;
for (int j = 0; j < 10; j++)
{
wsprintf(wsText, TEXT("SubItem Number %d-%d"), i, j);
tvi.pszText = wsText;
HTREEITEM hChild = TreeView_InsertItem(hWndTreeView, &tvins);
}
}
}
BOOL IsChild(HWND hWndTV, HTREEITEM hChild, HTREEITEM hParent)
{
HTREEITEM hCurrentParent;
hCurrentParent = hChild;
hCurrentParent = (HTREEITEM)SendMessage(hWndTV, TVM_GETNEXTITEM,
TVGN_PARENT, (LPARAM)hCurrentParent);
while (hCurrentParent)
{
if (hCurrentParent == hParent)
return TRUE;
hCurrentParent = (HTREEITEM)SendMessage(hWndTV, TVM_GETNEXTITEM,
TVGN_PARENT, (LPARAM)hCurrentParent);
}
return FALSE;
}
// Adapted from MS sample TreeViewDragAndDropV6.exe
// _CopyItem: Copy Item given to the destination as a child.
HTREEITEM _CopyItem(HWND hWndTV, HTREEITEM hTreeItemFrom, HTREEITEM hTreeItemTo)
{
const int ciBuffSize = MAX_PATH;
if (hTreeItemFrom == NULL || hTreeItemTo == NULL || hTreeItemFrom == hTreeItemTo)
{
return FALSE;
}
TCHAR szBuff[ciBuffSize];
TVINSERTSTRUCT tvs;
tvs.item.mask = TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_TEXT;
tvs.item.pszText = szBuff;
tvs.item.cchTextMax = ciBuffSize;
tvs.item.hItem = hTreeItemFrom;
if (TreeView_GetItem(hWndTV, &(tvs.item)) == FALSE)
{
return FALSE;
}
tvs.hParent = hTreeItemTo;
tvs.hInsertAfter = TVI_LAST;
HTREEITEM hNewItem = TreeView_InsertItem(hWndTV, &tvs);
TreeView_Expand(hWndTV, hTreeItemTo, TVE_EXPAND);
return hNewItem;
}
HTREEITEM CopyItemBranch(HWND hWndTV, HTREEITEM hTreeItemFrom, HTREEITEM hTreeItemTo)
{
if (IsChild(hWndTV, hTreeItemTo, hTreeItemFrom))
return NULL;
HTREEITEM hItemInsert, hItemChild;
hItemInsert = _CopyItem(hWndTV, hTreeItemFrom, hTreeItemTo);
hItemChild = (HTREEITEM)SendMessage(hWndTV, TVM_GETNEXTITEM,
TVGN_CHILD, (LPARAM)hTreeItemFrom);
while (hItemChild != 0)
{
CopyItemBranch(hWndTV, hItemChild, hItemInsert);
hItemChild = (HTREEITEM)SendMessage(hWndTV, TVM_GETNEXTITEM,
TVGN_NEXT, (LPARAM)hItemChild);
}
return hItemInsert;
}