
A projekt lefordítása után a Toolbox-ra felvehető egy BrowseForFolder komponens.
A megoldáshoz a shell32.dll-ben lévő SHBrowseForFolder függvényt kell felhasználnunk, mely megjeleníti a mappa választó ablakot. Mivel a függvény nem része a .NET Framework-nek, így külsőként kell deklarálnunk azt. Mivel a függvény mutatókkal dolgozik, így használnunk kell az unsafe kulcsszót a függvény deklarációjában.
[DllImport("Shell32.dll", EntryPoint="SHBrowseForFolder")]
private unsafe static extern ITEMIDLIST * BrowseForFolders(ref BROWSEINFO browseInfo);
Ahhoz, hogy alkalmazásunkban olyan függvényeket használjunk, melyeknél meg van adva az unsafe kulcsszó, engedélyeznünk kell a projekt konfigurációs beállításainál. Ehhez válasszuk a Project - Properties menüpontot, majd a megjelenő ablakban a Configuration Properties - Build elemet. A jobb oldali listában az Allow unsafe code blocks értékét állítsuk igazra.
A teljes megvalósításhoz szükségünk lesz további külső DLL-ekben lévő függvényekre. Ezeket szintén deklaráljuk.
Továbbá meg kell adnunk az IMalloc interfész deklarációját is.
[Guid("00000002-0000-0000-C000-000000000046")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
internal interface IMalloc
{
[PreserveSig]
IntPtr Alloc([MarshalAs(UnmanagedType.U4)] int cb);
[PreserveSig]
IntPtr Realloc(IntPtr pv, [MarshalAs(UnmanagedType.U4)] int cb);
[PreserveSig]
void Free(IntPtr pv);
[PreserveSig]
[return: MarshalAs(UnmanagedType.U4)]
int GetSize(IntPtr pv);
[PreserveSig]
int DidAlloc(IntPtr pv);
[PreserveSig]
void HeapMinimize();
}
A rendszerünkben vannak olyan speciális mappák, melyeknek elérési helye gépenként változó lehet. Amennyiben ezeket szeretnénk elérni, használhatjuk majd az alábbi SystemFolders felsorolt típus elemeit. Így például a Programs elem a Program files mappára mutat, melynek alapértelmezett helye a C:\Program files. Mivel ez gépenként más-más mappába is kerülhet, így ha a programok mappáját szeretnénk elérni, akkor arra nem tényleges elérési úttal, hanem a SystemFolders Programs elemével hivatkozhatunk.
public enum SystemFolders
{
Desktop = 0x0,
Internet = 0x1,
Programs = 0x2,
ControlPanel = 0x3,
Printers = 0x4,
...
}
A megjelenő dialóg ablak tulajdonságait befolyásolhatjuk a Flags felsorolt típus elemeinek választásával.
public enum Flags
{
ReturnOnlyFileSystemFolders = 0x1,
DontGoBelowDomain = 0x2,
StatusText = 0x4,
...
}
Létrehozunk egy InitialPath nevű property-t, ezen keresztül megadhatunk egy olyan elérési útvonalat, amelyben tallózni szeretnénk a mappákat.
private string initialPath;
public string InitialPath
{
get
{
return initialPath;
}
set
{
initialPath = value;
}
}
Ha nem használjuk az InitialPath property-t, akkor a RootFolder property-n keresztül kiválaszthatunk egy előre definiált elérési utat a SystemFolders elemi közül.
private SystemFolders rootFolder;
public SystemFolders RootFolder
{
get
{
return rootFolder;
}
set
{
rootFolder = value;
}
}
Szükségünk lesz még egy Prompt nevű property-re. Ebben egy tetszőleges sztringet megadhatunk. Ez a szöveg megjelenik a dialóg ablakban, így tetszőleges üzenetet küldhetünk a felhasználónak.
private string prompt;
public string Prompt
{
get
{
return prompt;
}
set
{
prompt = value;
}
}
Dialóg ablak megjelenítéséhez az Execute függvényt kell meghívnunk. Első paraméterében a hívó ablak azonosítóját, mint szülő ablakot kell megadnunk, másodikban a Flags felsorolt típus elemeinek tetszőleges kombinációját.
public unsafe string Execute(IntPtr handle, Flags flags)
{
BROWSEINFO browseInfo = new BROWSEINFO();
ITEMIDLIST * itemIdList = null;
IMalloc malloc = null;
Marshal.ThrowExceptionForHR(GetSpecialFolderLocation(handle, rootFolder, out itemIdList));
try
{
Marshal.ThrowExceptionForHR(GetMalloc(out malloc));
Callbacks callbacks = new Callbacks(initialPath);
A már létrehozott BROWSEINFO struktúrát fel kell töltenünk a megfelelő adatokkal. Így például a hwndOwner mezőbe kerül a szülő ablak azonosítója, a pidlRoot mezőbe kerül a kiinduló mappa elérési útvonalának azonosítója, melyet a Shell_GetSpecialFolderLocation függvény szolgáltat az itemIdList változóba.
browseInfo.hwndOwner = handle;
browseInfo.pidlRoot = new IntPtr(itemIdList);
browseInfo.lpszTitle = prompt;
browseInfo.ulFlags = (uint) flags;
browseInfo.lpfn = new BrowseForFolderCallbackProc(callbacks.BrowseForFolderCallback);
browseInfo.lParam = 0;
Ezt követően meghívhatjuk a Shell_BrowseForFolder függvényt, mely megjeleníti a mappa választó ablakot. Visszatérési értékként egy mutatót kapunk, mely ITEMIDLIST típusra mutat.
itemIdList = BrowseForFolders(ref browseInfo);
if (itemIdList == null)
{
return "";
}
Ahhoz, hogy ebből szöveges elérési útvonal legyen, a Shell_GetPathFromIDList függvényünket kell meghívni.
StringBuilder path = new StringBuilder(260);
GetPathFromIDList(itemIdList, path);
return path.ToString();
}
finally
{
...
}
}
}
}
Ezt követően a komponens felhasználása már egyszerű, a szükséges property beállítása után csak meg kell hívni az Execute függvényét. Visszatérési értékként a választott mappát kapjuk meg, illetve üres sztringet kapunk, amennyiben a felhasználó a Mégsem gombbal zárta be az ablakot.
private void button1_Click(object sender, System.EventArgs e)
{
label1.Text = browseForFolder1.Execute(this.Handle, BrowseForFolder.Flags.NewDialogStyle);
}