A Treeview-Based File Browser / Open Dialog
for SmartPhone [Compact Framework]

by Peter A. Bromberg, Ph.D.

Peter Bromberg

While working on my first Smartphone venture which uploads Smartphone camera photos you've taken with your phone up to your web site, I experimented with various controls and code, and even resurrected Andy Wigley's book, ".NET Compact Framework Core Reference" (MS Press), which contains lots of valuable Compact Framework information (even though it doesn't cover the SmartPhone form factor).

Along the way, I came upon an excellent article by Wei-Meng Lee which presents a TreeView - based explorer, written in VB.NET. This article is based in large part on Wei-Meng's article, however there are several important differences:

1) My code is all written in C#, my preferred .NET target language of choice.
2) I provide the component as a self-contained class library, making it easier to just add it to any existing project.
3) My code offers an eventing mechanism that returns the path of a selected file from the Explorer when it is     closed, and the caller can then use this just as one would the return value from an OpenFileDialog control.



The same eventing mechanism I use to return the path of the file that was last selected in the Treeview can be extended to do other things, for example to run a file, to open a URL, whatever you need to do. I could have also chosen to simple add another softkey context menu choice that would perform the action; I just found it a bit more intuitive that you decide to press the EXIT softkey to perform the action, indicating that you are really "done" with your little exploration, and this would return you to the calling form. What I do in this demo is select a photo that is in the same folder as the app, and when this is selected and the exit key pressed, the PictureBox on the main form utilises the path information returned by the event to populate the picturebox and show the photo as a "proof of concept".

The Smartphone, especially if you have one of the newest models that have the Compact Framework embedded in ROM, is not only an extremely useful little booger, but it's really a lot of fun and there is a growing list of developer tools, many of them free or open - source, targeted to it. There's also a growing list of software that does a lot of interesting things. Some of it is what one could consider "faddish" and most likely would not be used very much on a SmartPhone. There is a blog from the Compact Framework team at Microsoft, numerous articles on Compact Framework at various developer-oriented web sites, and there is also a series of newsgroups at msnews.microsoft.com that are targeted specifically to the Compact Framework and the Smartphone.

For my Treeview Explorer / FileDialog surrogate control, I will present the key aspects of the code, and then a discussion will follow:

First, below appears all of the code for the Treeview Explorer/File Dialog:

#region Using directives

using System;
using System.Drawing;
using System.Collections;
using System.Windows.Forms;
using System.Data;
using System.IO;

#endregion

namespace Explorer
{
 /// <summary>
 /// Summary description for Tree.
 /// </summary>
 public class Tree : System.Windows.Forms.Form
 {
        const int icoOpen = 0;
        const int icoClose = 1;
        const int icoFile = 2;
        bool copy  = true; // whether it is a copy operation
        string fileName;   // filename of file to be copied or cut
        string pathName;  // full pathname of file to be copied or cut

  private TreeView treeView1;
  private MenuItem menuItem1;
  private MenuItem menuItem2;
  private MenuItem menuItem3;
  private MenuItem menuItem4;
  private MenuItem menuItem5;
  private MenuItem mnuPaste;
  private ImageList imageList2;

        public event EventHandler NotifyParent;

        protected void OnNotifyParent()
        {
            if (NotifyParent != null) NotifyParent(this.pathName , EventArgs.Empty);
        }

  /// <summary>
  /// Main menu for the form.
  /// </summary>
  private System.Windows.Forms.MainMenu mainMenu1;

  public Tree()
  {
   InitializeComponent();
  }

  /// <summary>
  /// Clean up any resources being used.
  /// </summary>
  protected override void Dispose(bool disposing)
  {
   base.Dispose(disposing);
  }

  #region Windows Form Designer generated code

