Click here to Skip to main content
15,893,663 members
Articles / Programming Languages / C#

C# File Browser

Rate me:
Please Sign up or sign in to vote.
4.93/5 (173 votes)
21 Aug 200628 min read 2.4M   81.6K   587  
A file browser written in C#, very much like Windows Explorer.
using System;
using System.Collections.Generic;
using System.Text;
using ShellDll;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using System.Drawing;

namespace FileBrowser
{
    /// <summary>
    /// This class takes care of every drop operation in a BrowserTreeView
    /// </summary>
    internal class BrowserTVDropWrapper : ShellDll.IDropTarget, IDisposable
    {
        #region Fields

        // The browser for which to do the drop work
        private Browser br;

        private IntPtr treeViewHandle;

        // The current IDropTarget the cursor is over and the pointers to the target and dataobject
        private ShellDll.IDropTarget dropTarget;
        private IntPtr dropTargetPtr;
        private IntPtr dropDataObject;

        private IDropTargetHelper dropHelper;
        private IntPtr dropHelperPtr;

        // The current node the cursor is over to drop on
        private TreeNode dropNode;

        // The last selected node from the browser
        // This is to remember which node to select after drop has completed
        private TreeNode lastSelectedNode;

        // The parent ShellItems of the drop- and dragitem
        private ShellItem parentDropItem, parentDragItem;

        // The mouse and keys state when a drag enter occurs
        private ShellAPI.MK mouseButtons;

        // A bool to indicate whether this class has been disposed
        private bool disposed = false;

        // The event for when a drop is occuring
        public event DropEventHandler Drop;

        #endregion

        /// <summary>
        /// Registers the TreeView for drag/drop operations and uses this class as the IDropTarget
        /// </summary>
        /// <param name="br">The browser for which to support the drop</param>
        public BrowserTVDropWrapper(Browser br)
        {
            this.br = br;

            treeViewHandle = br.FolderView.Handle;
            ShellAPI.RegisterDragDrop(treeViewHandle, this);

            br.FolderView.HandleCreated += new EventHandler(FolderView_HandleCreated);
            br.FolderView.HandleDestroyed += new EventHandler(FolderView_HandleDestroyed);

            ShellHelper.GetIDropTargetHelper(out dropHelperPtr, out dropHelper);
        }

        ~BrowserTVDropWrapper()
        {
            ((IDisposable)this).Dispose();
        }

        #region Handle Changes

        void FolderView_HandleCreated(object sender, EventArgs e)
        {
            treeViewHandle = br.FolderView.Handle;
            ShellAPI.RegisterDragDrop(treeViewHandle, this);
        }

        void FolderView_HandleDestroyed(object sender, EventArgs e)
        {
            ShellAPI.RevokeDragDrop(br.FolderView.Handle);
        }

        #endregion

        #region Public

        /// <summary>
        /// This ShellItem is the parent item of the item being currently dragged. This field is used
        /// to check whether an item is being moved to it's original folder. If this is the case, we don't
        /// have to do anything, cause the item is allready there.
        /// </summary>
        public ShellItem ParentDragItem
        {
            get { return parentDragItem; }
            set { parentDragItem = value; }
        }

        #endregion

        #region Generated Events

        /// <summary>
        /// This event will be raised whenever a drop occurs on the TreeView.
        /// </summary>
        /// <param name="e">The DropEventArgs for the event</param>
        private void OnDrop(DropEventArgs e)
        {
            if (Drop != null)
                Drop(this, e);
        }

        #endregion

        #region IDropTarget Members

