views:

3810

answers:

7

Is there an "easy" way to select either a file OR a folder from the same dialog?

In many apps I create I allow for both files or folders as input. Until now i always end up creating a switch to toggle between file or folder selection dialogs or stick with drag-and-drop functionality only.

Since this seems such a basic thing i would imagine this has been created before, but googling does not result in much information. So it looks like i would need to start from scratch and create a custom selection Dialog, but I rather not introduce any problems by reinventing the wheel for such a trivial task.

Anybody any tips or existing solutions?

To keep the UI consistent it would be nice if it is possible to extend the OpenFileDialog (or the FolderBrowserDialog).

+1  A: 

AFAIK, there is nothing in the .NET framework that does this out of the box.

The .NET file dialogs derive from CommonDialog:

Inherited classes are required to implement RunDialog by invoking ShowDialog to create a specific common dialog box. Inherited classes can override HookProc to implement specific dialog box hook functionality.

Mitch Wheat
+1  A: 

All of the built in dialogs use the shell API's that correspond to their action, PrintDialog, OpenFileDialog, SaveFileDialog, etc...

You would most likely have to manually build this functionality.

Tom Anderson
+5  A: 

Technically, it is possible. The shell dialog used by FolderBrowseDialog has the ability to return both files and folders. Unfortunately, that capability isn't exposed in .NET. Not even reflection can poke the required option flag.

To make it work, you'd have to P/Invoke SHBrowseForFolder() with the BIF_BROWSEINCLUDEFILES flag turned on in BROWSEINFO.ulFlags (value = 0x4000). The P/Invoke is gritty, it is best to copy and paste the code from another source or the FolderBrowseDialog class itself with Reflector's help.

Hans Passant
Thanks, the BIF_BROWSEINCLUDEFILES did the trick.See answer below for a code sample.
barry
+4  A: 

Based on the above tips I found some working code that uses the standard Folder Browser dialog at the following location: http://topic.csdn.net/t/20020703/05/845468.html

The Class for the extended Folder Browser Dialog

Imports System   
Imports System.Text   
Imports System.Windows.Forms   
Imports System.Runtime.InteropServices   

