Dragging a shell object, part 5: Making somebody else do the heavy lifting

Date:December 10, 2004 / year-entry #418
Tags:code
Orig Link:https://blogs.msdn.microsoft.com/oldnewthing/20041210-00/?p=37063
Comments:    3
Summary:Creating that drag image was a bit of work. Fortunately, the listview control is willing to do some of the work for you. Throw away the OnLButtonDown function (and the HANDLE_MESSAGE that goes with it). Instead, we'll make the listview do all our presentation for us. BOOL OnCreate(HWND hwnd, LPCREATESTRUCT lpcs) { g_hwndChild = CreateWindow(WC_LISTVIEW,...

Creating that drag image was a bit of work. Fortunately, the listview control is willing to do some of the work for you.

Throw away the OnLButtonDown function (and the HANDLE_MESSAGE that goes with it). Instead, we'll make the listview do all our presentation for us.

BOOL
OnCreate(HWND hwnd, LPCREATESTRUCT lpcs)
{
  g_hwndChild = CreateWindow(WC_LISTVIEW, NULL,
                             WS_CHILD | WS_VISIBLE | LVS_ICON |
                             LVS_SHAREIMAGELISTS, // flag added 13 Dec
                             0, 0, 0, 0,
                             hwnd, (HMENU)1, g_hinst, 0);
  if (!g_hwndChild) return FALSE;

  SHFILEINFOW sfi;
  HIMAGELIST himl = (HIMAGELIST)
    SHGetFileInfoW(g_pszTarget, 0, &sfi, sizeof(sfi),
                   SHGFI_SYSICONINDEX |
                   SHGFI_DISPLAYNAME | SHGFI_LARGEICON);
  if (!himl) return FALSE;

  ListView_SetImageList(g_hwndChild, himl, LVSIL_NORMAL);

  LVITEM item;
  item.iSubItem = 0;
  item.mask = LVIF_TEXT | LVIF_IMAGE;
  item.pszText = sfi.szDisplayName;
  item.iImage = sfi.iIcon;
  if (ListView_InsertItem(g_hwndChild, &item) < 0)
    return FALSE;

  return TRUE;
}

We now let the listview control worry about the icon and its text and all the other UI that goes along with it. And we can make the listview worry about the drag image, too.

void OnBeginDrag(HWND hwnd, NMLISTVIEW *plv)
{
  IDataObject *pdto;
  if (SUCCEEDED(GetUIObjectOfFile(hwnd, g_pszTarget,
                   IID_IDataObject, (void**)&pdto))) {
    IDragSourceHelper *pdsh;
    if (SUCCEEDED(CoCreateInstance(CLSID_DragDropHelper, NULL,
                    CLSCTX_ALL, IID_IDragSourceHelper, (void**)&pdsh))) {
      pdsh->InitializeFromWindow(g_hwndChild, &plv->ptAction, pdto);
      pdsh->Release();
    }

    IDropSource *pds = new CDropSource();
    if (pds) {
      DWORD dwEffect;
      if (DoDragDrop(pdto, pds, DROPEFFECT_MOVE |
                     DROPEFFECT_COPY | DROPEFFECT_LINK,
                     &dwEffect) == DRAGDROP_S_DROP &&
          (dwEffect & DROPEFFECT_MOVE)) {
        DeleteFileW(g_pszTarget);
      }
      pds->Release();
    }
    pdto->Release();
  }
}

LRESULT OnNotify(HWND hwnd, int idCtrl, NMHDR *pnm)
{
  if (idCtrl == 1) {
    NMLISTVIEW *plv;
    switch (pnm->code) {
    case LVN_BEGINDRAG:
      plv = (NMLISTVIEW*)pnm;
      OnBeginDrag(hwnd, plv);
      break;
    }
  }
  return 0;
}

    HANDLE_MSG(hwnd, WM_NOTIFY, OnNotify);

Instead of detecting the drag operation, we let the listview do it and just wait for the LVN_BEGINDRAG notification, at which point we get the data object for the file we want to drag and ask the listview to create the drag image by passing its window handle to the IDragSourceHelper::InitializeFromWindow method.

The listview control does the work of generating the drag image and setting it into the data object. In our specific case, it may have been a toss-up which way is easier, but if you enable multiple-selection capability in the listview, using the IDragSourceHelper::InitializeFromWindow method is a major savings because the listview will do the work of generating the radial gradient alpha channel that you see when dragging multiple files in Explorer.

You may notice some color fringes around the icons generated by the listview. That's because we're using version 5 of the common controls, which doesn't support alpha channels very well. If you switch to version 6, you'll find that the fringes are gone and the icon looks a lot prettier.

That's all for now on the subject of initiating a drag/drop operation. Back to one-day topics for a while.


Comments (3)
  1. mfink says:

    According to the SHGetFileInfo() and ListView_SetImageList() function documentation on MSDN, don’t you have to set the LVS_SHAREIMAGELISTS style for this list control? I understand that the system image list is destroyed at exit anyway, but it would be cleaner, right?

  2. Raymond Chen says:

    Excellent point. Fixed.

  3. Mike Dunn says:

    And actually, you *must* use LVS_SHAREIMAGELISTS on 9x, because SHGetFileInfo() returns a handle to the real system image list, and you don’t want to destroy that. On NT, each process gets a copy, so destroying it isn’t as harmful.

Comments are closed.


*DISCLAIMER: I DO NOT OWN THIS CONTENT. If you are the owner and would like it removed, please contact me. The content herein is an archived reproduction of entries from Raymond Chen's "Old New Thing" Blog (most recent link is here). It may have slight formatting modifications for consistency and to improve readability.

WHY DID I DUPLICATE THIS CONTENT HERE? Let me first say this site has never had anything to sell and has never shown ads of any kind. I have nothing monetarily to gain by duplicating content here. Because I had made my own local copy of this content throughout the years, for ease of using tools like grep, I decided to put it online after I discovered some of the original content previously and publicly available, had disappeared approximately early to mid 2019. At the same time, I present the content in an easily accessible theme-agnostic way.

The information provided by Raymond's blog is, for all practical purposes, more authoritative on Windows Development than Microsoft's own MSDN documentation and should be considered supplemental reading to that documentation. The wealth of missing details provided by this blog that Microsoft could not or did not document about Windows over the years is vital enough, many would agree an online "backup" of these details is a necessary endeavor. Specifics include:

<-- Back to Old New Thing Archive Index