        public int DragEnter(IntPtr pDataObj, ShellAPI.MK grfKeyState, ShellAPI.POINT pt, ref DragDropEffects pdwEffect)
        {
            mouseButtons = grfKeyState;

            br.FolderView.Focus();
            br.SelectionChange = false;
            lastSelectedNode = br.FolderView.SelectedNode;

            ReleaseCom();

            dropDataObject = pDataObj;

            #region Get DropItem

            Point point = br.FolderView.PointToClient(new Point(pt.x, pt.y));
            TreeViewHitTestInfo hitTest = br.FolderView.HitTest(point);

            dropNode = hitTest.Node;
            br.FolderView.SelectedNode = dropNode;

            if (dropNode != null)
            {
                ShellItem item = (ShellItem)dropNode.Tag;
                parentDropItem = item;

                if (ShellHelper.GetIDropTarget(item, out dropTargetPtr, out dropTarget))
                {
                    dropTarget.DragEnter(pDataObj, grfKeyState, pt, ref pdwEffect);
                }
            }

            #endregion

            if (dropHelper != null)
                dropHelper.DragEnter(br.Handle, pDataObj, ref pt, pdwEffect);

            return ShellAPI.S_OK;
        }

        public int DragOver(ShellAPI.MK grfKeyState, ShellAPI.POINT pt, ref DragDropEffects pdwEffect)
        {
            bool reset = false;

            #region Get DropItem

            Point point = br.FolderView.PointToClient(new Point(pt.x, pt.y));
            TreeViewHitTestInfo hitTest = br.FolderView.HitTest(point);
            if (!TreeNode.Equals(dropNode, hitTest.Node))
            {
                if (dropTarget != null)
                    dropTarget.DragLeave();

                ReleaseCom();

                dropNode = hitTest.Node;
                br.FolderView.SelectedNode = dropNode;
                
                if (dropNode == null)
                {
                    pdwEffect = DragDropEffects.None;

                    if (dropHelper != null)
                        dropHelper.DragOver(ref pt, pdwEffect);

                    return ShellAPI.S_OK;
                }
                else
                {
                    ShellItem item = (ShellItem)dropNode.Tag;
                    parentDropItem = item;

                    ShellHelper.GetIDropTarget(item, out dropTargetPtr, out dropTarget);
                    reset = true;
                }
            }
            else if (dropNode == null)
            {
                if (dropTarget != null)
                    dropTarget.DragLeave();

                ReleaseCom();

                dropNode = null;
                br.SelectedNode = null;

                pdwEffect = DragDropEffects.None;

                if (dropHelper != null)
                    dropHelper.DragOver(ref pt, pdwEffect);

                return ShellAPI.S_OK;
            }

            #endregion

            if (dropTarget != null)
            {
                if (reset)
                    dropTarget.DragEnter(dropDataObject, grfKeyState, pt, ref pdwEffect);
                else
                    dropTarget.DragOver(grfKeyState, pt, ref pdwEffect);
            }
            else
                pdwEffect = DragDropEffects.None;

            if (dropHelper != null)
                dropHelper.DragOver(ref pt, pdwEffect);

            return ShellAPI.S_OK;
        }

        public int DragLeave()
        {
            ResetDrop();
            if (dropTarget != null)
            {
                dropTarget.DragLeave();

                ReleaseCom();
                dropDataObject = IntPtr.Zero;
            }

            if (dropHelper != null)
                dropHelper.DragLeave();

            return ShellAPI.S_OK;
        }

        public int DragDrop(IntPtr pDataObj, ShellAPI.MK grfKeyState, ShellAPI.POINT pt, ref DragDropEffects pdwEffect)
        {
            OnDrop(new DropEventArgs(mouseButtons, br.FolderView));

            if (!((mouseButtons & ShellAPI.MK.RBUTTON) != 0 ||
                  grfKeyState == ShellAPI.MK.CONTROL || 
                  grfKeyState == ShellAPI.MK.ALT || 
                  grfKeyState == (ShellAPI.MK.CONTROL | ShellAPI.MK.SHIFT)) && ShellItem.Equals(parentDragItem, parentDropItem))
            {
                ResetDrop();                
                ReleaseCom();
                pdwEffect = DragDropEffects.None;

                if (dropHelper != null)
                    dropHelper.DragLeave();

                return ShellAPI.S_OK;
            }

            ResetDrop();
            if (dropTarget != null)
            {
                dropTarget.DragDrop(pDataObj, grfKeyState, pt, ref pdwEffect);

                ReleaseCom();
                dropDataObject = IntPtr.Zero;
            }

            if (dropHelper != null)
                dropHelper.Drop(pDataObj, ref pt, pdwEffect);

            return ShellAPI.S_OK;
        }

