Transperent Edit Box

Creating a transparent edit box

Background of a window is not always white. Sometimes it may be green or gray. When we create a control like combo box or edit box the color of these controls turn out to be same as the background color of the window. This article seir background by default is white. White colored controls on green or gray background do not look very impressive. So instead of letting the edit box or the list box getting created in white color we can create them as controls with transparent background. When we do so the background knows how to create a transparent edit box so that it will get displayed in the color which is same as its parent window. You can use similar logic for other controls as well. An edit box is created using the object of CEdit class. The CWnd::OnCtlColor( ) function has been overridden in the view class CChildview. In this function a NULL brush has been created and returned. The OnCtlColor( ) function is called whenever the child control is to be drawn. Since a NULL brush is returned from the function the control is redrawn using the NULL brush thereby creating a transparent edit box.


Download

Owner Drawn Combo Box

Creating ownerdrawn combo box
By default, items in a combo box consist of strings of text. Should you need a combo box that displays graphical images instead of text, you can create an owner-drawn combo box. To create the owner-drawn combo box a class is derived from CComboBox. An object of the derived class is created and CComboBox::Create( ) is called to create the combo box. CComboBox::MeasureItem( ) and CComboBox::DrawItem( ) are overridden. When an item in an owner-drawn combo box needs to be drawn (or redrawn), Windows sends the combo box's parent a WM_DRAWITEM message with a pointer to a DRAWITEMSTRUCT structure containing all the information required to do the drawing, including a device context handle and a 0-based index identifying the item to be drawn. Before the first WM_DRAWITEM message arrives, the combo box's parent receives one or more WM_MEASUREITEM messages requesting the height of the items in the combo box. In this program an array of colors is created which is used to draw the combo box items when the window receives WM_DRAWITEM message. The DeflateRect( ) function decides the gap between two items. A rectangle is drawn around the item on which the cursor is placed. The color for highlighted item is retrieved using ::GetSysColor( ) function.


Download

Creatin Directory Tree Control With Check Boxes

This program displays entire directory structure of all the drives in a tree control. The tree control also displays check boxes in front of each directory which can be used to select or deselect the directory. We have created round check boxes instead of traditional square check boxes. User can check or uncheck the directory by clicking on the check box. Whether all of the sub-directories are checked can be known by the grayed bitmap displayed in front of the parent directory.

Generally directory tree with check boxes is needed while creating an anti-viral software in which user can check or uncheck the directories depending upon whether he wants to scan the directory or not.
Check boxes actually are bitmaps using which image list is created and images are loaded depending on the status (Checked/Unchecked) of the directory.

In this program, firstly only the drives are displayed with checked status. When user double clicks on the label or clicks on the plus sign the tree is expanded and directories of that drive are displayed in. Since the drive is checked all the directories also are displayed with checked sign. Now if the drive is unchecked all the directories also become unchecked.
This control only displays directories. You can anytime modify this program to display files of the selected directory.


Download

Priting List View Control Items

Many a times we need to print the contents listed in a list control. For example, generally in an anti virus software the details such as which files are infected, cured, quarantined etc. are listed in a list control which user may wish to print. We can implement the logic to print the list control. Here, we intend to print a list control in which filename, file size and date are displayed.

Create an SDI application and derive the view class from CListView class. Add appropriate columns and data to the list control. Here we will discuss only the handlers that are used for printing. Firstly add the following variables to the view class:

CStringList m_prnlist, m_reportlist ;

CFont m_prnfont ;

int ht, wd, linesperpage, charsperline, maxpage ;

Modify the OnPreparePrinting( ) function as shown below. Here, we have read the data from the list control and added it in a CStringList. We have also added the column names to the list.

BOOL CSortlistView::OnPreparePrinting ( CPrintInfo* pInfo )