Public Class DirectoryDialog 
    Public Structure BROWSEINFO 
     Public hWndOwner As IntPtr 
     Public pIDLRoot As Integer 
     Public pszDisplayName As String 
     Public lpszTitle As String 
     Public ulFlags As Integer 
     Public lpfnCallback As Integer 
     Public lParam As Integer 
     Public iImage As Integer 
    End Structure 

    Const MAX_PATH As Integer = 260

    Public Enum BrowseForTypes As Integer 
     Computers = 4096 
     Directories = 1 
     FilesAndDirectories = 16384 
     FileSystemAncestors = 8 
    End Enum 

    Declare Function CoTaskMemFree Lib "ole32" Alias "CoTaskMemFree" (ByVal hMem As IntPtr) As Integer 
    Declare Function lstrcat Lib "kernel32" Alias "lstrcat" (ByVal lpString1 As String, ByVal lpString2 As String) As IntPtr 
    Declare Function SHBrowseForFolder Lib "shell32" Alias "SHBrowseForFolder" (ByRef lpbi As BROWSEINFO) As IntPtr 
    Declare Function SHGetPathFromIDList Lib "shell32" Alias "SHGetPathFromIDList" (ByVal pidList As IntPtr, ByVal lpBuffer As StringBuilder) As Integer 
    Protected Function RunDialog(ByVal hWndOwner As IntPtr) As Boolean 

     Dim udtBI As BROWSEINFO = New BROWSEINFO() 
     Dim lpIDList As IntPtr 
     Dim hTitle As GCHandle = GCHandle.Alloc(Title, GCHandleType.Pinned) 
     udtBI.hWndOwner = hWndOwner 
     udtBI.lpszTitle = Title 
     udtBI.ulFlags = BrowseFor 
     Dim buffer As StringBuilder = New StringBuilder(MAX_PATH) 
     buffer.Length = MAX_PATH 
     udtBI.pszDisplayName = buffer.ToString() 
     lpIDList = SHBrowseForFolder(udtBI) 
     hTitle.Free() 
     If lpIDList.ToInt64() <> 0 Then 
      If BrowseFor = BrowseForTypes.Computers Then 
       m_Selected = udtBI.pszDisplayName.Trim() 
      Else 
       Dim path As StringBuilder = New StringBuilder(MAX_PATH) 
       SHGetPathFromIDList(lpIDList, path) 
       m_Selected = path.ToString() 
      End If 
      CoTaskMemFree(lpIDList) 
     Else 
      Return False 
     End If 
     Return True 
    End Function 

    Public Function ShowDialog() As DialogResult 
     Return ShowDialog(Nothing) 
    End Function 

    Public Function ShowDialog(ByVal owner As IWin32Window) As DialogResult 
     Dim handle As IntPtr 
     If Not owner Is Nothing Then 
      handle = owner.Handle 
     Else 
      handle = IntPtr.Zero 
     End If 
     If RunDialog(handle) Then 
      Return DialogResult.OK 
     Else 
      Return DialogResult.Cancel 
     End If 
    End Function 

    Public Property Title() As String 
     Get 
      Return m_Title 
     End Get 
     Set(ByVal Value As String) 
      If Value Is DBNull.Value Then 
       Throw New ArgumentNullException() 
      End If 
      m_Title = Value 
     End Set 
    End Property

    Public ReadOnly Property Selected() As String 
     Get 
      Return m_Selected 
     End Get 
    End Property 

    Public Property BrowseFor() As BrowseForTypes
     Get 
      Return m_BrowseFor 
     End Get 
     Set(ByVal Value As BrowseForTypes) 
      m_BrowseFor = Value 
     End Set 
    End Property 

    Private m_BrowseFor As BrowseForTypes = BrowseForTypes.Directories 
    Private m_Title As String = "" 
    Private m_Selected As String = "" 

    Public Sub New() 
    End Sub
End Class

The code to implement the extended dialog

Sub Button1Click(ByVal sender As Object, ByVal e As EventArgs)
    Dim frmd As DirectoryDialog = New DirectoryDialog()
    ' frmd.BrowseFor = DirectoryDialog.BrowseForTypes.Directories   
    ' frmd.BrowseFor = DirectoryDialog.BrowseForTypes.Computers   
    frmd.BrowseFor = DirectoryDialog.BrowseForTypes.FilesAndDirectories   
    frmd.Title = "Select a file or a folder"    
    If frmd.ShowDialog(Me) = DialogResult.OK Then   
     MsgBox(frmd.Selected)   
    End If   
End Sub
barry
A: 

If you would like to display only specific file types, the following article (with source code in C#) can help you:

http://www.codeproject.com/KB/shell/csdoesshell1.aspx?fid=14137&amp;df=90&amp;mpp=25&amp;noise=3&amp;sort=Position&amp;view=Quick&amp;fr=26

It also explains the other options that are available for "customizing" the FolderBrowser dialog,

barry
+3  A: 
Cheeso
+2  A: 

You can use standard OpenFileDialog to select a folder. Here is an article in CodeProject that demonstrated a way to do it (http://www.codeproject.com/KB/dialog/OpenFileOrFolderDialog.aspx).

stankovski
Thanks! i gave it a try, but i could not get it to work. The file open dialog could only Open files. It might have to do with the OS (I am using Windows 7). It would be really nice if we could use the File Open Dialog without having to create a custom control from scratch. The previous solutions using "extended" folder selection dialogs work but the resulting UI feels outdated.
barry
I found the problem with Windows 7. In addition to ValidateNames the following 2 flags should be set:dialog.CheckFileExists = false;dialog.CheckPathExists = true;
stankovski