        /// <summary>
        /// Reset all fields to the default values and release the IDropTarget
        /// </summary>
        private void ResetDrop()
        {
            if (dropNode != null)
            {
                dropNode = null;
                parentDropItem = null;
            }

            if (lastSelectedNode != null)
                br.FolderView.SelectedNode = lastSelectedNode;

            br.SelectionChange = true;
        }

        #endregion

        #region IDisposable Members

        /// <summary>
        /// If not disposed, dispose the class
        /// </summary>
        public void Dispose()
        {
            if (!disposed)
            {
                DisposeDropWrapper();
                GC.SuppressFinalize(this);

                disposed = true;
            }
        }

        /// <summary>
        /// Revokes the TreeView from getting drop messages and releases the IDropTarget
        /// </summary>
        private void DisposeDropWrapper()
        {
            ReleaseCom();

            if (dropHelper != null)
            {
                Marshal.ReleaseComObject(dropHelper);
            }
        }

        /// <summary>
        /// Release the IDropTarget and free's the allocated memory
        /// </summary>
        private void ReleaseCom()
        {
            if (dropTarget != null)
            {
                Marshal.ReleaseComObject(dropTarget);

                dropTarget = null;
                dropHelperPtr = IntPtr.Zero;
            }
        }

        #endregion
    }

    /// <summary>
    /// This class takes care of every drop operation in a BrowserListView
    /// </summary>
    internal class BrowserLVDropWrapper : ShellDll.IDropTarget, IDisposable
    {
        #region Fields

        // The browser for which to do the drop work
        private Browser br;
        
        private IntPtr listViewHandle;

        // The current IDropTarget the cursor is over and the pointers to the target and dataobject
        private ShellDll.IDropTarget dropTarget;
        private IntPtr dropTargetPtr;
        private IntPtr dropDataObject;

        private IDropTargetHelper dropHelper;
        private IntPtr dropHelperPtr;

        // The current ListViewItem the cursor is over to drop on
        private ListViewItem dropListItem;

        // The selected state from the dropListItem before the cursor moved over it
        private bool wasSelected;

        // The parent ShellItems of the drop- and dragitem
        private ShellItem parentDropItem, parentDragItem;

        // The mouse and keys state and DragDropEffects when a drag enter occurs
        private ShellAPI.MK mouseButtons;
        private DragDropEffects startEffects;

        // A bool to indicate whether this class has been disposed
        private bool disposed = false;

        // The event for when a drop is occuring
        public event DropEventHandler Drop;

        #endregion

        /// <summary>
        /// Registers the ListView for drag/drop operations and uses this class as the IDropTarget
        /// </summary>
        /// <param name="br">The browser for which to support the drop</param>
        public BrowserLVDropWrapper(Browser br)
        {
            this.br = br;

            listViewHandle = br.FileView.Handle;
            ShellAPI.RegisterDragDrop(listViewHandle, this);

            br.FileView.HandleCreated += new EventHandler(FileView_HandleCreated);
            br.FileView.HandleDestroyed += new EventHandler(FileView_HandleDestroyed);

            ShellHelper.GetIDropTargetHelper(out dropHelperPtr, out dropHelper);
        }

        ~BrowserLVDropWrapper()
        {
            ((IDisposable)this).Dispose();
        }

        #region Handle Changes

        void FileView_HandleCreated(object sender, EventArgs e)
        {
            listViewHandle = br.FileView.Handle;
            ShellAPI.RegisterDragDrop(listViewHandle, this);
        }

        void FileView_HandleDestroyed(object sender, EventArgs e)
        {
            ShellAPI.RevokeDragDrop(listViewHandle);
        }

        #endregion

        #region Public

        /// <summary>
        /// This ShellItem is the parent item of the item being currently dragged. This field is used
        /// to check whether an item is being moved to it's original folder. If this is the case, we don't
        /// have to do anything, cause the item is allready there.
        /// </summary>
        public ShellItem ParentDragItem
        {
            get { return parentDragItem; }
            set { parentDragItem = value; }
        }

