Click here to Skip to main content
14,607,858 members

Move Tree Items

Rate this:
4.92 (29 votes)
Please Sign up or sign in to vote.
4.92 (29 votes)
23 Apr 2003CPOL
Function to move or copy items and their children in a tree control

Dragging an item   After dropping the item


I recently had a situation where I needed to be able to move a part of a tree control's hierarchy to live under a different tree item. As there is no way provided by the control itself I had to write it myself. I have presented it here as I feel it something which may be of use to others.

How to use it

The function to move an item is shown below. To use it simply paste it somewhere where you wish to use it from, along with the callback function typedef, and call it with the tree control whose items you wish to move, the item to move, and the item under which the item should be moved to. Also, you may optionally specify whether the copy the item instead of moving it, a callback for copying the item data, and the position at which to create the new item relative to the new parent.


The function is fairly straightforward. A new item is created as a child of the new parent using the item information from the item being moved. This new item is a replica of the item to be moved. Then the function looks at each of the children of the item to be moved and recursively calls itself to move/copy the item's subtree.

(Note that when MoveTreeItem calls itself it does not pass in the hItemPos parameter, but always passes in TVI_LAST. This ensures that the ordering of the new subtree matches that of the existing tree.)

If the function is being used to move rather than just copy, once all children of an item have been moved, the function can delete the item moved as the stack is unravelled back to the first call of the function. As it is usual to have item data which needs deleting we must clear the item data before deleting each item so that any TVN_DELETEITEM handler does not try to delete the data now used by the copy of the item.

If the function is being used to copy (as opposed to move) items, then the items' data will be duplicated. If the data is a pointer to data which is freed on deleting the item, as is often the case, then taking a direct copy of the pointer can result in the pointer being freed more than once. To avoid this a callback function can be used to take a new copy of the data and use that in the copied item. An example of a callback function is used in the demo app, which stores pointers to CString objects allocated on the heap. The callback function simply allocates a new copy of the CString as shown here:

// copy our lParam, which happens to be a CString pointer
static LPARAM CopyData(const CTreeCtrl& tree, HTREEITEM hItem, LPARAM lParam)
  if (lParam == 0)
    return 0;
  CString* ps = (CString*)lParam;
  CString* psNew = new CString(*ps);
  return (LPARAM)psNew;

The callback function's address is passed to MoveTreeItems as follows:

MoveTreeItem(m_tree, m_hItemDrag, hItemDrop == NULL ? TVI_ROOT : hItemDrop, 
             bCopy, CopyData);

The Function

