Click here to Skip to main content
15,887,365 members
Articles / Desktop Programming / MFC
Article

CDropEdit

Rate me:
Please Sign up or sign in to vote.
4.57/5 (14 votes)
26 Feb 2002CPOL1 min read 165.9K   1.9K   35   42
Drag and drop files onto a CEdit, instead of using a file open dialog.

Introduction

This slight variation on the standard CEdit control allows users to drag and drop a file onto the control, instead of typing the path to the file. When a file (or folder) is dropped onto this control, the path to that file becomes the window text. This is an alternative to using a typical file-browse dialog.

Why

This is just another thing I do to make my apps easy to use. I never rely on this as the only way to get a path into the control, just another option. Plus, the code here can be adopted to almost any other control, so you can drag onto combo boxes, list boxes, etc., so I do this to any control that can accept a file name.

How

Using this class is fairly easy:

  1. First, call ::CoInitialize(NULL); in your
    CWinApp::InitInstance
    function. Also call ::CoUninitialize(); in your CWinApp::ExitInstance.
  2. Add a normal edit control to your dialog. Be sure to check its "Accept Files" property.
  3. In the header for your dialog class, declare a member variable of type CDropEdit (be sure to
    #include
    "CDropEdit.h"
    !)
  4. CDropEdit m_dropEdit;
  5. In your dialog's OnInitDialog, call:
  6. m_dropEdit.SubclassDlgItem(IDC_YOUR_EDIT_ID, this);
  7. If you want the edit control to handle directories, call:
  8. m_dropEdit.SetUseDir(TRUE);
  9. If you want the edit control to handle files, call:
  10. m_dropEdit.SetUseDir(FALSE);
  11. That's it

More

This code just shows the basic technique for getting the names of dropped files. It's fairly easy to modify this code to handle multiple dropped files, if you're filling a list box, for example. And, it's pretty simple to change this code to do other things with the dropped files, like display them, or execute them, or delete them, or chop them into little bits, or send them as attachments to 1,000 strangers, etc...

Have fun.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer
United States United States
Chris Losinger was the president of Smaller Animals Software, Inc. (which no longer exists).

Comments and Discussions

 
QuestionHow to do in CComboBox Pin
k7773-Jun-13 8:49
k7773-Jun-13 8:49 
AnswerRe: How to do in CComboBox Pin
Chris Losinger3-Jun-13 9:08
professionalChris Losinger3-Jun-13 9:08 
GeneralRe: How to do in CComboBox Pin
k7774-Jun-13 2:28
k7774-Jun-13 2:28 
GeneralRe: How to do in CComboBox Pin
Chris Losinger4-Jun-13 3:25
professionalChris Losinger4-Jun-13 3:25 
here's my class:

DropComboBox.h
#pragma once

// CDropComboBox

class CDropComboBox : public CComboBox
{
	DECLARE_DYNAMIC(CDropComboBox)


public:
 	inline void SetUseDir(bool u)			{m_bUseDir=u;}
	inline bool IsUseDir() const			{return m_bUseDir;}

public:
	CDropComboBox();
	virtual ~CDropComboBox();

protected:
   virtual void PreSubclassWindow();
   CString ExpandShortcut(CString &inFile);

protected:
	//{{AFX_MSG(CDropComboBox)
	afx_msg void OnDropFiles(HDROP dropInfo);
	//}}AFX_MSG

	DECLARE_MESSAGE_MAP()


   bool		m_bUseDir;
};


DropComboBox.cpp
// DropComboBox.cpp : implementation file
//

#include "stdafx.h"
#include "DEDemo.h"
#include "DropComboBox.h"
#include <sys/types.h>
#include <sys/stat.h>


// CDropComboBox

IMPLEMENT_DYNAMIC(CDropComboBox, CComboBox)

CDropComboBox::CDropComboBox()
{
m_bUseDir=FALSE;
}