  /// <summary>
  /// Required method for Designer support - do not modify
  /// the contents of this method with the code editor.
  /// </summary>
  private void InitializeComponent()
  {
            System.ComponentModel.ComponentResourceManager resources = 
new System.ComponentModel.ComponentResourceManager(typeof(Tree)); this.mainMenu1 = new System.Windows.Forms.MainMenu(); this.menuItem1 = new System.Windows.Forms.MenuItem(); this.menuItem2 = new System.Windows.Forms.MenuItem(); this.menuItem3 = new System.Windows.Forms.MenuItem(); this.menuItem4 = new System.Windows.Forms.MenuItem(); this.menuItem5 = new System.Windows.Forms.MenuItem(); this.mnuPaste = new System.Windows.Forms.MenuItem(); this.treeView1 = new System.Windows.Forms.TreeView(); this.imageList2 = new System.Windows.Forms.ImageList(); // // mainMenu1 // this.mainMenu1.MenuItems.Add(this.menuItem1); this.mainMenu1.MenuItems.Add(this.menuItem2); // // menuItem1 // this.menuItem1.Text = "Exit"; this.menuItem1.Click += new System.EventHandler(this.menuItem1_Click); // // menuItem2 // this.menuItem2.MenuItems.Add(this.menuItem3); this.menuItem2.MenuItems.Add(this.menuItem4); this.menuItem2.MenuItems.Add(this.menuItem5); this.menuItem2.MenuItems.Add(this.mnuPaste); this.menuItem2.Text = "File"; this.menuItem2.Click += new System.EventHandler(this.menuItem2_Click); // // menuItem3 // this.menuItem3.Text = "Copy"; this.menuItem3.Click += new System.EventHandler(this.mnuCopy_Click); // // menuItem4 // this.menuItem4.Text = "Cut"; this.menuItem4.Click += new System.EventHandler(this.menuCut_Click); // // menuItem5 // this.menuItem5.Text = "Delete"; this.menuItem5.Click += new System.EventHandler(this.mnuDelete_Click); // // mnuPaste // this.mnuPaste.Text = "Paste"; this.mnuPaste.Click += new System.EventHandler(this.mnuPaste_Click); // // treeView1 // this.treeView1.ImageIndex = 0; this.treeView1.ImageList = this.imageList2; this.treeView1.Indent = 20; this.treeView1.Location = new System.Drawing.Point(0, 0); this.treeView1.SelectedImageIndex = 0; this.treeView1.Size = new System.Drawing.Size(176, 177); this.treeView1.BeforeExpand +=
new
System.Windows.Forms.TreeViewCancelEventHandler(this.treeView1_BeforeExpand); this.treeView1.AfterSelect +=
new System.Windows.Forms.TreeViewEventHandler(this.treeView1_AfterSelect); this.imageList2.Images.Clear(); this.imageList2.Images.Add(((System.Drawing.Image)(resources.GetObject("resource")))); this.imageList2.Images.Add(((System.Drawing.Image)(resources.GetObject("resource1")))); this.imageList2.Images.Add(((System.Drawing.Image)(resources.GetObject("resource2")))); // // Tree // this.ClientSize = new System.Drawing.Size(176, 180); this.Controls.Add(this.treeView1); this.Menu = this.mainMenu1; this.Text = "Explorer"; this.Closing += new System.ComponentModel.CancelEventHandler(this.Tree_Closing); this.Load += new System.EventHandler(this.Tree_Load); } #endregion public string stripExtraSlash(string str) { string path =String.Empty; if ( str.Length > 1 && str.StartsWith(@"\")) { path = str.Substring( 2, str.Length - 2); } else { path = str; } return path; } private void displayChildNodes (System.Windows.Forms.TreeNode parentNode ) { DirectoryInfo FS = new DirectoryInfo(stripExtraSlash (parentNode.FullPath)); try { foreach (DirectoryInfo dirInfo in FS.GetDirectories() ) { //' �-create a new node �- TreeNode node = new TreeNode(); node.Text = dirInfo.Name; node.ImageIndex = icoClose; node.SelectedImageIndex = icoOpen; parentNode.Nodes.Add(node); // �-add the dummy node�- node.Nodes.Add(""); } } catch (Exception err) { System.Windows.Forms.MessageBox.Show(err.Message); } try { // �display all files�- foreach (FileInfo fileInfo in FS.GetFiles() ) { // �-create a new node to be added�- TreeNode node = new TreeNode(); node.Text = fileInfo.Name; node.ImageIndex = icoFile; node.SelectedImageIndex = icoFile; parentNode.Nodes.Add(node); } } catch( Exception err) { System.Windows.Forms.MessageBox.Show(err.Message); } } private void Tree_Load(object sender, EventArgs e) { TreeNode node=new TreeNode(); try { // �-create the root node�- node.ImageIndex = icoClose; node.SelectedImageIndex = icoOpen; node.Text = @"\"; treeView1.Nodes.Add(node); // �-Add the dummy node�- node.Nodes.Add(""); treeView1.SelectedNode = node; } catch (Exception err) { } } private void treeView1_BeforeExpand(object sender, TreeViewCancelEventArgs e) { // �-if leaf node then exit�- if( e.Node.ImageIndex == icoFile) return; // �-remove the dummy node and display // the subdirectories and files�- try { e.Node.Nodes.Clear(); // clears all the nodes and... displayChildNodes(e.Node); // create the nodes again } catch(Exception err ) { System.Windows.Forms.MessageBox.Show(err.Message); } // �-change the icon for this node to open�- if (e.Node.GetNodeCount(false) > 0) { e.Node.ImageIndex = icoClose; e.Node.SelectedImageIndex = icoOpen; } } private void menuItem2_Click(object sender, EventArgs e) { } private void mnuCopy_Click(object sender, EventArgs e) { // copy pathName = stripExtraSlash(treeView1.SelectedNode.FullPath); fileName = treeView1.SelectedNode.Text; copy = true; mnuPaste.Enabled = true; } private void menuCut_Click(object sender, EventArgs e) { pathName = stripExtraSlash(treeView1.SelectedNode.FullPath); fileName = treeView1.SelectedNode.Text; copy = false; mnuPaste.Enabled = true; treeView1.SelectedNode.Remove(); } private void mnuPaste_Click(object sender, EventArgs e) { File.Copy(pathName, stripExtraSlash(treeView1.SelectedNode.FullPath) + @"\" + fileName, true); System.Windows.Forms.TreeNode node = new System.Windows.Forms.TreeNode(); node.Text = fileName; node.ImageIndex = icoFile; node.SelectedImageIndex = icoFile; treeView1.SelectedNode.Nodes.Add(node); if (!copy){ File.Delete(pathName); } mnuPaste.Enabled = false; } private void mnuDelete_Click(object sender, EventArgs e) { File.Delete(stripExtraSlash(treeView1.SelectedNode.FullPath)); treeView1.SelectedNode.Remove(); } private void menuItem1_Click(object sender, EventArgs e) { this.Close(); } // This fires the event that provides selected file path to caller private void Tree_Closing(object sender, System.ComponentModel.CancelEventArgs e) { this.OnNotifyParent(); } private void treeView1_AfterSelect(object sender, TreeViewEventArgs e) { this.pathName = stripExtraSlash(treeView1.SelectedNode.FullPath); } } }

And this is how we would use it:

private void menuItem2_Click(object sender, EventArgs e)
  {
   Explorer.Tree exp = new Explorer.Tree();
            exp.NotifyParent += new EventHandler(tree_NotifyParent);
   exp.Show();   
  }

 private void tree_NotifyParent(object sender, EventArgs e)
       {            
             this.textBox1.Text= sender.ToString();
             if (this.textBox1.Text != String.Empty)
             {
                 System.Drawing.Bitmap bmp = new Bitmap(this.textBox1.Text);
                 this.pictureBox1.Image = bmp;
                 this.pictureBox1.Show();
             }             
       }

Notice above that we have an EventHandler subscription attached to the explorer's NotifyParent public eventhandler; this is doen right in the menu_click handler method when we bring up the Tree Control. Finally we provide a tree_NotifyParent method which is the target of the delegate. We assign the sender parameter to our textBox text property, and we create a Bitmap from the image file path and show it. Easy to use, easy to code. Most of the other code is simply a C# re-write of Mr. Lee's VB.NET code. I find C# much lower stress; VB.NET makes me nervous - especially after having to correct so much bad VB code that the C# compiler would never let you get away with (don't get me wrong, you can write great .NET code with VB.NET -- I just wish more developers would do so).

You can see how the TreeView FileDialog control works as illustrated by the below three screen captures:

 

You can download the Visual Studio.NET 2005 SmartPhone Solution Here.

You can download the CAB installer for it directly to your SmartPhone here.

 


Peter Bromberg is a C# MVP, MCP, and .NET consultant who has worked in the banking and financial industry for 20 years. He has architected and developed web - based corporate distributed application solutions since 1995, and focuses exclusively on the .NET Platform.

Article Discussion: