15 мая 2023 года "Исходники.РУ" отмечают своё 23-летие!
Поздравляем всех причастных и неравнодушных с этим событием!
И огромное спасибо всем, кто был и остаётся с нами все эти годы!

Главная Форум Журнал Wiki DRKB Discuz!ML Помощь проекту




"Проводник" для компьютеров, директорий, файлов и принтеров

(Перевод одноимённой статьи с сайта delphi.about.com )

Обзор (Browse)
В Windows есть несколько способов отобразить структуру директорий на компьютере. Большинство программистов Delphi знают, как пользоваться компонентой TOpenDialog, чтобы предоставить возможность пользователю перед открытием файла предварительно просматривать структуру директорий. Однако, используя API функцию SHBrowseForFolder мы можем заставить стандартный диалог Windows осуществлять просмотр не только директорий на диске, но и принтеров и компьютеров в сети.

Browse dialog

Сперва давайте посмотрим, что необходимо для SHBrowseForFolder. Вот объявление функции:

 

function SHBrowseForFolder
(var BrowseInfo: TBrowseInfo): PItemIDList; stdcall;

Вся информация для инициализации и настройки диалогового окна Browse For Folder передаётся через переменную типа BrowseInfo. Сама же функция нам возвратит список ID элементов (скажем: местоположение выбранной папки).
Теперь давайте посмотрим, как заполнить структуру записей информацией, которая необходима для инициализации диалогового окна Browse for Folder, а затем вызовем SHBrowseForFolder для отображения диалогового окна.

Структура BrowseInfo
Основные элементы структуры, это поля lpszTitle и ulFlags. Поле lpszTitle - это текст, в виде статического текстового элемента управления (static text control) над деревом (treeview). Элемент ulFlags определяет флаги, которые задают режим отображения диалога.
Флаги позволяют сделать диалог более юзабельным, чем просто отображение директорий. В таблице приведены некоторые флаги, которые позволяют расширить возможности диалога Browse For Folders:

 

Значение Назначение
BIF_BROWSEFORCOMPUTER Возвратить только компьютеры. Если пользователь выбрал что-то отличное от компьютеров, то кнопка OK останется серой.
BIF_BROWSEFORPRINTER Возвратить только принтеры. Если пользователь выбрал что-то отличное от принтеров, то кнопка OK останется серой.
BIF_RETURNONLYFSDIRS Возвратить только директории файловой системы. Если пользователь выберет директории, которые не являются частью файловой системы, то кнопка OK останется серой.
BIF_BROWSEINCLUDEFILES Диалог просмотра будет отображать файлы вместе с директориями


Например, для просмотра как директорий, так и файлов, необходимо установить ulFlags в BIF_BROWSEINCLUDEFILES.

Browse Delphi Form

   Код для Delphi
Когда мы поместим всё вышеописанное в функцию Delphi, которая будет создавать структуру, инициализировать её, и вызывать SHBrowseForFolder() для отображения диалогового окна, то она будет выглядеть следующим образом:

 

uses ShellAPI, ShlObj;
...
function BrowseDialog
 (const Title: string; const Flag: integer): string;
var
  lpItemID : PItemIDList;
  BrowseInfo : TBrowseInfo;
  DisplayName : array[0..MAX_PATH] of char;
  TempPath : array[0..MAX_PATH] of char;
begin
  Result:='';
  FillChar(BrowseInfo, sizeof(TBrowseInfo), #0);
  with BrowseInfo do begin
    hwndOwner := Application.Handle;
    pszDisplayName := @DisplayName;
    lpszTitle := PChar(Title);
    ulFlags := Flag;
  end;
  lpItemID := SHBrowseForFolder(BrowseInfo);
  if lpItemId <> nil then begin
    SHGetPathFromIDList(lpItemID, TempPath);
    Result := TempPath;
    GlobalFreePtr(lpItemID);
  end;
end;

Функция BrowseDialog имеет на входе два параметра: Title и Flag. Title - это текст над деревом (поле lpszTitle в записи BrowseInfo). Параметр Flag используется для заполнения поля ulFlags.

Теперь эту функцию можно вызвать (для отображения директории, выбранной пользователем) следующим образом:

 

procedure TfrMain.btnBrowseClick(Sender: TObject);
var sTitle, sFolder: string;
    iFlag : integer;
begin
  sTitle:='Choose a ' +
          rgBrowseFor.Items[rgBrowseFor.ItemIndex];
  case rgBrowseFor.ItemIndex of
    0:  iFlag :=  BIF_RETURNONLYFSDIRS;
    1:  iFlag :=  BIF_BROWSEINCLUDEFILES;
    2:  iFlag :=  BIF_BROWSEFORCOMPUTER;
    3:  iFlag :=  BIF_BROWSEFORPRINTER;
  end;
  sFolder := BrowseDialog(sTitle, iFlag);
  if sFolder <> '' then
    edSelected.text := sFolder
  else
    edSelected.text := 'Nothing selected';
end;

Обратите внимание: На форме (с именем: frMain) присутствует компонент RadioGroup (группа радио кнопок (с именем: rgBrowseFor)) который имеет четыре элеемента, каждый из которых представляет значение для переменной Flag (см. картинку выже). Функция извлекает из lpItemID путь к выбранной папке и возвращает его (заполнив компонент текстового поля edSelected). Если функция вернула ошибку (или пользователь нажал кнопку Cancel), то возвращённая строка будет пустой.

   Ссылки на информацию, относящуюся к данной статье
Детальная информация по функции SHBrowseForFolder и по структуре BROWSEINFO. Так же в приложении можно ограничить диапазон директорий, которые пользователь может открывать, путём указания корневой директории. См. как установить диалоговое окно SHBrowseForFolder с корневой директорией, установленной на Program Files (к сожалению, код не на Delphi, но смысл понятен).