// Function:	MoveTreeItem
// Version:	4
// Created:	24-Apr-2003
// Author:	Paul S. Vickery
// E-mail:
// Description:
//    Function to move or copy a tree item and its children to another location
// Parameters:
//    CTreeCtrl& tree - the tree control which owns the items
//    HTREEITEM hItem - the item to copy/move
//    HTREEITEM hItemTo - the new parent item for the item moved/copied
//    BOOL bCopyOnly - whether to move (copy+delete) or copy (Default: FALSE)
//    PFNMTICOPYDATA pfnCopyData - optional pointer to a function to copy the 
//                         item's data when making a duplicate item
//    HTREEITEM hItemPos - positioning for new (top) item. This can be one of 
//                         TVI_FIRST, TVI_LAST (Default), TVI_SORT, or TVI_ROOT.
// Return:
//    HTREEITEM - returns the new tree item created as a copy of hItem
// You are free to use or modify this code, with no restrictions, other than
// you continue to acknowledge me as the original author in this source code,
// or any code derived from it.
// If you use this code, or use it as a base for your own code, it would be 
// nice to hear from you simply so I know it's not been a waste of time!
// Copyright (c) 2003 Paul S. Vickery
// Version History:
// Version 4 - 24-Apr-2003
// =======================
// Added callback to copy item data when copying items, so data doesn't get 
// disposed of more than once
// Version 3 - 23-Apr-2003
// =======================
// Added code to prevent items being moved to their descendants (thanks to 
// Matt Korth for reporting this one)
// Version 2 - 06-Feb-2003
// =======================
// Fixed bug where moving a tree item with more than one child loses all but
// the first child (spotted by Jack Ploeg).
// Version 1 - 31-Jan-2003
// =======================
// Initial version
HTREEITEM MoveTreeItem(CTreeCtrl& tree, HTREEITEM hItem, HTREEITEM hItemTo, 
		       BOOL bCopyOnly = FALSE, PFNMTICOPYDATA pfnCopyData = NULL, 
		       HTREEITEM hItemPos = TVI_LAST)
  if (hItem == NULL || hItemTo == NULL)
    return NULL;
  if (hItem == hItemTo || hItemTo == tree.GetParentItem(hItem))
    return hItem;
  // check we're not trying to move to a descendant
  HTREEITEM hItemParent = hItemTo;
  while (hItemParent != TVI_ROOT && 
        (hItemParent = tree.GetParentItem(hItemParent)) != NULL)
    if (hItemParent == hItem)
      return NULL;

  // copy items to new location, recursively, then delete old hierarchy
  // get text, and other info
  CString sText = tree.GetItemText(hItem);
  tvis.item.mask = TVIF_HANDLE | TVIF_IMAGE | TVIF_PARAM | 
  tvis.item.hItem = hItem;
  // we don't want to copy selection/expanded state etc
  tvis.item.stateMask = (UINT)-1 & ~(TVIS_DROPHILITED | TVIS_EXPANDED | 
  tvis.hParent = hItemTo;
  tvis.hInsertAfter = hItemPos;
  // if we're only copying, then ask for new data
  if (bCopyOnly && pfnCopyData != NULL)
    tvis.item.lParam = pfnCopyData(tree, hItem, tvis.item.lParam);
  HTREEITEM hItemNew = tree.InsertItem(&tvis);
  tree.SetItemText(hItemNew, sText);

  // now move children to under new item
  HTREEITEM hItemChild = tree.GetChildItem(hItem);
  while (hItemChild != NULL)
    HTREEITEM hItemNextChild = tree.GetNextSiblingItem(hItemChild);
    MoveTreeItem(tree, hItemChild, hItemNew, bCopyOnly, pfnCopyData);
    hItemChild = hItemNextChild;

  if (! bCopyOnly)
    // clear item data, so nothing tries to delete stuff based on lParam
    tree.SetItemData(hItem, 0);
    // no (more) children, so we can safely delete top item

  return hItemNew;

Callback Function typedef

// typedef for callback function for copying item data
// Passes in the tree control, the item being copied, and the item's data.
// The function should make and return a new copy of the data if required.


Version 4 - 24-Apr-2003

  • Added callback to copy item data when copying items, so data doesn't get disposed of more than once

Version 3 - 23 Apr 2003

  • Added code to prevent items being moved to their descendants (thanks to Matt Korth for reporting this one)

Version 2 - 06 Feb 2003

  • Fixed bug where moving a tree item with more than one child loses all but the first child (thanks to Jack Ploeg for spotting this)

Version 1 - 31 Jan 2003

  • First version


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


About the Author

Paul Vickery
Software Developer (Senior)
United Kingdom United Kingdom
This member doesn't quite have enough reputation to be able to display their biography and homepage.

Comments and Discussions

QuestionThanks for the Code, But need a help with it. Pin
Mayur S778927-Nov-13 17:31
MemberMayur S778927-Nov-13 17:31 
QuestionRequest Pin
Ankiy26-Apr-12 19:20
MemberAnkiy26-Apr-12 19:20 
Generalgood job Pin
xunonxyz20-Apr-12 17:14
Memberxunonxyz20-Apr-12 17:14 
GeneralTree items without icons? Pin
pscholl26-Apr-11 6:12
Memberpscholl26-Apr-11 6:12 
Questionwhy not to let move nodes inside one parent? Pin
sorvik28-Jun-08 3:29
Membersorvik28-Jun-08 3:29 
GeneralVery useful routine Pin
satya16524-Jul-06 20:41
Membersatya16524-Jul-06 20:41 
GeneralLook out Pin
Amit Ziv6-Nov-05 7:07
MemberAmit Ziv6-Nov-05 7:07 
GeneralGod job! Pin
bear_20-Sep-04 1:50
Memberbear_20-Sep-04 1:50 
Generalsuggestion Pin
Marquis.D.J8-Dec-03 16:32
MemberMarquis.D.J8-Dec-03 16:32 
QuestionHow can we move a childtree without... Pin
m_harriss1-Nov-03 11:42
Memberm_harriss1-Nov-03 11:42 
AnswerRe: How can we move a childtree without... Pin
Paul Vickery13-Nov-03 5:18
professionalPaul Vickery13-Nov-03 5:18 
GeneralProcessing control notifications Pin
Basim3-Jun-03 20:20
MemberBasim3-Jun-03 20:20 
QuestionKeeping item's state ? Pin
darthmaul5-May-03 0:17
Memberdarthmaul5-May-03 0:17 
Generalunable to make MoveTreeItem a class member Pin
AnkushChopra28-Apr-03 19:40
MemberAnkushChopra28-Apr-03 19:40 
GeneralRe: unable to make MoveTreeItem a class member Pin
Paul Vickery6-May-03 5:45
professionalPaul Vickery6-May-03 5:45 
GeneralRe: unable to make MoveTreeItem a class member Pin
AnkushChopra11-May-03 23:11
MemberAnkushChopra11-May-03 23:11 
GeneralMore Access Violations! Help Required Pin
AnkushChopra23-Apr-03 20:57
MemberAnkushChopra23-Apr-03 20:57 
GeneralRe: More Access Violations! Help Required Pin
Paul Vickery24-Apr-03 1:51
professionalPaul Vickery24-Apr-03 1:51 
GeneralRe: More Access Violations! Help Required Pin
AnkushChopra24-Apr-03 16:56
MemberAnkushChopra24-Apr-03 16:56 
GeneralA bit of a bug... Pin
Matt Korth6-Mar-03 9:53
MemberMatt Korth6-Mar-03 9:53 
GeneralRe: A bit of a bug... Pin
Paul Vickery23-Apr-03 6:06
professionalPaul Vickery23-Apr-03 6:06 
GeneralSmall bug Pin
Jack Ploeg6-Feb-03 3:48
MemberJack Ploeg6-Feb-03 3:48 
GeneralRe: Small bug Pin
Paul Vickery6-Feb-03 4:33
professionalPaul Vickery6-Feb-03 4:33 
GeneralPretty handy :-) Pin
Nish Nishant1-Feb-03 19:09
sitebuilderNish Nishant1-Feb-03 19:09 
GeneralERs Pin
Stephane Rodriguez.31-Jan-03 5:37
MemberStephane Rodriguez.31-Jan-03 5:37 

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.

Posted 30 Jan 2003


66 bookmarked