文章編號: 306285 - 上次校閱: 2004年8月13日 - 版次: 1.0
HOW TO:使用 Visual C# .NET 實作包裝 [瀏覽資料夾] 通用對話方塊的 Managed 元件
本篇逐步解說的文件說明了如何撰寫一個設計階段的元件來包裝
[瀏覽資料夾] 的通用對話方塊。
實作設計階段元件 先建立一個空的專案。若要建立空的專案,建立一個新的空白解決方案,並將其命名為 BrowseForFolder。在新建立的解決方案中加入新的 Visual C# .NET 類別庫,然後將該專案命名為 WinFormsExtras。 修改精靈所產生的程式碼。若要修改程式碼,將專案中的 Class1.cs 檔案,更名為 FolderBrowser.cs,然後刪除由精靈加入至這個檔案的 Class1 類別。再將命名空間由 WinFormsExtra.cs (WinFormsExtras) 更名為 Microsoft.Samples.WinForms.Extras 。在專案的 [屬性] 頁面中,將預設的命名空間由 WinFormsExtras 更名為 Microsoft.Samples.WinForms.Extras 。再將組件的名稱由 WinFormsExtras 變更為 Microsoft.Samples.WinForms.Extras 。 加入 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 );
}
}
建立 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 descriptionText = "Please select a folder below:";
// 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,
}
}
實作 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;
}
加入允許自訂對話方塊的簡易屬性。若要加入屬性,在對話方塊的 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;
}
}
提供元件的工具列圖示。若要加入工具列圖示,在 [方案總管] 中的專案上按一下滑鼠右鍵,並依序按下 [加入] 、[新增項目] 、[資源] ,然後按一下 [點陣圖檔] 。將這個新的點陣圖檔命名為 FolderBrowser.bmp,調整其大小成為 16 x 16 像素。視需要編輯或修改點陣圖檔。在 [方案總管] 中,選取 FolderBrowser.bmp 檔案,開啟屬性方格,然後將 [建置動作] 設定成 [內嵌資源] 。接下來,加入一個屬性 (Attribute) 至 FolderBrowser 類別中,以建立點陣圖和元件的關聯性,如下所示:
[ToolboxBitmap(typeof(FolderBrowser))]
public sealed class FolderBrowser : Component
{ // ...
}
建置專案。 實作 Test 專案 產生一個空的專案。若要產生專案,將名稱為 CsClient 的新 Visual C# Windows 應用程式專案加入至 BrowseForFolder 方案中。在剛才所建立的 Microsoft.Samples.WinForms.Extras 組件中加入參考。 將元件加入至工具列。若要加入元件,開啟 [工具箱] 視窗,在該視窗中按一下滑鼠右鍵,然後選取 [自訂工具箱] 。在 [.NET Framework 元件] 索引標籤中,按一下 [瀏覽] ,然後巡覽至您剛才所建立的 Microsoft.Samples.WinForms.Extras.dll 檔案。當清單中出現 FolderBrowser 元件時,選取該元件並關閉對話方塊。 自訂表單。若要自訂表單,將 Form1 開啟於設計模式中,開啟 [工具箱] 視窗,將按鈕和 FolderBrowser 元件拖曳至您的設計區域中。 自訂 FolderBrowser 元件。若要自訂該元件,按一下 folderBrowser1 圖示,然後記下屬性窗格中所有可以自訂的屬性。您可以視個人需要自訂這些屬性。 加入顯示對話方塊的程式碼。若要加入此一程式碼,在您拖曳至表單中的按鈕上按一下滑鼠右鍵,然後將下列程式碼加入至按鈕的處理常式中:
private void button1_Click(object sender, System.EventArgs e)
{
if ( DialogResult.OK == folderBrowser1.ShowDialog() )
MessageBox.Show ( folderBrowser1.DirectoryPath );
}
建置方案,然後執行 CsClient 專案。 如果您對本文有意見或是建議,請將您的寶貴意見以電子郵件傳送給
Visual C# .NET 文章的意見回覆
(mailto:vcskb@microsoft.com?subject=Q306285 Feedback)
。
這篇文章中的資訊適用於: Microsoft Visual C# .NET 2002 Standard Edition kbhowto kbhowtomaster kbdownload kbcominterop KB306285
Microsoft及(或)其供應商不就任何在本伺服器上發表的文字資料及其相關圖表資訊的恰當性作任何承諾。所有文字資料及其相關圖表均以「現狀」供應,不負任何擔保責任。Microsoft及(或)其供應商謹此聲明,不負任何對與此資訊有關之擔保責任,包括關於適售性、適用於某一特定用途、權利或不侵權的明示或默示擔保責任。Microsoft及(或)其供應商無論如何不對因或與使用本伺服器上資訊或與資訊的實行有關而引起的契約、過失或其他侵權行為之訴訟中的特別的、間接的、衍生性的損害或任何因使用而喪失所導致的之損害、資料或利潤負任何責任。