CDropComboBox::~CDropComboBox()
{
}


BEGIN_MESSAGE_MAP(CDropComboBox, CComboBox)
	//{{AFX_MSG_MAP(CDropComboBox)
	ON_WM_DROPFILES()
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()



// CDropComboBox message handlers
/////////////////////////////////////////////////////////////////////////////
//	handles WM_DROPFILES

void CDropComboBox::OnDropFiles(HDROP dropInfo)
{
	// Get the number of pathnames that have been dropped
	WORD wNumFilesDropped = DragQueryFile(dropInfo, -1, NULL, 0);

	CString csFirstFile = _T("");

	// there may be many, but we'll only use the first
	if (wNumFilesDropped > 0)
	{
		// Get the number of bytes required by the file's full pathname
		WORD wPathnameSize = DragQueryFile(dropInfo, 0, NULL, 0);

		// Allocate memory to contain full pathname & zero byte
		wPathnameSize+=1;

		char * pFile = new TCHAR[wPathnameSize];
		if (pFile == NULL)
		{
			ASSERT(0);
			DragFinish(dropInfo);
			return;
		}

		// Copy the pathname into the buffer
		DragQueryFile(dropInfo, 0, pFile, wPathnameSize);

		csFirstFile = pFile;

		// clean up
		delete [] pFile; 
	}

	// Free the memory block containing the dropped-file information
	DragFinish(dropInfo);

	// if this was a shortcut, we need to expand it to the target path
	CString csExpandedFile = ExpandShortcut(csFirstFile);

	// if that worked, we should have a real file name
	if (!csExpandedFile.IsEmpty()) 
	{
		csFirstFile = csExpandedFile;
	}

	if (!csFirstFile.IsEmpty())
	{
		struct _stat buf;
	
		// get some info about that file
		int result = _stat( csFirstFile, &buf );
		if( result == 0 ) 
		{
			// verify that we have a dir (if we want dirs)
			if ((buf.st_mode & _S_IFDIR) == _S_IFDIR) 
			{
				if (m_bUseDir)
				{
					SetWindowText(csFirstFile);
				}

			// verify that we have a file (if we want files)
			} 
			else if ((buf.st_mode & _S_IFREG) == _S_IFREG) 
			{
				if (!m_bUseDir)
				{
					SetWindowText(csFirstFile);
				}
			}
		}
	}
}

/////////////////////////////////////////////////////////////////////////////

void CDropComboBox::PreSubclassWindow() 
{
	DragAcceptFiles(TRUE);
	
	CComboBox::PreSubclassWindow();
}

/////////////////////////////////////////////////////////////////////////////

CString CDropComboBox::ExpandShortcut(CString &inFile)
{
	CString outFile;

    // Make sure we have a path
    ASSERT(inFile != _T(""));

    IShellLink* psl;
    HRESULT hres;
    LPTSTR lpsz = inFile.GetBuffer(MAX_PATH);

    // Create instance for shell link
    hres = ::CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink, (LPVOID*) &psl);

    if (SUCCEEDED(hres))
    {
        // Get a pointer to the persist file interface
        IPersistFile* ppf;

        hres = psl->QueryInterface(IID_IPersistFile, (LPVOID*) &ppf);
        if (SUCCEEDED(hres))
        {
            // Make sure it's ANSI
            WCHAR wsz[MAX_PATH];
            ::MultiByteToWideChar(CP_ACP, 0, lpsz, -1, wsz, MAX_PATH);

            // Load shortcut
            hres = ppf->Load(wsz, STGM_READ);

            if (SUCCEEDED(hres)) 
			{
				WIN32_FIND_DATA wfd;

				// find the path from that
				HRESULT hres = psl->GetPath(outFile.GetBuffer(MAX_PATH), 
								MAX_PATH,
								&wfd, 
								SLGP_UNCPRIORITY);

				outFile.ReleaseBuffer();
            }

            ppf->Release();
        }

        psl->Release();
    }

	inFile.ReleaseBuffer();

	// if this fails, outFile == ""
    return outFile;
}


