Click here to Skip to main content
15,885,365 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.5K   587  
A file browser written in C#, very much like Windows Explorer.
using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
using System.Collections;
using System.Drawing;
using System.Runtime.InteropServices;
using ShellDll;
using System.Drawing.Drawing2D;
using System.Globalization;
using System.ComponentModel;

namespace FileBrowser
{
    #region TreeView

    /// <summary>
    /// This is the TreeView used in the Browser control
    /// </summary>
    internal class BrowserTreeView : TreeView
    {
        private BrowserTreeSorter sorter;

        public BrowserTreeView()
        {
            HandleCreated += new EventHandler(BrowserTreeView_HandleCreated);

            sorter = new BrowserTreeSorter();
        }

        #region Override

        #endregion

        #region Events

        /// <summary>
        /// Once the handle is created we can assign the image list to the TreeView
        /// </summary>
        void BrowserTreeView_HandleCreated(object sender, EventArgs e)
        {
            ShellImageList.SetSmallImageList(this);
        }

        #endregion

        #region Public

        public bool GetTreeNode(ShellItem shellItem, out TreeNode treeNode)
        {
            ArrayList pathList = new ArrayList();
            
            while (shellItem.ParentItem != null)
            {
                pathList.Add(shellItem);
                shellItem = shellItem.ParentItem;
            }
            pathList.Add(shellItem);

            pathList.Reverse();

            treeNode = Nodes[0];
            for (int i = 1; i < pathList.Count; i++)
            {
                bool found = false;
                foreach (TreeNode node in treeNode.Nodes)
                {
                    if (node.Tag != null && node.Tag.Equals(pathList[i]))
                    {
                        treeNode = node;
                        found = true;
                        break;
                    }
                }

                if (!found)
                {
                    treeNode = null;
                    return false;
                }
            }

            return true;
        }

        /// <summary>
        /// This method will check whether a TreeNode is a parent of another TreeNode
        /// </summary>
        /// <param name="parent">The parent TreeNode</param>
        /// <param name="child">The child TreeNode</param>
        /// <returns>true if the parent is a parent of the child, false otherwise</returns>
        public bool IsParentNode(TreeNode parent, TreeNode child)
        {
            TreeNode current = child;
            while (current.Parent != null)
            {
                if (current.Parent.Equals(parent))
                    return true;

                current = current.Parent;
            }
            return false;
        }

        /// <summary>
        /// This method will check whether a TreeNode is a parent of another TreeNode
        /// </summary>
        /// <param name="parent">The parent TreeNode</param>
        /// <param name="child">The child TreeNode</param>
        /// <param name="path">If the parent is indeed a parent of the child, this will be a path of
        /// TreeNodes from the parent to the child including both parent and child</param>
        /// <returns>true if the parent is a parent of the child, false otherwise</returns>
        public bool IsParentNode(TreeNode parent, TreeNode child, out TreeNode[] path)
        {
            ArrayList pathList = new ArrayList();

            TreeNode current = child;
            while (current.Parent != null)
            {
                pathList.Add(current);
                if (current.Parent.Equals(parent))
                {
                    pathList.Add(parent);
                    pathList.Reverse();
                    path = (TreeNode[])pathList.ToArray(typeof(TreeNode));
                    return true;
                }

                current = current.Parent;
            }

            path = null;
            return false;
        }

        public void SetSorting(bool sorting)
        {
            if (sorting)
                this.TreeViewNodeSorter = sorter;
            else
                this.TreeViewNodeSorter = null;
        }

        #endregion
    }

    /// <summary>
    /// This class is used to sort the TreeNodes in the BrowserTreeView
    /// </summary>
    internal class BrowserTreeSorter : IComparer
    {
        #region IComparer Members