        #endregion

        #region Generated Events

        /// <summary>
        /// This event will be raised whenever a drop occurs on the ListView.
        /// </summary>
        /// <param name="e">The DropEventArgs for the event</param>
        private void OnDrop(DropEventArgs e)
        {
            if (Drop != null)
                Drop(this, e);
        }

        #endregion

        #region IDropTarget Members

        public int DragEnter(IntPtr pDataObj, ShellAPI.MK grfKeyState, ShellAPI.POINT pt, ref DragDropEffects pdwEffect)
        {
            mouseButtons = grfKeyState;
            startEffects = pdwEffect;

            br.FileView.Focus();
            br.SelectionChange = false;
            ReleaseCom();

            dropDataObject = pDataObj;

            #region Get DropItem
            Point point = br.FileView.PointToClient(new Point(pt.x, pt.y));
            ListViewHitTestInfo hitTest = br.FileView.HitTest(point);
            if (hitTest.Item != null && 
                (br.FileView.View != View.Details || hitTest.SubItem == null || hitTest.Item.Name == hitTest.SubItem.Name) &&
                (hitTest.Location == ListViewHitTestLocations.Image ||
                 hitTest.Location == ListViewHitTestLocations.Label ||
                 hitTest.Location == ListViewHitTestLocations.StateImage))
            {
                dropListItem = hitTest.Item;

                wasSelected = dropListItem.Selected;
                dropListItem.Selected = true;

                ShellItem item = (ShellItem)dropListItem.Tag;
                parentDropItem = item;

                ShellHelper.GetIDropTarget(item, out dropTargetPtr, out dropTarget);
            }
            else
            {
                dropListItem = null;
                parentDropItem = br.SelectedItem;
                ShellHelper.GetIDropTarget(br.SelectedItem, out dropTargetPtr, out dropTarget);
            }
            #endregion

            if (dropTarget != null)
                dropTarget.DragEnter(pDataObj, grfKeyState, pt, ref pdwEffect);

            if (dropHelper != null)
                dropHelper.DragEnter(br.Handle, pDataObj, ref pt, pdwEffect);

            return ShellAPI.S_OK;
        }

        public int DragOver(ShellAPI.MK grfKeyState, ShellAPI.POINT pt, ref DragDropEffects pdwEffect)
        {
            bool reset = false;

            #region Get DropItem

            Point point = br.FileView.PointToClient(new Point(pt.x, pt.y));
            ListViewHitTestInfo hitTest = br.FileView.HitTest(point);
            if (hitTest.Item != null &&
                (br.FileView.View != View.Details || hitTest.SubItem == null || hitTest.Item.Name == hitTest.SubItem.Name) &&
                (hitTest.Location == ListViewHitTestLocations.Image ||
                 hitTest.Location == ListViewHitTestLocations.Label ||
                 hitTest.Location == ListViewHitTestLocations.StateImage))
            {                
                if (!hitTest.Item.Equals(dropListItem))
                {
                    if (dropTarget != null)
                        dropTarget.DragLeave();

                    ReleaseCom();

                    if (dropListItem != null)
                        dropListItem.Selected = wasSelected;

                    dropListItem = hitTest.Item;
                    wasSelected = dropListItem.Selected;
                    dropListItem.Selected = true;

                    ShellItem item = (ShellItem)dropListItem.Tag;
                    parentDropItem = item;

                    ShellHelper.GetIDropTarget(item, out dropTargetPtr, out dropTarget);
                    reset = true;
                }
            }
            else
            {
                if (dropListItem != null)
                {
                    if (dropTarget != null)
                        dropTarget.DragLeave();

                    ReleaseCom();

                    dropListItem.Selected = wasSelected;

                    dropListItem = null;
                    parentDropItem = br.SelectedItem;

                    ShellHelper.GetIDropTarget(br.SelectedItem, out dropTargetPtr, out dropTarget);
                    reset = true;
                }
            }

            #endregion

            if (dropTarget != null)
            {
                if (reset)
                    dropTarget.DragEnter(dropDataObject, grfKeyState, pt, ref pdwEffect);
                else
                    dropTarget.DragOver(grfKeyState, pt, ref pdwEffect);
            }
            else
                pdwEffect = DragDropEffects.None;

            if (dropHelper != null)
                dropHelper.DragOver(ref pt, pdwEffect);

            return ShellAPI.S_OK;
        }

