可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
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:
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.
回答2:
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
回答3:
It's been done. You can use FolderBrowserDialogEx -
a re-usable derivative of the built-in FolderBrowserDialog. This one allows you to type in a path, even a UNC path. You can browse for folders, or files+folders. You can browse for computers or printers with it. Based on the built-in FBD, but ... better. More flexible. If you click a folder in the GUI, the path appears in the textbox. If you key in a path, the folder gets activatied. Lots of options the built-in dialog lacks.
Full Source code. Free. MS-Public license.
FolderBrowserDialogEx http://www.freeimagehosting.net/uploads/bfbab6cea5.png
Code to use it:
var dlg1 = new Ionic.Utils.FolderBrowserDialogEx();
dlg1.Description = "Select a folder to extract to:";
dlg1.ShowNewFolderButton = true;
dlg1.ShowEditBox = true;
//dlg1.NewStyle = false;
dlg1.SelectedPath = txtExtractDirectory.Text;
dlg1.ShowFullPathInEditBox = true;
dlg1.RootFolder = System.Environment.SpecialFolder.MyComputer;
// Show the FolderBrowserDialog.
DialogResult result = dlg1.ShowDialog();
if (result == DialogResult.OK)
{
txtExtractDirectory.Text = dlg1.SelectedPath;
}
回答4:
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).
回答5:
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.
回答6:
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.
回答7:
http://www.pinvoke.net/default.aspx/shell32.shbrowseforfolder
here is gerat link if you change in this sample
bi.ulFlags = BIF_NEWDIALOGSTYLE | BIF_SHAREABLE;
for
bi.ulFlags = BIF_NEWDIALOGSTYLE | BIF_SHAREABLE | BIF_BROWSEINCLUDEFILES;
you will get what you want
回答8:
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&df=90&mpp=25&noise=3&sort=Position&view=Quick&fr=26
It also explains the other options that are available for "customizing" the FolderBrowser dialog,
回答9:
this will allow you to select folders using OpenFileDialog
openFileDialog1.CheckFileExists = false;
openFileDialog1.ValidateNames = false;
回答10:
The Ookii Dialogs implement a folder browser dialog that allow for both files or folders as input, and is available for Windows Forms and WPF.
Ookii.Dialogs.Wpf
https://github.com/caioproiete/ookii-dialogs-wpf
Ookii.Dialogs.WinForms
https://github.com/caioproiete/ookii-dialogs-winforms