        /// <summary>
        /// This method will compare the ShellItems of the TreeNodes to determine the return value for
        /// comparing the TreeNodes.
        /// </summary>
        public int Compare(object x, object y)
        {
            TreeNode nodeX = x as TreeNode;
            TreeNode nodeY = y as TreeNode;

            if (nodeX.Tag != null && nodeY.Tag != null)
                return ((ShellItem)nodeX.Tag).CompareTo(nodeY.Tag);
            else if (nodeX.Tag != null)
                return 1;
            else if (nodeY.Tag != null)
                return -1;
            else
                return 0;
        }

        #endregion
    }

    #endregion

    #region ListView

    /// <summary>
    /// This is the ListView used in the Browser control
    /// </summary>
    internal class BrowserListView : ListView
    {
        #region Fields

        // The arraylist to store the order by which ListViewItems has been selected
        private ArrayList selectedOrder;

        private ContextMenu columnHeaderContextMenu;
        private bool suspendHeaderContextMenu;
        private int columnHeight = 0;

        private BrowserListSorter sorter;

        #endregion

        public BrowserListView()
        {
            OwnerDraw = true;

            HandleCreated += new EventHandler(BrowserListView_HandleCreated);
            selectedOrder = new ArrayList();
            SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
            SetStyle(ControlStyles.AllPaintingInWmPaint, true);

            DrawItem += new DrawListViewItemEventHandler(BrowserListView_DrawItem);
            DrawSubItem += new DrawListViewSubItemEventHandler(BrowserListView_DrawSubItem);
            DrawColumnHeader += new DrawListViewColumnHeaderEventHandler(BrowserListView_DrawColumnHeader);

            this.Alignment = ListViewAlignment.Left;
            sorter = new BrowserListSorter();
        }

        #region Owner Draw

        void BrowserListView_DrawItem(object sender, DrawListViewItemEventArgs e)
        {
            e.DrawDefault = true;
        }

        void BrowserListView_DrawSubItem(object sender, DrawListViewSubItemEventArgs e)
        {
            e.DrawDefault = true;
        }

        void BrowserListView_DrawColumnHeader(object sender, DrawListViewColumnHeaderEventArgs e)
        {
            e.DrawDefault = true;
            columnHeight = e.Bounds.Height;
        }

        #endregion

        #region Override

        public new View View
        {
            get
            {
                return base.View;
            }
            set
            {
                base.View = value;

                if (value == View.Details)
                {
                    foreach (ColumnHeader col in Columns)
                        if (col.Width == 0)
                            col.Width = 120;
                }
            }
        }

        protected override void OnItemSelectionChanged(ListViewItemSelectionChangedEventArgs e)
        {
            if (e.IsSelected)
                selectedOrder.Insert(0, e.Item);
            else
                selectedOrder.Remove(e.Item);

            base.OnItemSelectionChanged(e);
        }

        protected override void WndProc(ref Message m)
        {
            if (this.View == View.Details && columnHeaderContextMenu != null &&
                (int)m.Msg == (int)ShellAPI.WM.CONTEXTMENU)
            {
                if (suspendHeaderContextMenu)
                    suspendHeaderContextMenu = false;
                else
                {
                    int x = (int)ShellHelper.LoWord(m.LParam);
                    int y = (int)ShellHelper.HiWord(m.LParam);
                    Point clientPoint = PointToClient(new Point(x, y));
                    
                    if (clientPoint.Y <= columnHeight)
                        columnHeaderContextMenu.Show(this, clientPoint);
                }

                return;
            }

            base.WndProc(ref m);
        }

        #endregion

        #region Events

        /// <summary>
        /// Once the handle is created we can assign the image lists to the ListView
        /// </summary>
        void BrowserListView_HandleCreated(object sender, EventArgs e)
        {
            ShellImageList.SetSmallImageList(this);
            ShellImageList.SetLargeImageList(this);
        }

        #endregion

        #region Public

        [Browsable(false)]
        public ArrayList SelectedOrder
        {
            get { return selectedOrder; }
        }

        [Browsable(false)]
        public bool SuspendHeaderContextMenu
        {
            get { return suspendHeaderContextMenu; }
            set { suspendHeaderContextMenu = value; }
        }