        public int DragLeave()
        {
            ResetDrop();
            if (dropTarget != null)
            {
                dropTarget.DragLeave();

                ReleaseCom();
                dropDataObject = IntPtr.Zero;
            }

            if (dropHelper != null)
                dropHelper.DragLeave();

            return ShellAPI.S_OK;
        }

        public int DragDrop(IntPtr pDataObj, ShellAPI.MK grfKeyState, ShellAPI.POINT pt, ref DragDropEffects pdwEffect)
        {
            OnDrop(new DropEventArgs(mouseButtons, br.FileView));

            if (!((mouseButtons & ShellAPI.MK.RBUTTON) != 0 ||
                  grfKeyState == ShellAPI.MK.CONTROL ||
                  grfKeyState == ShellAPI.MK.ALT ||
                  grfKeyState == (ShellAPI.MK.CONTROL | ShellAPI.MK.SHIFT)) && ShellItem.Equals(parentDragItem, parentDropItem))
            {
                ResetDrop();
                ReleaseCom();
                pdwEffect = DragDropEffects.None;

                if (dropHelper != null)
                    dropHelper.Drop(pDataObj, ref pt, pdwEffect);

                return ShellAPI.S_OK;
            }

            ResetDrop();
            if (dropTarget != null)
            {
                dropTarget.DragDrop(pDataObj, grfKeyState, pt, ref pdwEffect);

                ReleaseCom();
                dropDataObject = IntPtr.Zero;
            }

            if (dropHelper != null)
                dropHelper.Drop(pDataObj, ref pt, pdwEffect);

            return ShellAPI.S_OK;
        }

        /// <summary>
        /// Reset all fields to the default values and release the IDropTarget
        /// </summary>
        private void ResetDrop()
        {
            if (dropListItem != null)
            {
                dropListItem.Selected = wasSelected;
                dropListItem = null;
                parentDropItem = null;
            }

            br.SelectionChange = true;
        }

        #endregion

        #region IDisposable Members

        /// <summary>
        /// If not disposed, dispose the class
        /// </summary>
        public void Dispose()
        {
            if (!disposed)
            {
                DisposeDropWrapper();
                GC.SuppressFinalize(this);

                disposed = true;
            }
        }

        /// <summary>
        /// Revokes the ListView from getting drop messages and releases the IDropTarget
        /// </summary>
        private void DisposeDropWrapper()
        {
            ReleaseCom();

            if (dropHelper != null)
            {
                Marshal.ReleaseComObject(dropHelper);                
            }
        }

        /// <summary>
        /// Release the IDropTarget and free's the allocated memory
        /// </summary>
        private void ReleaseCom()
        {
            if (dropTarget != null)
            {
                Marshal.ReleaseComObject(dropTarget);

                dropTarget = null;
                dropHelperPtr = IntPtr.Zero;
            }
        }

        #endregion
    }

    #region Event Classes

    internal delegate void DropEventHandler(object sender, DropEventArgs e);

    internal class DropEventArgs : EventArgs
    {
        private ShellAPI.MK mouseButtons;
        private Control dragStartControl;

        public DropEventArgs(ShellAPI.MK mouseButtons, Control dragStartControl)
        {
            this.mouseButtons = mouseButtons;
            this.dragStartControl = dragStartControl;
        }

        public ShellAPI.MK MouseButtons { get { return mouseButtons; } }
        public Control DragStartControl { get { return dragStartControl; } }
    }

    #endregion
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Netherlands Netherlands
I'm a student in Amsterdam (The Netherlands). I study Artificial Intelligence at the University of Amsterdam and I'm very fond of programming.

I discovered .Net programming a few years ago and immediately liked the Visual Studio environment. Since then I experimented a lot with .Net.

Comments and Discussions