Title:  Create Open File Dialog for Windows Mobile 6 (Smartphone) device
Author:      Greg Dubinovskiy 
Email:       [email protected]
Environment: VB.NET
Keywords:    OpenFileDlg, SaveFileAsDlg
Level:       Intermediate
Description: Create "Open File" and "Save File as" Dialog windows for Windows Mobile 6 (Smartphone) device
Section      Miscellaneous
SubSection   General

Introduction

While working on the project of converting and upgrading Siccolo for Windows Mobile 5 to Siccolo for Windows Mobile 6 for Smartphones, I realized that Windows Mobile 6 for Smartphone edition missing OpenFilDlg and SaveFileAsDlg controls.

While the Smartphone platform shares many features with its bigger brother the Pocket PC, it differs somewhat in its file management functionality. By default a Smartphone does not include a File Explorer application and there are no common file dialog boxes available to the developer. If your application involves opening data files you will need some method of browsing and selecting data files. Another consideration for Smartphone developers is that these devices generally have less on-board storage memory than a basic Pocket PC. Depending on your application you can use either removable storage cards or retrieve data via the mobile network.
The .NET Compact Framework includes a common dialog for selecting a file to be opened, however this dialog is not available for the Smartphone platform. The full .NET Framework offers a FolderBrowserDialog as a quick method to choose a specific folder, this is not present on the .NET Compact Framework at all.

In Siccolo SP was necessary in some scenarios to provide the user with a mechanism to browse the file system.

Some of the features of Siccolo includes ability to browse and select SQL Script on mobile device, and be able to save SQL Script in a given folder on mobile device.
This article shows how to create a form to allow select file for opening, or to "save file as":

Code