        [Browsable(true)]
        public ContextMenu ColumnHeaderContextMenu
        {
            get { return columnHeaderContextMenu; }
            set { columnHeaderContextMenu = value; }
        }

        public void SetSorting(bool sorting)
        {
            if (sorting)
                this.ListViewItemSorter = sorter;
            else
                this.ListViewItemSorter = null;
        }

        public void ClearSelections()
        {
            selectedOrder.Clear();
            selectedOrder.Capacity = 0;
        }

        public bool GetListItem(ShellItem shellItem, out ListViewItem listItem)
        {
            listItem = null;

            foreach (ListViewItem item in Items)
            {
                if (shellItem.Equals(item.Tag))
                {
                    listItem = item;
                    return true;
                }
            }

            return false;
        }

        #endregion
    }

    /// <summary>
    /// This class is used to sort the ListViewItems in the BrowserListView
    /// </summary>
    internal class BrowserListSorter : IComparer
    {
        #region IComparer Members

        /// <summary>
        /// This method will compare the ShellItems of the ListViewItems to determine the return value for
        /// comparing the ListViewItems.
        /// </summary>
        public int Compare(object x, object y)
        {
            ListViewItem itemX = x as ListViewItem;
            ListViewItem itemY = y as ListViewItem;

            if (itemX.Tag != null && itemY.Tag != null)
                return ((ShellItem)itemX.Tag).CompareTo(itemY.Tag);
            else if (itemX.Tag != null)
                return 1;
            else if (itemY.Tag != null)
                return -1;
            else
                return 0;
        }

        #endregion
    }

    #endregion

    #region ComboBox

    /// <summary>
    /// This is the ComboBox used in the navigationbar. It is extended to show the icons that go with the
    /// ShellItems.
    /// </summary>
    internal class BrowserComboBox : ToolStripComboBox
    {
        #region Fields

        // The width of the indent of the ComboItems
        private int indentWidth = 10;

        // The item for the browser's current selected directory
        private BrowserComboItem currentItem;

        // The class to draw the icon in the textbox area of the ComboBox
        private ComboEditWindow editWindow;

        #endregion

        public BrowserComboBox()
        {
            ComboBox.DrawMode = DrawMode.OwnerDrawFixed;
            ComboBox.DrawItem += new DrawItemEventHandler(ComboBox_DrawItem);
            ComboBox.HandleCreated += new EventHandler(ComboBox_HandleCreated);

            ComboBox.MouseDown += new MouseEventHandler(ComboBox_MouseDown);
            ComboBox.MouseMove += new MouseEventHandler(ComboBox_MouseMove);
            ComboBox.MouseClick += new MouseEventHandler(ComboBox_MouseClick);

            ComboBox.DropDown += new EventHandler(ComboBox_DropDown);
        }

        #region Events

        /// <summary>
        /// This method will change the currentItem field once a new item is selected
        /// </summary>
        protected override void OnSelectedIndexChanged(EventArgs e)
        {
            base.OnSelectedIndexChanged(e);

            if (ComboBox.SelectedIndex > -1)
                currentItem = ComboBox.SelectedItem as BrowserComboItem;         
        }

        /// <summary>
        /// Once the handle has been created a new instance of ComboEditWindow is made to make sure
        /// the icons are drawn in the TextBox part of the ComboBox
        /// </summary>
        void ComboBox_HandleCreated(object sender, EventArgs e)
        {
            editWindow = new ComboEditWindow(this);
        }

