CListCtrl and Drag and Drop

I've got a list view in which I've got a list of files.  I'd like to have the use select some files and then drag then out to another folder where it copies those files.  Explorer does this very thing everyday so I know it's possible.
This will become worth more points once I have them available.
LVL 1
ElmerFudAsked:
Who is Participating?
 
RONSLOWConnect With a Mentor Commented:
Construct a COleDataSource object when you get a begin drag message in you list view, initialize it with the filename of the corresponging item (in CF_FILENAME format), and call the data source object's DoDragDrop member function.
0
 
RONSLOWCommented:
something like this ...

CString name = ... file name of selected item
int buflen = name.GetLength() + 1;
HANDLE hData =
::GlobalAlloc (GMEM_MOVEABLE | GMEM_SHARE, buflen);
LPTSTR* pString = (LPTSTR)::GlobalLock (hData);
strcpy(pString,string);
::GlobalUnlock (hData);
COleDataSource ods;
ods.CacheGlobalData (CF_FILENAME, hData);
ods.DoDragDrop (DROPEFFECT_COPY);


0
 
RONSLOWCommented:
Better is to use CF_HDROP instead of CF_FILENAME

Set up a DROPFILES object .. this supports a list of file name (double null terminated)

Lookup DROPFILE or CF_HDROP in the on-line help for further info

0
The 14th Annual Expert Award Winners

The results are in! Meet the top members of our 2017 Expert Awards. Congratulations to all who qualified!

 
ElmerFudAuthor Commented:
ok, I'm gonna try it.  I'm kind of confused as to where I should put this code, is there some event that is related to the user trying to drag an item from the ListView?  I'm gonna play around with it this morning and see what I come up with.  I've got 5 more points today.  This is worth 50 easy, I just didn't hav ethe points yesterday.
0
 
RONSLOWCommented:
I'd try a handler for LVN_BEGINDRAG.

0
 
ElmerFudAuthor Commented:
Ahhh!  That makes since.  :)  Gonna try it right now.  Be back ASAP.
0
 
RONSLOWCommented:
Good luck .. I haven't tried it myself yet (got my own problems at the moment) .. but you should have the concept at least.  I'll give it a go myself ASAP .. when the panic subsides

0
 
ElmerFudAuthor Commented:
Ahhh!  That makes since.  :)  Gonna try it right now.  Be back ASAP.
0
 
ElmerFudAuthor Commented:
I'm most certainly doing this wrong.  I'm very confused.  :) Here's my function.  I don't think I'm doing the multiple files thingy right. There is very little documentation on the clipboard formats which are built into Visual C++.

void CNoteDlg::OnBegindragFileList(NMHDR* pNMHDR, LRESULT* pResult)
{
  NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR;

  CString Files = "";
  int i = m_FileList.GetNextItem( -1, LVNI_ALL | LVNI_SELECTED);
  while( i != -1 )
  {
    Files += CurDir + "Recieved Files\\" + m_FileList.GetItemText(i, 0) + "\0\0";
    i = m_FileList.GetNextItem(i, LVNI_ALL | LVNI_SELECTED);
  }

  HANDLE hData = ::GlobalAlloc (GMEM_MOVEABLE | GMEM_SHARE, Files.GetLength() + 1);
  LPTSTR pString = (LPTSTR)::GlobalLock (hData);
  strcpy(pString, Files);
  ::GlobalUnlock (hData);
  COleDataSource ods;
  ods.CacheGlobalData(CF_HDROP, hData);
  ods.DoDragDrop (DROPEFFECT_COPY);
  GlobalFree(hData);
      
  *pResult = 0;
}

i'm pretty sure this line is wrong:
  ods.CacheGlobalData(CF_HDROP, hData);
I need to turn it into the right data structure.  I'm gonna fiddle with it some more.  Anyway Good luck with your troubles there.  Answer when ever you can.  I frequently find myself putting out the flames.  :)
-ElmerFud

0
 
ElmerFudAuthor Commented:
I'm most certainly doing this wrong.  I'm very confused.  :) Here's my function.  I don't think I'm doing the multiple files thingy right. There is very little documentation on the clipboard formats which are built into Visual C++.

void CNoteDlg::OnBegindragFileList(NMHDR* pNMHDR, LRESULT* pResult)
{
  NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR;

  CString Files = "";
  int i = m_FileList.GetNextItem( -1, LVNI_ALL | LVNI_SELECTED);
  while( i != -1 )
  {
    Files += CurDir + "Recieved Files\\" + m_FileList.GetItemText(i, 0) + "\0\0";
    i = m_FileList.GetNextItem(i, LVNI_ALL | LVNI_SELECTED);
  }

  HANDLE hData = ::GlobalAlloc (GMEM_MOVEABLE | GMEM_SHARE, Files.GetLength() + 1);
  LPTSTR pString = (LPTSTR)::GlobalLock (hData);
  strcpy(pString, Files);
  ::GlobalUnlock (hData);
  COleDataSource ods;
  ods.CacheGlobalData(CF_HDROP, hData);
  ods.DoDragDrop (DROPEFFECT_COPY);
  GlobalFree(hData);
      
  *pResult = 0;
}

i'm pretty sure this line is wrong:
  ods.CacheGlobalData(CF_HDROP, hData);
I need to turn it into the right data structure.  I'm gonna fiddle with it some more.  Anyway Good luck with your troubles there.  Answer when ever you can.  I frequently find myself putting out the flames.  :)
-ElmerFud

0
 
RONSLOWCommented:
You cannot build up strings with embedded '\0' in them using CString .. as it takes the nul char as meaning the end of string.

I suggest building the CString with, say, ';' separators, then doing a strdup on it and changing all the ';' into '\0'.  Or just build it up in a char[] buffer yourself.

Then try again.

PS: You probably graded too early on this one .. if you cannot get it to work then other experts cannot come in here to help you further (if I am unable to do so).

But thanks anyway for the points and appreciation.

0
 
ElmerFudAuthor Commented:
I'm most certainly doing this wrong.  I'm very confused.  :) Here's my function.  I don't think I'm doing the multiple files thingy right. There is very little documentation on the clipboard formats which are built into Visual C++.

void CNoteDlg::OnBegindragFileList(NMHDR* pNMHDR, LRESULT* pResult)
{
  NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR;

  CString Files = "";
  int i = m_FileList.GetNextItem( -1, LVNI_ALL | LVNI_SELECTED);
  while( i != -1 )
  {
    Files += CurDir + "Recieved Files\\" + m_FileList.GetItemText(i, 0) + "\0\0";
    i = m_FileList.GetNextItem(i, LVNI_ALL | LVNI_SELECTED);
  }

  HANDLE hData = ::GlobalAlloc (GMEM_MOVEABLE | GMEM_SHARE, Files.GetLength() + 1);
  LPTSTR pString = (LPTSTR)::GlobalLock (hData);
  strcpy(pString, Files);
  ::GlobalUnlock (hData);
  COleDataSource ods;
  ods.CacheGlobalData(CF_HDROP, hData);
  ods.DoDragDrop (DROPEFFECT_COPY);
  GlobalFree(hData);
      
  *pResult = 0;
}

i'm pretty sure this line is wrong:
  ods.CacheGlobalData(CF_HDROP, hData);
I need to turn it into the right data structure.  I'm gonna fiddle with it some more.  Anyway Good luck with your troubles there.  Answer when ever you can.  I frequently find myself putting out the flames.  :)
-ElmerFud

0
 
ElmerFudAuthor Commented:
I just learned a good lesson.  don't use the reload button in Expert's Exchange  :)  I'll try that.
0
 
ElmerFudAuthor Commented:
Why am I getting this error?

E:\Projects\cpp\vtifs\NoteDlg.cpp(420) : error C2065: 'CF_FILENAME' : undeclared identifier

I searched for it and I got:

Searching for 'CF_FILENAME'...
C:\Program Files\devstudio\VC\include\SHLOBJ.H(2235):#define CFSTR_FILENAMEA         TEXT("FileName")                // CF_FILENAMEA
C:\Program Files\devstudio\VC\include\SHLOBJ.H(2236):#define CFSTR_FILENAMEW         TEXT("FileNameW")               // CF_FILENAMEW
C:\Program Files\devstudio\VC\include\SHLOBJ.H(2238):#define CFSTR_FILENAMEMAPA      TEXT("FileNameMap")             // CF_FILENAMEMAPA
C:\Program Files\devstudio\VC\include\SHLOBJ.H(2239):#define CFSTR_FILENAMEMAPW      TEXT("FileNameMapW")            // CF_FILENAMEMAPW
4 occurrence(s) have been found.

so I did:
#include <SHLOBJ.H>

I thought maybe I could get it to do one file.
0
 
RONSLOWCommented:
Use CF_HDROP instead - it allows multiple files [if you can build the string :-)]

If you want the CF_FILENAME format .. use CF_FILENAMEA (unless you are using UNICODE)

0
 