{

m_reportlist.RemoveAll( ) ;

CString str, temp ;

str.Format ( "%25s %25s %25s", " Filename", " Size ", " Date " ) ;

m_reportlist.AddTail ( str ) ;

int listcount = GetListCtrl( ).GetItemCount( ) ;

for ( int i = 0 ; i <>

{

str.Format ( "%2d. ", i ) ;

for ( int j = 0 ; j <>

{

temp.Format ( "%25s", GetListCtrl( ).GetItemText ( i, j ) ) ;

str += temp ;

}

m_reportlist.AddTail ( str ) ;

}

return DoPreparePrinting ( pInfo ) ;

}

Let us now create a font in which the contents of the list control would be printed. Also, we need to calculate the characters per line, number of pages to be printed etc. This has been done in the OnBeginPrinting( ) function as shown below:

void CSortlistView::OnBeginPrinting ( CDC* pDC, CPrintInfo* pInfo )

{

m_prnfont.CreatePointFont ( 100, "Arial", pDC ) ;

CFont *prevfont = pDC -> SelectObject ( &m_prnfont ) ;

TEXTMETRIC tm ;

pDC -> GetTextMetrics ( &tm ) ;

ht = tm.tmHeight + tm.tmExternalLeading ;

wd = tm.tmAveCharWidth + 1 ;

int vertpixels = pDC -> GetDeviceCaps ( VERTRES ) ;

int horzpixels = pDC -> GetDeviceCaps ( HORZRES ) ;

CSize sz ( horzpixels, vertpixels ) ;

pDC -> DPtoLP ( &sz ) ;

linesperpage = sz.cy / ht ;

charsperline = sz.cx / wd ;

linesperpage -= 10 ;

CString tempstr ;

POSITION temppos = m_reportlist.GetHeadPosition( ) ;

int tempcount = m_reportlist.GetCount( ) ;

m_prnlist.RemoveAll( ) ;

for ( int i = 0 ; i <>

{

tempstr = m_reportlist.GetNext ( temppos ) ;

m_prnlist.AddTail ( tempstr ) ;

}

POSITION prnpos = m_prnlist.GetHeadPosition( ) ;

POSITION posprev1, posprev2 ;

int prncount = m_prnlist.GetCount( ) ;

CString str, pa ;

for ( i = 0 ; i <>

{

if ( prnpos == NULL )

break ;

posprev1 = posprev2 = prnpos ;

str = m_prnlist.GetNext ( prnpos ) ;

int len = str.GetLength( ) ;

if ( len > charsperline )

{

int linecount = max ( 1, ( len + ( charsperline - 1 ) ) /

charsperline ) ;

CString leftstr ;

int remainlen, leftlen = 0 ;

for ( int j = 0 ; j <>

{

leftstr = str.Left ( charsperline ) ;

m_prnlist.InsertAfter ( posprev2, leftstr ) ;

leftlen += leftstr.GetLength( ) ;

remainlen = len - leftlen ;

m_prnlist.GetNext ( posprev2 ) ;

str = str.Right ( remainlen ) ;

}

str = "" ;

pa = m_prnlist.GetAt ( posprev1 ) ;

m_prnlist.RemoveAt ( posprev1 ) ;

pa.FreeExtra( ) ;

}

else

{

m_prnlist.InsertAfter ( posprev1, str ) ;

pa = m_prnlist.GetAt ( posprev1 ) ;

m_prnlist.RemoveAt ( posprev1 ) ;

pa.FreeExtra( ) ;

}

}

prncount = m_prnlist.GetCount( ) ;

maxpage = max ( 1, ( prncount + ( linesperpage - 1 ) ) / linesperpage ) ;

pDC -> SelectObject ( prevfont ) ;

pInfo -> SetMaxPage ( maxpage ) ;

}

Let us now carry out the actual printing job in the OnPrint( ) function. We have displayed the title ‘Files List’ as the header and page number as the footer. In addition to the list control data we have also added the line number before each row.

void CSortlistView::OnPrint ( CDC* pDC, CPrintInfo* pInfo )

{

CFont *prevfont = pDC -> SelectObject ( &m_prnfont ) ;

TEXTMETRIC tm ;

pDC -> GetTextMetrics ( &tm ) ;

int horzpixels = pDC -> GetDeviceCaps ( HORZRES ) ;

CSize sz ( horzpixels, 0 ) ;

pDC -> DPtoLP ( &sz ) ;

int center = sz.cx / 2 ;

CString header = "Files List" ;

UINT l = center - ( ( header.GetLength( ) / 2 ) * tm.tmAveCharWidth ) ;

pDC -> TextOut ( l, 20, header ) ;

int prncount = m_prnlist.GetCount( ) ;

int start = ( ( pInfo -> m_nCurPage ) - 1 ) * linesperpage ;

int end = start + linesperpage ;

POSITION prnpos = m_prnlist.FindIndex ( linesperpage * ( ( pInfo -> m_nCurPage ) - 1 ) ) ;

for ( int i = start, y = ( ht * 5 ) ; i <>

{

pDC ->TextOut ( 10, y, m_prnlist.GetNext ( prnpos ) ) ;

y += ht ;

}

CString pagenumber ;

pagenumber.Format ( "%2d / %2d", pInfo -> m_nCurPage, maxpage ) ;

l = center - ( ( pagenumber.GetLength( ) / 2 ) * tm.tmAveCharWidth ) ;

pDC -> TextOut ( l, ( linesperpage + 5 + 3 ) * ht, pagenumber ) ;

pDC -> SelectObject ( prevfont ) ;

}

Finally let us do the clean up job in the OnEndPrinting( ) function given below:

void CSortlistView::OnEndPrinting ( CDC* pDC, CPrintInfo* pInfo )

{

m_reportlist.RemoveAll( ) ;

m_prnlist.RemoveAll( ) ;

m_prnfont.DeleteObject( ) ;

CListView::OnEndPrinting ( pDC, pInfo ) ;

}

Run the program and click the ‘Print’ menu item from the File menu. On doing this the contents of the list control would be printed in appropriate rows and columns.


Download

Sorting Items Listed in the List Control

In the applications like Find, Explorer, etc. where data is displayed in the list control if we click on a column the data listed in the control gets sorted in either ascending or descending order as per the values in the column. If we use the list control in our application we won’t get such a facility from MFC. We need to write code for this ourselves. Let us see how to do this.

Create an SDI application ‘sortlist’ with ‘Doc/View’ support. Select CListView as the base class of the view class. We would display the files of the root directory, their size and last access date. For this insert the columns and data in the list view control in OnInitialUpdate( ) function as given below:

void CSortlistView::OnInitialUpdate( )

{

CListView::OnInitialUpdate( ) ;

GetListCtrl( ).InsertColumn ( 0,"Filename",LVCFMT_LEFT, 100);

GetListCtrl( ).InsertColumn ( 1, "Size", LVCFMT_LEFT, 100 ) ;

GetListCtrl( ).InsertColumn ( 2, "Last Access Date", LVCFMT_LEFT, 150 ) ;

CFileFind file ;

CString name ;

int count = 0 ;

file.FindFile ( "C:\\*.*" ) ;

BOOL found = TRUE ;

while ( found )

{

found = file.FindNextFile( ) ;

if ( file.IsDots( ) )

continue ;

if ( file.IsDirectory( ) )

continue ;

name = file.GetFileName( ) ;

iteminfo *it = new iteminfo ;

it -> filename = name ;

it -> filesize = file.GetLength( ) ;

file.GetLastAccessTime ( it -> filedate ) ;

LV_ITEM lvi ;

lvi.mask = LVIF_TEXT | LVIF_PARAM ;

lvi.iItem = count ;

lvi.iSubItem = 0 ;

lvi.iImage = 0 ;

lvi.pszText = ( LPSTR ) name.operator LPCTSTR( ) ;

lvi.lParam = ( LPARAM ) it ;

GetListCtrl( ).InsertItem ( &lvi ) ;

count++ ;

}

}

Here, we have inserted three columns ‘Filename’, ‘Size’ and ‘Last Access Date’ by calling the function CListCtrl::InsertColumn( ). GetListCtrl( ) function returns a reference to the list control associated with the view using which we have called the member functions of CListCtrl. We have browsed the files of the root directory by calling member functions of CFileFind class. We have allocated memory in a pointer to iteminfo structure and stored the details of the file obtained in a structure iteminfo. iteminfo is a user defined structure which is given below:

struct iteminfo

{

CString filename ;

int filesize ;

CTime filedate ;

} ;

To insert the filename, size and date in the list control we have used LV_ITEM structure. The data is added to the list control using CListCtrl::InsertItem( ) function.

There are two ways in which we can display the data in list control so as to make the comparison of items possible. First way is to maintain a copy of each item’s data and assign the data to lParam parameter of InsertItem( ) function. However, if this way is used we will have to deallocate the memory in which the data is stored. The second way is to provide data to list control in response to LVN_GETDISPINFO notification. LVN_GETDISPINFO is sent to the parent window to provide information needed to display or sort a list view item. The OnGetdispinfo( ) function looks as shown below:

void CSortlistView::OnGetdispinfo ( NMHDR* pNMHDR, LRESULT* pResult )

{

LV_DISPINFO* pDispInfo = ( LV_DISPINFO* ) pNMHDR ;

CString string ;

if ( pDispInfo -> item.mask & LVIF_TEXT )

{

iteminfo *pitem = ( iteminfo * ) pDispInfo -> item.lParam ;

switch ( pDispInfo -> item.iSubItem )

{

case 0 : // File Name

strcpy ( pDispInfo -> item.pszText, pitem->filename ) ;

break ;

case 1 : // File Size

string.Format ( "%u", pitem -> filesize ) ;

strcpy ( pDispInfo -> item.pszText, string ) ;

break ;

case 2 : // Date and Time

CTime time ( pitem -> filedate ) ;

string.Format ( "%d/%0.2d/%0.2d",

time.GetMonth( ), time.GetDay( ),

time.GetYear( ) % 100 ) ;

strcpy ( pDispInfo -> item.pszText, string ) ;

break ;

}

}

*pResult = 0 ;

}

pNMHDR points to the LV_DISPINFO structure The structure’s item.lParam element contains the address of the iteminfo structure for the item in question and item.iSubItem element contains index of the subitem.

Add a handler for LVN_COLUMNCLICK notification and add the following code to it.

void CSortlistView::OnColumnclick ( NMHDR* pNMHDR, LRESULT* pResult )

{

NM_LISTVIEW* pNMListView = ( NM_LISTVIEW* ) pNMHDR ;

if ( m_flag == false )

m_flag = true ;

else

m_flag = false ;

GetListCtrl( ).SortItems ( compare, pNMListView -> iSubItem ) ;

*pResult = 0;

}

OnColumnclick( ) handler would get called whenever the user clicks the column. Since we intend to sort the contents in ascending order if clicked for the first time and then in ascending order if clicked again we have kept a flag to ascertain whether the sorting should take place in ascending or descending order. Add m_flag as a static data member of CSortlistView of type bool. Add the following statement in ‘sortlistView.cpp’.

bool CSortlistView::m_flag = true ;

Next, we have called CListCtrl::SortItems( ) function. The first parameter passed to SortItems( ) is address of a function that performs the comparison of arbitrarily selected items. It is a callback function which the control’s built in sorting routine calls. The second parameter is the zero based index of column on which user has clicked.

Declare the compare( ) function in ‘sortlistView.cpp’ as given below:

static int CALLBACK compare ( LPARAM lparam1, LPARAM lparam2, LPARAM lparamsort ) ;

The function compare( ) is given below:

static int CALLBACK compare ( LPARAM lparam1, LPARAM lparam2, LPARAM lparamsort )

{

iteminfo *item1 = ( iteminfo * ) lparam1 ;

iteminfo *item2 = ( iteminfo * ) lparam2 ;

iteminfo *item = new iteminfo ;

if ( CSortlistView::m_flag )

{

item = item2 ;

item2 = item1 ;

item1 = item ;

}

int res = 0 ;

switch ( lparamsort )

{

case 0 : // file name

res = item1 -> filename.CompareNoCase ( item2 -> filename ) ;

break ;

case 1 : // size

res = item1 -> filesize - item2 -> filesize ;

break ;

case 2 : // date

res = ::CompareFileTime ( ( FILETIME* ) &item1 -> filedate,

FILETIME* ) &item2 -> filedate ) ;

break ;

}

return res ;

}

The lparam1 and lparam2 parameters specify the item data for the two items being compared. The lparamsort parameter specifies the zero based index of the column that has been clicked. We have first checked the value of m_flag and swapped the items if it is true. The comparison function must return a negative value if the first item should precede the second, a positive value if the first item should follow the second, or zero if the two items are equivalent.

The memory allocated for each item is deallocated in OnDestroy( ) function as given below:

void CSortlistView::OnDestroy( )

{

int count = GetListCtrl( ).GetItemCount( ) ;

for ( int i = 0 ; i <>

delete ( iteminfo* ) GetListCtrl( ).GetItemData ( i ) ;

CListView::OnDestroy( ) ;

}

GetItemData( ) function returns the value stored in lParam element (pointer to iteminfo structure) of LV_ITEM structure.

Run the program and click on the ‘Filename’ column. You will see the items sorted as shown in the following figure:

Click here to Download Project

Using Drag And Drop on Tree List Control

Implementing Drag And Drop On Tree Items

we will implement drag and drop operation on tree control items. We will implement a tree control displaying all the drives and directories of the drives. We can drag the tree item i.e a folder and drop on the desired destination item. The folder actually gets moved to the destination folder.Create a dialog-based application ‘DDtreeitems’. Insert a new class by selecting ‘Insert | New Class’. Enter the class name as CDragDropTree and select CTreeCtrl as the base class. Click the ‘OK’ button. Add the OnCreate( ) handler to the CDragDropTree class and add the code to it as shown below:

int CDragDropTree::OnCreate ( LPCREATESTRUCT lpCreateStruct )

{

if ( CTreeCtrl::OnCreate ( lpCreateStruct ) == -1 )

return –1 ;

m_imglist.Create ( IDB_BITMAP1, 16, 1, CLR_NONE ) ;

SetImageList ( &m_imglist, TVSIL_NORMAL ) ;

CString drive ;

for ( char ch = 'A' ; ch <= 'Z' ; ch++ )

{

drive = ch ;

drive += ":\\" ;

if ( GetDriveType ( drive ) != DRIVE_NO_ROOT_DIR )

{

HTREEITEM hitem = InsertItem ( drive, 0, 0, TVI_ROOT ) ;

InsertItem ( "", 0, 0, hitem ) ;

}

}

return 0 ;

}

In the OnCreate( ) handler, firstly, we have created an image list by using the bitmap IDB_BITMAP1 to be displayed in the tree control. Add a member variable m_imglist of type CImageList to the CDragDropTree class. Since we have to display two different images for drives and folders our bitmap looks like below:

The image list is then set to the tree control by calling CTreeCtrl::SetImageList( ). Next, we have added all the available drives to the tree control. We have also added an empty item to each drive so that ‘+’ sign is shown for the item. When the user expands a particular drive the folders present on that drive get added to the tree. For this, we have handled TVN_ITEMEXPANDING notification in the CDragDropTree class which is received when the user expands the tree. The OnItemexpanding( ) handler is given below:

void CDragDropTree::OnItemexpanding ( NMHDR* pNMHDR, LRESULT* pResult )

{

NM_TREEVIEW* pNMTreeView = ( NM_TREEVIEW*) pNMHDR ;

HTREEITEM hitem = pNMTreeView -> itemNew.hItem ;

if ( pNMTreeView -> action == TVE_EXPAND )

{

deleteitems ( hitem ) ;

CString string = getpathfromitem ( hitem ) ;

adddirectories ( hitem, string ) ;

SortChildren ( hitem ) ;

}

else

{

deleteitems ( hitem ) ;

InsertItem ( "", 0, 0, hitem ) ;

}

*pResult = 0;

}

Before adding the folders to the tree we have deleted the child items by calling a user defined function deleteitems( ) we have passed the handle to the item being expanded to this function. The deleteitems( ) function is given below:

void CDragDropTree::deleteitems ( HTREEITEM hitem )

{

HTREEITEM childitem = GetNextItem ( hitem, TVGN_CHILD ) ;

while ( childitem )

{

DeleteItem ( childitem ) ;

childitem = GetNextItem ( hitem, TVGN_CHILD ) ;

}

}

After deleting the previous items we have called another user defined function getpathfromitem( ) from OnItemexpanding( ) function to get the valid path of the selected item. To this function we have passed the handle to the selected item. This function is given below:

CString CDragDropTree::getpathfromitem ( HTREEITEM hitem )

{

CString pathstr = GetItemText ( hitem ) ;

HTREEITEM hparent ;

CString string ;

while ( ( hparent = GetParentItem ( hitem ) ) != NULL )

{

string = GetItemText ( hparent ) ;

if ( string.Right ( 1 ) != "\\" )

string += "\\" ;

pathstr = string + pathstr ;

hitem = hparent ;

}

return pathstr ;

}

The getpathfromitem( ) function returns a valid path of the item. After the path is obtained we have added folders by calling a function adddirectories( ).We have passed the handle of the item to which folders are to be added and the path returned by getpathfromitem( ) function.

void CDragDropTree::adddirectories ( HTREEITEM hitem, CString path )

{

CFileFind fd ;

if ( path.Right ( 1 ) != "\\" )

path += "\\" ;

path += "*.*" ;

BOOL found ;

found = fd.FindFile ( path ) ;

if ( !found )

return ;

HTREEITEM childhitem ;

while ( found )

{

found = fd.FindNextFile( ) ;

if ( fd.IsDots( ) )

continue ;

if ( fd.IsDirectory( ) )

{

childhitem = InsertItem ( fd.GetFileName( ), 1, 1, hitem ) ;

InsertItem ( "", 0, 0, childhitem ) ;

}

}

}

The folders are browsed by using member functions of CFileFind class. The folders are added to the specified tree item by calling CTreeCtrl::InsertItem( ) function.

After adding the folders to the tree we have sorted them in ascendind order by calling CTreeCtrl::SortChildren( ) function from the OnItemexpanding( ) handler.

Add a variable m_tree of type CDragDropTree class to the CDDtreeitemsDlg class. Create the tree control by calling CDragDropTree::OnCreate( ) function from CDDtreeitemsDlg::OnInitDialog( ) function as shown below:

BOOL CDDtreeitemsDlg::OnInitDialog( )

{

/Appwizard added code

m_tree.Create ( WS_CHILD | WS_VISIBLE | TVS_HASLINES |

TVS_HASBUTTONS | TVS_LINESATROOT,

CRect ( 50, 20, 350, 300 ), this, 1 ) ;

return TRUE ;

}

Now if you run the program you will see the tree control with drives. If you expand the tree you will see the folders displayed in the tree. You can further expand a folder to view its sub-folders.

Now add variables and handlers for drag and drop operation. Add the following variables to CDragDropTree class.

CImageList* m_pdragimage ;

BOOL m_dragging ;

HTREEITEM m_hitemdrag, m_hitemdrop ;

When the user starts dragging an item TVN_BEGINDRAG notification is generated. Add the handler for this notification. The OnBegindrag( ) handler is given below:

void CDragDropTree::OnBegindrag ( NMHDR* pNMHDR, LRESULT*pResult )

{

NM_TREEVIEW* pNMTreeView = ( NM_TREEVIEW* ) pNMHDR ;

m_hitemdrag = pNMTreeView -> itemNew.hItem ;

m_hitemdrop = NULL ;

SetTimer ( 1, 75, NULL ) ;

m_pdragimage = CreateDragImage ( m_hitemdrag ) ;

if ( !m_pdragimage )

return ;

m_dragging = TRUE ;

m_pdragimage -> BeginDrag ( 0, CPoint ( -15, -15 ) ) ;

POINT pt = pNMTreeView -> ptDrag ;

ClientToScreen ( &pt ) ;

m_pdragimage -> DragEnter ( NULL, pt ) ;

SetCapture( ) ;

*pResult = 0;

}


Here, we have retrieved the item the user is dragging. We have set the timer for the scrolling purpose. We have called CTreeCtrl::CreateDragImage( ) function to create a dragging image for the given item in a tree view control CreateDragImage( ) returns pointer to the image list or NULL if no image is associated with the tree control. We have used a set of static functions provided by CImageList class for dragging an image in the window. The CImageList::BeginDrag( ) member function begins a drag operation. The first parameter passed to BeginDrag( ) is the index of the image to drag. The second parameter is the location of the hot spot within the image. The hot spot specifies the coordinates of the starting drag position which is typically the cursor position. We have specified (-15, -15) as the hot spot so that the dragged image is displayed below the cursor. The CImageList::DragEnter( ) function displays the drag image at a position mentioned by the second parameter. The ptDrag element of NM_TREEVIEW structure contains the mouse coordinates relative to the client area at the time when the event occurred. We have called SetCapture( ) function so as to get the mouse messages even if the user drags the image outside the tree control.

Add OnMouseMove( ) handler to the CDragDropTree class. This handler is given below:

void CDragDropTree::OnMouseMove ( UINT nFlags, CPoint point )

{

HTREEITEM hitem ;

UINT flags ;

if ( m_dragging )

{

POINT pt = point ;

ClientToScreen ( &pt ) ;

CImageList::DragMove ( pt ) ;

if ( ( hitem = HitTest ( point, &flags ) ) != NULL )

{

CImageList::DragShowNolock ( FALSE ) ;

SelectDropTarget ( hitem ) ;

m_hitemdrop = hitem ;

CImageList::DragShowNolock ( TRUE ) ;

}

}

CTreeCtrl::OnMouseMove(nFlags, point);

}

If the user is dragging the item we have moved the image to a new location by calling CImageList::DragMove( ) function. Next we have called CTreeCtrl::HitTest( ) function to see if the specified point lies on an item. It returns handle of the item at the specified point. The DragEnter( ) function locks all other updates to the given window during the drag operation. If we need to do any drawing during a drag operation, such as highlighting the target of a drag-and-drop operation we can temporarily hide the dragged image by using the DragShowNoLock( ) member function and perform our operation. Passing FALSE to the function hides the image whereas passing TRUE to it displays the image. As the user moves the cursor on items we have selected the item on which the cursor is currently placed.

Add OnLButtonUp( ) handler to the CDragDropTree class. This handler is given below:

void CDragDropTree::OnLButtonUp ( UINT nFlags, CPoint point )

{

if ( m_dragging )

{

m_dragging = FALSE ;

CImageList::DragLeave ( this ) ;

CImageList::EndDrag( ) ;

ReleaseCapture( ) ;

delete m_pdragimage ;

SelectDropTarget ( NULL ) ;

if ( m_hitemdrag == m_hitemdrop )

{

KillTimer ( 1 ) ;

return;

}

HTREEITEM hitemparent = m_hitemdrop ;

while ( ( hitemparent = GetParentItem ( hitemparent ) ) != NULL )

{

if ( hitemparent == m_hitemdrag )

{

KillTimer ( 1 ) ;

return ;

}

}

CString source, target ;

source = getpathfromitem ( m_hitemdrag ) ;

target = getpathfromitem ( m_hitemdrop ) ;

source += '\0' ;

target += '\0' ;

SHFILEOPSTRUCT sh = { 0 } ;

sh.hwnd = m_hWnd ;

sh.pFrom = source ;

sh.pTo = target ;

sh.wFunc = FO_MOVE ;

sh.fFlags = FOF_NOCONFIRMMKDIR ;

if ( SHFileOperation ( &sh ) != 0 )

{

KillTimer ( 1 ) ;

return ;

}

moveitems( m_hitemdrag, m_hitemdrop ) ;

DeleteItem ( m_hitemdrag ) ;

SelectItem ( m_hitemdrop ) ;

SortChildren ( m_hitemdrop ) ;

Expand ( m_hitemdrop, TVE_EXPAND ) ;

KillTimer ( 1 ) ;

}

CTreeCtrl::OnLButtonUp(nFlags, point);

}

When the user releases the left mouse button we have called the functions CImageList::DragLeave( ) and CImageList::EndDrag( ) to hide the image and to end the drag operation respectively. Next, we have called SelectDropTarget( ) function by passing it NULL to un-highlight the item. If the drag source and drag target are the same or if the drag source is the parent of the drag target the control returns.

Once the drag source and target are finalised we have retrieved the valid path of both the items by calling getpathfromitem( ) function. Since the pTo and pFrom elements of SHFILEOPSTRUCT structures needs double NULL terminated strings we have appended ‘\0’ to the source and target strings. FO_MOVE flag stored in the wFunc element specifies that we intend to move the folders. The SHFileOperation( ) function moves the folder from pFrom to pTo. The function returns 0 if the folder gets moved successfully. If the function fails a system message box is popped up specifying the error.

To move the items in the tree control we have called a user defined function moveitems( ) We have passed the handle of the source and target items to the moveitems( ) function. The moveitems( ) function is given below:

void CDragDropTree::moveitems ( HTREEITEM hbranch,

HTREEITEM hparent )

{

HTREEITEM hchild, hnewitem ;

CString str = GetItemText ( hbranch ) ;

hnewitem = InsertItem ( str, 1, 1, hparent, TVI_LAST ) ;

hchild = GetChildItem ( hbranch ) ;

while ( hchild != NULL )

{

moveitems ( hchild, hnewitem ) ;

hchild = GetNextSiblingItem ( hchild ) ;

}

}

Here, firstly the text of the dragged item is retrieved by calling CTreeCtrl::GetItemText( ) function. A new item is then inserted as a child of the specified item. The drag source may contain child items so to move the entire branch we have called the moveitems( ) function recursively.

If the user drags on the border of the tree control the window should scroll so that the drag target becomes visible. We have set the timer in OnBegindrag( ) function. After every 75 miliseconds it will get checked in the OnTimer( ) function whether the user is dragging on the border or not. The OnTimer( ) function is given below:

void CDragDropTree::OnTimer(UINT nIDEvent)

{

POINT pt ;

GetCursorPos ( &pt ) ;

RECT rect ;

GetClientRect ( &rect ) ;

ClientToScreen ( &rect ) ;

CImageList::DragMove ( pt ) ;

HTREEITEM hitem = GetFirstVisibleItem( ) ;

if ( pt.y <>

{

CImageList::DragShowNolock ( FALSE ) ;

SendMessage ( WM_VSCROLL, SB_LINEUP ) ;

SelectDropTarget ( hitem ) ;

m_hitemdrop = hitem ;

CImageList::DragShowNolock ( TRUE ) ;

}

else

{

if ( pt.y > rect.bottom - 10 )

{

CImageList::DragShowNolock ( FALSE ) ;

SendMessage ( WM_VSCROLL, SB_LINEDOWN ) ;

int count = GetVisibleCount( ) ;

for ( int i = 0 ; i <>

hitem = GetNextVisibleItem ( hitem ) ;

if ( hitem )

SelectDropTarget ( hitem ) ;

m_hitemdrop = hitem ;

CImageList::DragShowNolock ( TRUE ) ;

}

}

CTreeCtrl::OnTimer(nIDEvent);

}


Here, firstly we have retrieved the cursor position and moved the dragged image to the new cursor position by calling CImageList::OnDrag( ) function. We have obtained the first visible item of the tree control by calling CTreeCtrl::GetFirstVisible( ) function. If the user drags on the upper border we have scrolled up by calling SendMessage( ) function and kept the first visible item selected by calling CTreeCtrl::SelectDropTarget( ) function. If the user drags on the lower border we have scrolled down and kept selected the last visible item.

Run the program. You can expand the trees and select the desired item to be dragged and dropped as shown in the following figure.


Download