Here's how I use it:
DEDemoDlg.h
...
#include "DropComboBox.h"

class CDEDemoDlg : public CDialog
{
...

public:
CDropComboBox m_dropCombo;


DEDemoDlg.cpp:
C#
void CDEDemoDlg::DoDataExchange(CDataExchange* pDX)
{
   CDialog::DoDataExchange(pDX);
   //{{AFX_DATA_MAP(CDEDemoDlg)
   // NOTE: the ClassWizard will add DDX and DDV calls here
   //}}AFX_DATA_MAP
   DDX_Control(pDX, IDC_COMBO1, m_dropCombo);
}


BOOL CDEDemoDlg::OnInitDialog()
{
   ...

   m_dropCombo.SetUseDir(FALSE);

   ...
}


No subclassing required. This works fine on Win7, VS2008.

GeneralRe: How to do in CComboBox Pin
k7774-Jun-13 4:28
k7774-Jun-13 4:28 
Questionhow to use this code with may textbox? Pin
ngthtra20-Jun-12 21:08
ngthtra20-Jun-12 21:08 
AnswerRe: how to use this code with may textbox? Pin
Chris Losinger21-Jun-12 1:07
professionalChris Losinger21-Jun-12 1:07 
GeneralRe: how to use this code with may textbox? Pin
ngthtra21-Jun-12 16:22
ngthtra21-Jun-12 16:22 
GeneralRe: how to use this code with may textbox? Pin
Chris Losinger21-Jun-12 17:10
professionalChris Losinger21-Jun-12 17:10 
GeneralWhen using this in a CFormView instead of a CDialog Pin
Harold Bamford23-Jul-09 11:15
Harold Bamford23-Jul-09 11:15 
Questionnumeric Pin
nasma14-Jun-08 8:05
nasma14-Jun-08 8:05 
QuestionIt doesn't work when inside a groupbox Pin
vakka28-Feb-07 20:13
vakka28-Feb-07 20:13 
AnswerRe: It doesn't work when inside a groupbox Pin
ryoaska2-Mar-07 16:47
ryoaska2-Mar-07 16:47 
AnswerRe: It doesn't work when inside a groupbox Pin
ryoaska2-Mar-07 20:03
ryoaska2-Mar-07 20:03 
GeneralAnother way to do it without subclassing Pin
DevaRoo24-May-06 9:05
DevaRoo24-May-06 9:05 
GeneralRe: Another way to do it without subclassing Pin
Chris Losinger24-May-06 9:32
professionalChris Losinger24-May-06 9:32 
GeneralRe: Another way to do it without subclassing Pin
DevaRoo25-May-06 11:15
DevaRoo25-May-06 11:15 
GeneralA 5 after years Pin
Mircea Puiu26-Oct-05 7:54
Mircea Puiu26-Oct-05 7:54 
GeneralGoooooood! Pin
cjwn26-Oct-05 7:11
cjwn26-Oct-05 7:11 
GeneralCustom Control Pin
yofnik1-Dec-03 12:40
yofnik1-Dec-03 12:40 
GeneralRe: Custom Control Pin
st0per5-Aug-04 15:15
st0per5-Aug-04 15:15 
GeneralVery cool Pin
alex.barylski25-Aug-02 16:30
alex.barylski25-Aug-02 16:30 
Questionis 'now what?' a stupid question? Pin
cedoc21-Aug-02 5:53
cedoc21-Aug-02 5:53 
AnswerRe: is 'now what?' a stupid question? Pin
Chris Losinger21-Aug-02 5:57
professionalChris Losinger21-Aug-02 5:57 
GeneralRe: is 'now what?' a stupid question? Pin
cedoc21-Aug-02 8:41
cedoc21-Aug-02 8:41 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.