        /// <summary>
        /// This method will draw the items of the DropDownList. It will draw the Icon and the text and
        /// with the indent that goes with the item
        /// </summary>
        void ComboBox_DrawItem(object sender, DrawItemEventArgs e)
        {
            if (e.Index == -1)
                return;
            else
            {
                BrowserComboItem item = (BrowserComboItem)Items[e.Index];

                e.DrawBackground();
                e.DrawFocusRectangle();

                int indentOffset = indentWidth * item.Indent;

                int imageYOffset = (e.Bounds.Height - item.Image.Height) / 2;
                Point imagePoint = new Point(
                    e.Bounds.Left + indentOffset + 2, 
                    e.Bounds.Top + imageYOffset);

                Size textSize = e.Graphics.MeasureString(item.Text, Font).ToSize();
                int textYOffset = (e.Bounds.Height - textSize.Height) / 2;
                Point textPoint = new Point(
                    e.Bounds.Left + item.Image.Width + indentOffset + 2, 
                    e.Bounds.Top + textYOffset);

                e.Graphics.DrawIcon(item.Image, imagePoint.X, imagePoint.Y);
                e.Graphics.DrawString(item.Text, e.Font, new SolidBrush(e.ForeColor), textPoint);
            }
        }

        /// <summary>
        /// If the mouse is moved over the icon of the ComboBox this method will change the cursor
        /// into an hand to indicate that the icon can be clicked
        /// </summary>
        void ComboBox_MouseMove(object sender, MouseEventArgs e)
        {
            if (editWindow.ImageRect.Contains(e.Location))
            {
                Cursor.Current = Cursors.Hand;
            }
            else if (Cursor.Current == Cursors.Hand)
            {
                Cursor.Current = Cursors.Default;
            }
        }

        /// <summary>
        /// If the mouse is moved over the icon of the ComboBox this method will change the cursor
        /// into an hand to indicate that the icon can be clicked
        /// </summary>
        void ComboBox_MouseDown(object sender, MouseEventArgs e)
        {
            if (editWindow.ImageRect.Contains(e.Location))
                Cursor.Current = Cursors.Hand;
        }  

        /// <summary>
        /// If the icon of the ComboBox is clicked this method will select all text in the ComboBox
        /// </summary>
        void ComboBox_MouseClick(object sender, MouseEventArgs e)
        {
            if (editWindow.ImageRect.Contains(e.Location))
                SelectAll();
        }

        /// <summary>
        /// This method will make sure that when the ComboBox is dropped down, the width of the DropDownList
        /// will be sufficient to fit all items
        /// </summary>
        void ComboBox_DropDown(object sender, EventArgs e)
        {
            int width = 0;
            Graphics gfx = ComboBox.CreateGraphics();
            foreach (BrowserComboItem item in Items)
            {
                int itemWidth =
                    gfx.MeasureString(item.Text, Font).ToSize().Width +
                    item.Image.Width +
                    indentWidth * item.Indent +
                    (Items.Count > MaxDropDownItems ? SystemInformation.VerticalScrollBarWidth : 0);

                if (itemWidth > width)
                    width = itemWidth;
            }

            if (width > this.Width)
                ComboBox.DropDownWidth = width;
            else
                ComboBox.DropDownWidth = this.Width;
        } 

        #endregion

        #region Public

        [Browsable(false)]
        public BrowserComboItem CurrentItem 
        { 
            get { return currentItem; }
            set { currentItem = value; }
        }

        #endregion

        #region ComboEditWindow

        class ComboEditWindow : NativeWindow
        {
            private const int LEFTMARGIN = 0x1;
            private BrowserComboBox owner;
            private Rectangle imageRect;
            private int margin = 0;

            /// <summary>
            /// The native window's original handle is released 
            /// and the handle of the TextBox is assigned to it.
            /// </summary>
            public ComboEditWindow(BrowserComboBox owner)
            {
                this.owner = owner;

                ShellAPI.COMBOBOXINFO info = new ShellAPI.COMBOBOXINFO();
                info.cbSize = Marshal.SizeOf(typeof(ShellAPI.COMBOBOXINFO));
                ShellAPI.GetComboBoxInfo(owner.ComboBox.Handle, ref info);

                if (!this.Handle.Equals(IntPtr.Zero))
                {
                    this.ReleaseHandle();
                }
                this.AssignHandle(info.hwndEdit);
            }

            public Rectangle ImageRect { get { return imageRect; } }

            /// <summary>
            /// Set the margin of the TextBox to make room for the icon
            /// </summary>
            /// <param name="margin">The margin to set</param>
            private void SetMargin(int margin)
            {
                if (this.margin != margin)
                {
                    this.margin = margin;

                    if (owner == null)
                        return;

                    ShellAPI.SendMessage(
                        this.Handle, ShellAPI.WM.SETMARGINS, LEFTMARGIN,
                        new IntPtr(margin));
                }
            }

            /// <summary>
            /// Whenever the textbox is repainted, this method will draw the icon.
            /// </summary>
            private void DrawImage()
            {
                if (owner.CurrentItem != null)
                {
                    SetMargin(owner.CurrentItem.Image.Width + 3);

                    Icon icon = owner.CurrentItem.Image;
                    Bitmap image = new Bitmap(icon.Width, icon.Height);
                    
                    Graphics imageGfx = Graphics.FromImage(image);
                    imageRect = new Rectangle(new Point(0, 0), image.Size);
                    imageGfx.FillRectangle(Brushes.White, imageRect);
                    imageGfx.DrawIcon(icon, 0, 0);
                    imageGfx.Flush();

                    // Gets a GDI drawing surface from the textbox.
                    Graphics gfx = Graphics.FromHwnd(this.Handle);

                    if (owner.RightToLeft == RightToLeft.Yes)
                    {
                        gfx.DrawImage(image, (int)gfx.VisibleClipBounds.Width - icon.Width, 0);
                    }
                    else if (owner.RightToLeft == RightToLeft.No)
                    {
                        gfx.DrawImage(image, 0, 0);
                    }

                    gfx.Flush();
                    gfx.Dispose();
                }
            }

            // Override the WndProc method so that we can redraw the TextBox when the textbox is repainted.
            protected override void WndProc(ref Message m)
            {
                switch (m.Msg)
                {
                    case (int)ShellAPI.WM.PAINT:
                        base.WndProc(ref m);
                        DrawImage();
                        break;
                    case (int)ShellAPI.WM.LBUTTONDOWN:
                        base.WndProc(ref m);
                        DrawImage();
                        break;
                    case (int)ShellAPI.WM.KEYDOWN:
                        base.WndProc(ref m);
                        DrawImage();
                        break;
                    case (int)ShellAPI.WM.KEYUP:
                        base.WndProc(ref m);
                        DrawImage();
                        break;
                    case (int)ShellAPI.WM.CHAR:
                        base.WndProc(ref m);
                        DrawImage();
                        break;
                    case (int)ShellAPI.WM.GETTEXTLENGTH:
                        base.WndProc(ref m);
                        DrawImage();
                        break;
                    case (int)ShellAPI.WM.GETTEXT:
                        base.WndProc(ref m);
                        DrawImage();
                        break;
                    default:
                        base.WndProc(ref m);
                        break;
                }
            }
        }

        #endregion
    }
    
    /// <summary>
    /// This class represents the items that can be used for the BrowserComboBox
    /// </summary>
    internal class BrowserComboItem
    {
        // The ShellItem that goes with the ComboItem
        private ShellItem shellItem;

        // The indent that goes with the ComboItem
        private int indent;

        // The Icon that has to be drawn for this ComboItem
        private Icon image;

        public BrowserComboItem(ShellItem shellItem, int indent)
        {
            this.shellItem = shellItem;
            this.indent = indent;
            this.image = ShellImageList.GetIcon(shellItem.ImageIndex, true);
        }

        #region Properties

        public ShellItem ShellItem { get { return shellItem; } }
        public int Indent { get { return indent; } }
        public Icon Image { get { return image; } }
        public string Text { get { return ShellItem.Text; } }

        #endregion

        public override string ToString()
        {
            return ShellItem.Path;
        }
    }

    #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