ElmerFudAuthor Commented:
I see.  I was using CF_DROP, but I couldn't get that to work so I went for the one fie drag.  There is no on-line help for CF_HDROP.  It is refered to in several places, but never defined.  I'll keep trying.  There is a definition for DROPFILE, but that doesn't do me any good.
0
 
ElmerFudAuthor Commented:
CF_FILENAMEA is also undeclared
0
 
ElmerFudAuthor Commented:
typedef struct _DROPFILES {
    DWORD pFiles; // offset of file list
    POINT pt;     // drop point (coordinates depend on fNC)
    BOOL fNC;     // see below
    BOOL fWide;   // TRUE if file contains wide characters,
                  // FALSE otherwise
} DROPFILES, FAR * LPDROPFILES

what is the "offset of file list"?  I've been passing in a pointer to the filelist.
0
 
RONSLOWCommented:
oops meant CFSTR_FILENAMEA

anyway .. docs say ...
===
CF_HDROP format

The global memory object contains a DROPFILES structure. If the object was copied to the clipboard as part of a drag-and-drop operation, the pt member of DROPFILES includes the coordinates of the point where the drop occurred. The pFiles member is the offset to a double null-terminated list of file names. An application can retrieve information from the data object by passing the object's handle to the DragQueryFile and DragQueryPoint functions.
===

so .. if you place the list of names directly after the DROPFILES struct then pFiles would be 'sizeof(DROPFILES)' (I guess).

0
 
RONSLOWCommented:
I got this from online help on CF_HDROP (I've got MSDN though .. but should be in platform SDK help?)

There isn't a lot though.
0
 
RONSLOWCommented:
There is a MS sample callsed FILEDRAG.EXE that you can donwload from MS FTP site .. it might help

0
 
RONSLOWCommented:
It has a routine...

HGLOBAL CreateHDrop()
{
  HGLOBAL hGlobal;
  // for my sample I am assuming number of files as 2
  int cnt = 2;
  // allocate space for DROPFILE structure plus the number of
  // file and one extra byte for final NULL terminator
  hGlobal = GlobalAlloc(GHND|GMEM_SHARE,(DWORD) (sizeof(DROPFILES)+(_MAX_PATH)*cnt+1));
  if(hGlobal == NULL) return hGlobal;
  LPDROPFILES pDropFiles;
  pDropFiles = (LPDROPFILES)GlobalLock(hGlobal);
  // set the offset where the starting point of the file start
  pDropFiles->pFiles = sizeof(DROPFILES);
  // file does not contain wide characters
  pDropFiles->fWide=FALSE;
  int CurPosition = sizeof(DROPFILES);
  for (int i = 0;i < cnt;i++) {
    TCHAR pszTempPath[MAX_PATH];
    TCHAR szFileName[_MAX_PATH];
    //Get File Name
    wsprintf(szFileName, "myfile%d.txt",i);
    // Create the Temp FileName, Also Note, I don't delete it.
    // since the file has exist on the hard drive. for an
    //application to open it.
    // if I delete it this will not work.
    CreateMyTempFile(szFileName, pszTempPath);
    // copy the file into global memory
    lstrcpy((LPSTR)((LPSTR)(pDropFiles)+CurPosition),TEXT(pszTempPath));
    // move the current position beyond the file name copied
    // don't forget the Null terminator (+1)
    CurPosition +=strlen(pszTempPath)+1;
  }
  // final null terminator as per CF_HDROP Format specs.
  ((LPSTR)pDropFiles)[CurPosition]=0;
  GlobalUnlock(hGlobal);
  return hGlobal;
}

This might be of some help

0
 
ElmerFudAuthor Commented:
I'll give that a shot.  Thanks.
0
 
ElmerFudAuthor Commented:
Didn't work.  I'm begining to wonder if there's isn't something wrong with how I set up my project.  Does it have to be an "ole server" to work?  Is there any check box or anything that I'm missing?  Could you e-mail me the source for that whole project rhamilto@geocites.com?
0
 
ElmerFudAuthor Commented:
I though yuo'd be glad to know that I got it to work.  :)  You need to make this call beforee doig any OLE stuff:

      OleInitialize(NULL);

and this call when you're done:

      OleUninitialize();

Silly me.  Anyway, thank you for your time.
-Elmer
0
 
RONSLOWCommented:
Aha .. forgot about that .. sorry I've been asleep.  I live in Australia .. so I sleep while you work [actually, sometimes I sleep while _I_ work :-)].

Glad you've got it going.

I'd be very interested to see the code you ended up with, if you wouldn't mind emailing me at roger.onslow@compsys.com.au

Thanks

0
Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.

All Courses

From novice to tech pro — start learning today.