Now, the code part. In my case, I wanted to create a form that:
  • Looks and works like standard file explorer in Windows Mobile 6,
  • Allows to switch between "Open File" and "Save File As" dialog mode - allows to select a file to open, or to select a folder and enter a file name for saving.
  • Exposes SelectedFileName as a property
  • Allows to "filter" files to display

    To display folders and files on the mobile device, I'm using TreeView control. To allow one form behave as "Open File Dialog" and "Save File As Dialog", I added text box (hidden/shown during run-time based on selection):



       Public Enum DialogMode As Integer
            OpenFile = 0
            SaveFileAs = 1
        End Enum
        Private m_DialogMode As DialogMode = DialogMode.OpenFile
        Public Property Mode() As DialogMode
            Get
                Return m_DialogMode
            End Get
            Set(ByVal value As DialogMode)
                m_DialogMode = value
    
                If m_DialogMode = DialogMode.SaveFileAs Then
                    txtFileName.Visible = True
                    lblFileName.Visible = True
                    trwFileExplorer.Height = trwFileExplorer.Height - txtFileName.Height - 10
                End If
            End Set
        End Property
    


    and to display such form:
    	...
    	...
       Dim dlgOpenFile As New frmOpenFileDialogSP
       dlgOpenFile.Filter = "*.sql"
       dlgOpenFile.Mode = frmOpenFileDialogSP.DialogMode.OpenFile
    
       If dlgOpenFile.ShowDialog() = Windows.Forms.DialogResult.OK Then
    	...
    	...
    


    In the TreeView, I wanted to display just "one level", and as soon as user selects a folder, TreeView will display contents (sub-folders and files) of the selected folder:



    In order to populate TreeView with sub folders and files for a given folder:
       Private Sub AddSubFolders(ByVal ParentPath As String)
            Try
    
                Dim ParentFolder As New DirectoryInfo(stripExtraSlash(ParentPath))
    
                'loop in directories
                Dim DirInfoComparer As DirectoryInfoComparer = New DirectoryInfoComparer()
                Dim DirList() As DirectoryInfo = ParentFolder.GetDirectories()
                Array.Sort(DirList, DirInfoComparer)
    
                Dim dirInfo As DirectoryInfo
    
                For Each dirInfo In DirList 'ParentFolder.GetDirectories()
                    Dim DirectoryNode As New TreeNode
                    DirectoryNode.Text = dirInfo.Name ' name of file or dir
                    DirectoryNode.Tag = dirInfo.FullName
                    DirectoryNode.ImageIndex = 0 'folder
                    trwFileExplorer.Nodes.Add(DirectoryNode)
                Next
    
                'loop in files:
                Dim FileInfoComparer As FileInfoComparer = New FileInfoComparer()
    
                If m_Filter = "" Then m_Filter = "*.*"
                Dim FileList() As FileInfo = ParentFolder.GetFiles(m_Filter)
                Array.Sort(FileList, FileInfoComparer)
    
                Dim fileInfo As FileInfo
                Dim AddFile As Boolean = True
    
                For Each fileInfo In FileList   'ParentFolder.GetFiles()
                    Dim FileNode As New TreeNode
                    FileNode.Text = fileInfo.Name
                    FileNode.Tag = fileInfo.FullName
                    FileNode.ImageIndex = 1 'file
                    FileNode.SelectedImageIndex = 1 'file
                    trwFileExplorer.Nodes.Add(FileNode)
                Next
    
            Catch ex_add_subfolder As Exception
                ' handle error here....
            End Try
        End Sub
    
      In the above code:
    1. first, get a folder we're in: Dim ParentFolder As New DirectoryInfo(stripExtraSlash(ParentPath)), where stripExtraSlash() function :
         Private Function stripExtraSlash(ByVal str As String) As String
              ' —-strip away the extra "\" for 
              ' subdirectories. e.g. \\My documents
              Dim path As String = str
              If path.Length > 1 And (Mid(path, 1, 1) = "\") Then
                  path = Mid(path, 2, path.Length - 1)
              End If
      
              Return path
          End Function
      
    2. then, get sub-folders, using ParentFolder.GetDirectories(). But because I wanted to present list of folders and files sorted, I had to add "custom" comparer for sorting:
      	...
         Dim DirInfoComparer As DirectoryInfoComparer = New DirectoryInfoComparer()
         Dim DirList() As DirectoryInfo = ParentFolder.GetDirectories()
         Array.Sort(DirList, DirInfoComparer)
      	...
      
      where DirectoryInfoComparer() and FileInfoComparer():
      Imports System.IO
      
      Public Class DirectoryInfoComparer Implements System.Collections.IComparer
      
          Public Function Compare(ByVal objDirInfo1 As Object, ByVal objDirInfo2 As Object) As Integer _
      			Implements IComparer.Compare
              Dim DirInfo1 As System.IO.DirectoryInfo = CType(objDirInfo1, System.IO.DirectoryInfo)
              Dim DirInfo2 As System.IO.DirectoryInfo = CType(objDirInfo2, System.IO.DirectoryInfo)
      
              Return String.Compare(DirInfo1.Name, DirInfo2.Name, True)
          End Function
      End Class
      
      Public Class FileInfoComparer   Implements System.Collections.IComparer
          Public Function Compare(ByVal objFileInfo1 As Object, ByVal objFileInfo2 As Object) As Integer _
      			Implements IComparer.Compare
              Dim FileInfo1 As System.IO.FileInfo = CType(objFileInfo1, System.IO.FileInfo)
              Dim FileInfo2 As System.IO.FileInfo = CType(objFileInfo2, System.IO.FileInfo)
      
              Return String.Compare(FileInfo1.Name, FileInfo2.Name, True)
          End Function
      End Class
      
    3. Once folders are in more "sorted" way, we can add them to the TreeView:
      	...
         For Each dirInfo In DirList 'ParentFolder.GetDirectories()
                      Dim DirectoryNode As New TreeNode
                      DirectoryNode.Text = dirInfo.Name ' name of file or dir
                      DirectoryNode.Tag = dirInfo.FullName
                      DirectoryNode.ImageIndex = 0 'folder
                      trwFileExplorer.Nodes.Add(DirectoryNode)
         Next
      
      	... 
      

    Notice, as a folder node is added to TreeView, node tag contains full name of a direcory being added.
    And files are the same way - get them with ParentFolder.GetFiles(), sort them using FileInfoComparer(), add them to TreeView using Nodes.Add(). And we can provide "filtering" with ParentFolder.GetFiles(searchPattern).

    And, when user selectes a folder (using "enter" key on smartphone), TreeView is populated with contains of that folder:
       Private Sub frmOpenFileDialogSP_KeyDown(ByVal sender As System.Object, ByVal e As System.Windows.Forms.KeyEventArgs) _
    			Handles MyBase.KeyDown
    		...
            If (e.KeyCode = System.Windows.Forms.Keys.Enter) Then
                'Enter:
                'do we have file selected?
                If trwFileExplorer.SelectedNode Is Nothing Then
                    Exit Sub
                End If
    
                If trwFileExplorer.SelectedNode.ImageIndex <> 1 Then
                    Dim Path As String = System.IO.Path.Combine(trwFileExplorer.Tag, trwFileExplorer.SelectedNode.FullPath)
                    trwFileExplorer.BeginUpdate()
                    'delete nodes:
                    trwFileExplorer.Nodes.Clear()
                    'selected is folder - open it!
                    AddTopFolder(Path)
                    'done
                    trwFileExplorer.EndUpdate()
                    Exit Sub
                End If
    
    		...
    		...	
    
            End If
    
    		...
    		...
        End Sub
    

    Where AddTopFolder() function:
       Private Sub AddTopFolder(ByVal InitialPath As String)
    
            Dim strPath As String = IIf(InitialPath = "", "\", InitialPath)
    
            'check if path given is valid:
            Try
                Dim TopFolder As New DirectoryInfo(stripExtraSlash(strPath))
    
                ChangeTopFolder(TopFolder)
    
                mnuUp.Enabled = Not (TopFolder.Parent Is Nothing)
    
            Catch ex As Exception
    
                MessageBox.Show("Invalid Path [" & InitialPath & "]" & vbCrLf & _
                               "-----------------------------------" & vbCrLf & _
                               ex.Message & vbCrLf & _
                               "-----------------------------------", _
                               "Siccolo SP", _
                               MessageBoxButtons.OK, _
                               MessageBoxIcon.Exclamation, MessageBoxDefaultButton.Button1)
    
                Me.DialogResult = Windows.Forms.DialogResult.Cancel
                Me.Close()
    
            End Try
    
    
            'Dim newNode As Windows.Forms.TreeNode = New TreeNode
            'newNode = trwFileExplorer.Nodes.Add(strNode)
            'newNode.Tag = strNode
            '************************************************************
            'add sub folders, under root folder:
            AddSubFolders(strPath)
            '************************************************************
        End Sub
    

    And ChangeTopFolder():
        Private Sub ChangeTopFolder(ByVal Folder As DirectoryInfo)
            lblPath.Text = Folder.Name
            trwFileExplorer.Tag = Folder.FullName
        End Sub
    
    So, everytime Parent Folder is changed - TreeView keeps directory name in Tag property - to get full path of a selected folder in TreeView, we just need to combine Path.Combine(trwFileExplorer.Tag, trwFileExplorer.SelectedNode.FullPath)
    And then, we just need to allow user to go up one level (as in standard Windows Mobile File Explorer):
        Private Sub mnuUp_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles mnuUp.Click
            'can we go Up?!:
            Try
                Dim Path As String = trwFileExplorer.Tag
                Dim ParentFolder As DirectoryInfo = New DirectoryInfo(Path)
                If ParentFolder.Parent Is Nothing Then Exit Sub
    
                Path = ParentFolder.Parent.FullName
    
                trwFileExplorer.BeginUpdate()
                'delete nodes:
                trwFileExplorer.Nodes.Clear()
                'selected is folder - open it!
                AddTopFolder(Path)
                'done
                trwFileExplorer.EndUpdate()
    
            Catch ex_go_up As Exception
            End Try
        End Sub
    
    To "go up" - because TreeView.Tag property contains full directory path of the current folder, based on that we create New DirectoryInfo(Path), and by referencing ParentFolder.Parent, we get full directory path of "up" folder.


    And finally, when user "clicks" enter key
       Private Sub frmOpenFileDialogSP_KeyDown(ByVal sender As System.Object, _
    		ByVal e As System.Windows.Forms.KeyEventArgs) Handles MyBase.KeyDown
    	...
    
                If m_DialogMode = DialogMode.OpenFile Then
                    'set properties:
                    m_FullPath = trwFileExplorer.Tag
                    m_SelectedFileName = trwFileExplorer.SelectedNode.Text
                    m_SelectedFullFileName = System.IO.Path.Combine(m_FullPath, m_SelectedFileName)
    
                    Me.DialogResult = Windows.Forms.DialogResult.OK
                    Me.Close()
    
                Else
                    'move focus to text box
                    txtFileName.Focus()
                End If
    	...
        end sub	
    
        Private Sub txtFileName_KeyDown(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyEventArgs) _
    		Handles txtFileName.KeyDown
            If (e.KeyCode = System.Windows.Forms.Keys.Enter) Then
                If m_DialogMode = DialogMode.SaveFileAs Then
                    'set properties:
                    m_FullPath = trwFileExplorer.Tag
                    m_SelectedFileName = txtFileName.Text
                    m_SelectedFullFileName = System.IO.Path.Combine(m_FullPath, m_SelectedFileName)
    
                    Me.DialogResult = Windows.Forms.DialogResult.OK
                    Me.Close()
                End If
            End If
        End Sub
    
    
    And result: