| 1. | 先建立一個空的專案。若要建立空的專案,建立一個新的空白解決方案,並將其命名為 BrowseForFolder。在新建立的解決方案中加入新的 Visual C# .NET 類別庫,然後將該專案命名為 WinFormsExtras。 |
| 2. | 修改精靈所產生的程式碼。若要修改程式碼,將專案中的 Class1.cs 檔案,更名為 FolderBrowser.cs,然後刪除由精靈加入至這個檔案的 Class1 類別。再將命名空間由 WinFormsExtra.cs (WinFormsExtras) 更名為 Microsoft.Samples.WinForms.Extras。在專案的 [屬性] 頁面中,將預設的命名空間由 WinFormsExtras 更名為 Microsoft.Samples.WinForms.Extras。再將組件的名稱由 WinFormsExtras 變更為 Microsoft.Samples.WinForms.Extras。 |
| 3. | 加入 P/Invoke 及 ComInterop 所需的宣告。請確定已參考到 System.Runtime.InteropServices 及 System.Text,然後加入各個所需函式和介面的宣告,如下所示:using System.Runtime.InteropServices;using System.Text; internal class Win32API { // C# representation of the IMalloc interface. [InterfaceType ( ComInterfaceType.InterfaceIsIUnknown ), Guid ( "00000002-0000-0000-C000-000000000046" )] public interface IMalloc { [PreserveSig] IntPtr Alloc ( [In] int cb ); [PreserveSig] IntPtr Realloc ( [In] IntPtr pv, [In] int cb ); [PreserveSig] void Free ( [In] IntPtr pv ); [PreserveSig] int GetSize ( [In] IntPtr pv ); [PreserveSig] int DidAlloc ( IntPtr pv ); [PreserveSig] void HeapMinimize ( ); } [DllImport("User32.DLL")] public static extern IntPtr GetActiveWindow ( ); public class Shell32 { // Styles used in the BROWSEINFO.ulFlags field. [Flags] public enum BffStyles { RestrictToFilesystem = 0x0001, // BIF_RETURNONLYFSDIRS RestrictToDomain = 0x0002, // BIF_DONTGOBELOWDOMAIN RestrictToSubfolders = 0x0008, // BIF_RETURNFSANCESTORS ShowTextBox = 0x0010, // BIF_EDITBOX ValidateSelection = 0x0020, // BIF_VALIDATE NewDialogStyle = 0x0040, // BIF_NEWDIALOGSTYLE BrowseForComputer = 0x1000, // BIF_BROWSEFORCOMPUTER BrowseForPrinter = 0x2000, // BIF_BROWSEFORPRINTER BrowseForEverything = 0x4000, // BIF_BROWSEINCLUDEFILES } // Delegate type used in BROWSEINFO.lpfn field. public delegate int BFFCALLBACK ( IntPtr hwnd, uint uMsg, IntPtr lParam, IntPtr lpData ); [StructLayout ( LayoutKind.Sequential, Pack=8 )] public struct BROWSEINFO { public IntPtr hwndOwner; public IntPtr pidlRoot; public IntPtr pszDisplayName; [MarshalAs ( UnmanagedType.LPTStr )] public string lpszTitle; public int ulFlags; [MarshalAs ( UnmanagedType.FunctionPtr )] public BFFCALLBACK lpfn; public IntPtr lParam; public int iImage; } [DllImport ( "Shell32.DLL" )] public static extern int SHGetMalloc ( out IMalloc ppMalloc ); [DllImport ( "Shell32.DLL" )] public static extern int SHGetSpecialFolderLocation ( IntPtr hwndOwner, int nFolder, out IntPtr ppidl ); [DllImport ( "Shell32.DLL" )] public static extern int SHGetPathFromIDList ( IntPtr pidl, StringBuilder Path ); [DllImport ( "Shell32.DLL", CharSet=CharSet.Auto )] public static extern IntPtr SHBrowseForFolder ( ref BROWSEINFO bi ); } } |
| 4. | 建立 FolderBrowser 元件類別。若要建立元件類別,將參考加入 System.Drawing 及 System.Windows.Forms 這兩個 .NET 組件中。請務必要指定用於元件實作中的命名空間,然後建立該元件的內部資料結構,如下所示: using System.Drawing;using System.Windows.Forms;using System.ComponentModel;using System.Security.Permissions; /// <summary> /// Component wrapping access to the Browse For Folder common dialog box. /// Call the ShowDialog() method to bring the dialog box up. /// </summary> public sealed class FolderBrowser : Component { private static readonly int MAX_PATH = 260; // Root node of the tree view. private FolderID startLocation = FolderID.Desktop; // Browse info options. private int publicOptions = (int) Win32API.Shell32.BffStyles.RestrictToFilesystem | (int) Win32API.Shell32.BffStyles.RestrictToDomain; private int privateOptions = (int) Win32API.Shell32.BffStyles.NewDialogStyle; // Description text to show. private string descripti; // Folder chosen by the user. private string directoryPath = String.Empty; /// <summary> /// Enum of CSIDLs identifying standard shell folders. /// </summary> public enum FolderID { Desktop = 0x0000, Printers = 0x0004, MyDocuments = 0x0005, Favorites = 0x0006, Recent = 0x0008, SendTo = 0x0009, StartMenu = 0x000b, MyComputer = 0x0011, NetworkNeighborhood = 0x0012, Templates = 0x0015, MyPictures = 0x0027, NetAndDialUpConnections = 0x0031, } } |
| 5. | 實作 FolderBrowse 類別中的 ShowDialog 方法,如下所示: /// <summary> /// Helper function that returns the IMalloc interface used by the shell. /// </summary> private static Win32API.IMalloc GetSHMalloc ( ) { Win32API.IMalloc malloc; Win32API.Shell32.SHGetMalloc ( out malloc ); return malloc; } /// <summary> /// Shows the folder browser dialog box. /// </summary> public DialogResult ShowDialog ( ) { return ShowDialog ( null ); } /// <summary> /// Shows the folder browser dialog box with the specified owner window. /// </summary> public DialogResult ShowDialog ( IWin32Window owner ) { IntPtr pidlRoot = IntPtr.Zero; // Get/find an owner HWND for this dialog. IntPtr hWndOwner; if ( owner != null ) { hWndOwner = owner.Handle; } else { hWndOwner = Win32API.GetActiveWindow ( ); } // Get the IDL for the specific startLocation. Win32API.Shell32.SHGetSpecialFolderLocation ( hWndOwner, (int) startLocation, out pidlRoot ); if (pidlRoot == IntPtr.Zero) { return DialogResult.Cancel; } int mergedOptions = (int)publicOptions | (int)privateOptions; if ( ( mergedOptions & (int)Win32API.Shell32.BffStyles.NewDialogStyle ) != 0 ) { if ( System.Threading.ApartmentState.MTA == Application.OleRequired ( ) ) mergedOptions = mergedOptions & (~ (int)Win32API.Shell32.BffStyles.NewDialogStyle); } IntPtr pidlRet = IntPtr.Zero; try { // Construct a BROWSEINFO. Win32API.Shell32.BROWSEINFO bi = new Win32API.Shell32.BROWSEINFO ( ); IntPtr buffer = Marshal.AllocHGlobal ( MAX_PATH); bi.pidlRoot = pidlRoot; bi.hwndOwner = hWndOwner; bi.pszDisplayName = buffer; bi.lpszTitle = descriptionText; bi.ulFlags = mergedOptions; // The rest of the fields are initialized to zero by the constructor. // bi.lpfn = null; bi.lParam = IntPtr.Zero; bi.iImage = 0; // Show the dialog. pidlRet = Win32API.Shell32.SHBrowseForFolder ( ref bi ); // Free the buffer you've allocated on the global heap. Marshal.FreeHGlobal ( buffer ); if ( pidlRet == IntPtr.Zero ) { // User clicked Cancel. return DialogResult.Cancel; } // Then retrieve the path from the IDList. StringBuilder sb = new StringBuilder ( MAX_PATH ); if ( 0 == Win32API.Shell32.SHGetPathFromIDList ( pidlRet, sb ) ) { return DialogResult.Cancel; } // Convert to a string. directoryPath = sb.ToString ( ); } finally { Win32API.IMalloc malloc = GetSHMalloc ( ); malloc.Free ( pidlRoot ); if ( pidlRet != IntPtr.Zero ) { malloc.Free ( pidlRet ); } } return DialogResult.OK; } |
| 6. | 加入允許自訂對話方塊的簡易屬性。若要加入屬性,在對話方塊的 publicOptions 欄位中加入允許使用者設定或重設旗標的數個 Bool 屬性。以下就是可以實作的屬性: | • | OnlyFilesystem | | • | ShowNetworkFolders | | • | OnlySubfolders | | • | ShowTextBox | | • | ValidateUserInput | | • | SelectComputer | | • | SelectPrinter | | • | SelectFiles | | • | DirectoryPath | | • | Description |
此外,請依照下列方式實作 StartLocation 屬性: /// <summary> /// Helper function used to set and reset bits in the publicOptions bitfield. /// </summary> private void SetOptionField ( int mask, bool turnOn ) { if (turnOn) publicOptions |= mask; else publicOptions &= ~mask; } /// <summary> /// Only return file system directories. If the user selects folders /// that are not part of the file system, the OK button is unavailable. /// </summary> [Category ( "Navigation" )] [Description ( "Only return file system directories. If the user selects folders " + "that are not part of the file system, the OK button is unavailable." )] [DefaultValue ( true )] public bool OnlyFilesystem { get { return (publicOptions & (int) Win32API.Shell32.BffStyles.RestrictToFilesystem) != 0; } set { SetOptionField ( (int) Win32API.Shell32.BffStyles.RestrictToFilesystem, value ); } } /// <summary> /// Location of the root folder from which to start browsing. Only the specified /// folder and any folders beneath it in the namespace hierarchy appear /// in the dialog box. /// </summary> [Category ( "Navigation" )] [Description ( "Location of the root folder from which to start browsing. Only the specified " + "folder and any folders beneath it in the namespace hierarchy appear " + "in the dialog box." )] [DefaultValue ( typeof(FolderID), "0")] public FolderID StartLocation { get { return startLocation; } set { new UIPermission ( UIPermissionWindow.AllWindows ).Demand ( ); startLocation = value; } } /// <summary> /// Full path to the folder selected by the user. /// </summary> [Category("Navigation")] [Description("Full path to the folder slected by the user.")] public string DirectoryPath { get { return directoryPath; } } |
| 7. | 提供元件的工具列圖示。若要加入工具列圖示,在 [方案總管] 中的專案上按一下滑鼠右鍵,並依序按下 [加入]、[新增項目]、[資源],然後按一下 [點陣圖檔]。將這個新的點陣圖檔命名為 FolderBrowser.bmp,調整其大小成為 16 x 16 像素。視需要編輯或修改點陣圖檔。在 [方案總管] 中,選取 FolderBrowser.bmp 檔案,開啟屬性方格,然後將 [建置動作] 設定成 [內嵌資源]。接下來,加入一個屬性 (Attribute) 至 FolderBrowser 類別中,以建立點陣圖和元件的關聯性,如下所示: [ToolboxBitmap(typeof(FolderBrowser))] public sealed class FolderBrowser : Component { // ... } |
| 8. | 建置專案。 |