355 500 произведений, 25 200 авторов.

Электронная библиотека книг » Алексей Гладкий » Excel. Трюки и эффекты » Текст книги (страница 9)
Excel. Трюки и эффекты
  • Текст добавлен: 4 октября 2016, 04:13

Текст книги "Excel. Трюки и эффекты"


Автор книги: Алексей Гладкий


Соавторы: Александр Чиртик
сообщить о нарушении

Текущая страница: 9 (всего у книги 24 страниц) [доступный отрывок для чтения: 9 страниц]

Построение дерева каталогов

Рассмотрим довольно интересный пример, основанный на использовании функции поиска SearchlnFolder, – построение дерева каталогов для определенного диска (рис. 4.7).

Рис. 4.7. Дерево каталогов

Для простоты (и чтобы не отвлекать внимания от построения дерева) диск задается в программе жестко. При необходимости это можно легко исправить (как определять диски, вы уже знаете).

Рассмотрим работу приложения по порядку. Элемент управления TreeView на форме имеет имя tree. Содержимое списка изображений (ImageList), используемого деревом, приведено на рис. 4.8.

Рис. 4.8. Изображения для элементов дерева

Первый элемент дерева (соответствует диску) образуется при создании формы (листинг 4.27).

Листинг 4.27. Создание первого элемента дерева – диска

procedure TForm3.FormCreate(Sender: TObject);

begin

//Корневой элемент дерева (диск)

root := tree.Items.Add(tree.Items.GetFirstNode, 'C:');

root.ImageIndex := 0;

root.SelectedIndex := 0;

SetExpanded(root, False);

end;

Здесь и далее в примере root позволяет быстро получать доступ к корневому элементу дерева. Используемая в листинге 4.27 процедура применяется для установки состояния элементов дерева (листинг 4.28).

Листинг 4.28. Установка состояния элемента дерева

procedure TForm3.SetExpanded(Node: TTreeNode; isExpanded: Boolean);

begin

if isExpanded then

begin

//Подготавливаем элемент к загрузке содержимого каталога

Node.Data := Pointer(1);

Node.DeleteChildren;

end

else

begin

//Содержимое каталога не прочитано (или его следует обновить)

Node.Data := Pointer(0);

Node.Collapse(False);

Node.DeleteChildren;

tree.Items.AddChild(Node, ''); //Фиктивный элемент(чтобы

//отображался "+", позволяющий

//развернуть элемент)

end;

end;

Если после создания элементов дерева процедура SetExpanded вызывается с параметром isExpanded, равным False (как в листинге 4.27), то для переданного в процедуру элемента дерева создается фиктивный дочерний элемент. Это делается для того, чтобы не зачитывать содержимое каждого не развернутого еще элемента дерева (для папок с большим количество файлов программа будет сильно «тормозить»). А так у каждого еще не развернутого элемента отображается символ +, позволяющий развернуть его в нужный момент. При этом не нужно забывать удалять созданный фиктивный элемент дерева (что и делает SetExpanded с параметром isExpanded, равным True).

Каждый не развернутый еще элемент дерева помечается значением поля Node. Data, равным 0. Каждый элемент, содержимое которого уже прочитано с диска, помечается значением поля Node. Data, равным 1. Для проверки, было ли прочитано содержимое каталога, соответствующего элементу дерева, используется простая функция IsExpanded (листинг 4.29).

Листинг 4.29. Проверка, загружено ли содержимое каталога

function TForm3.IsExpanded(Node: TTreeNode): Boolean;

begin

IsExpanded := Integer(Node.Data) = 1;

end;

Загрузка содержимого каталога и одновременное формирование дочерних элементов в дереве происходят при разворачивании элемента дерева (листинг 4.30).

Листинг 4.30. Загрузка содержимого каталога

procedure TForm3.treeExpanding(Sender: TObject; Node: TTreeNode;

var AllowExpansion: Boolean);

var

strFolder: String;

subfolders: TStrings;

i: Integer;

item: TTreeNode;

begin

if not IsExpanded(Node) then

//Содержимое каталога нужно зачитать

SetExpanded(Node, True)

else

begin

//Список подкаталогов для выделенного каталога

//был составлен ранее

AllowExpansion := True;

Exit;

end;

//Составление списка подкаталогов

strFolder := NodeToFolderPath( Node );

subfolders := TStringList.Create;

if SearchInFolder(strFolder, '*', FILE_ATTRIBUTE_DIRECTORY,

subfolders)

then begin

//Добавим в дерево элементы, соответствующие подкаталогам

for i := 0 to subfolders.Count – 1 do

begin

item := tree.Items.AddChild(Node, subfolders[i]);

item.ImageIndex := 1;

item.SelectedIndex := 2;

SetExpanded(item, False); //Содержимое подкаталога еще

//не прочитано

end;

AllowExpansion := True;

end

else

//В каталоге нет подкаталогов

AllowExpansion := False;

subfolders.Free;

end;

В листинге 4.30 для определения пути каталога, заданного элементом дерева, используется функция NodeToFolderPath. Реализуется она совсем несложно (листинг 4.31).

Листинг 4.31. Определение полного пути элемента дерева

function TForm3.NodeToFolderPath(Node: TTreeNode): String;

var

path: String;

item: TTreeNode;

begin

item := Node;

while item <> nil do

begin

if path <> '' then

path := item.Text + '' + path

else

path := item.Text;

item := item.Parent;

end;

NodeToFolderPath := path;

end;

Приведенный здесь пример построения дерева может пригодиться при решении некоторых задач. Дополнительно же нужно сказать, что на вкладке Samples (Delphi 7) можно найти компоненты, прекрасно подходящие для построения пользовательского интерфейса приложений для просмотра содержимого не только физически существующих дисков: полное дерево каталогов SheLLTreeView (включая корневой элемент Рабочий стол и прочие виртуальные каталоги), список основных элементов системы каталогов (ShellComboBox), а также элемент управления для просмотра содержимого папки (SheLLListView).

4.3. Файлы

В завершение главы рассмотрим три несложных примера работы с файлами: копирование файла (с отображением хода копирования в ProgressBar), определение значков, ассоциированных с файлами, и извлечение значков из ЕХЕ– и DLL-файлов.

Красивое копирование файла

Казалось бы, что особенного в организации копирования большого файла с отображением процесса: читай файл порциями, записывай прочитанные данные в файл назначения, попутно показывая в ProgressBar или где-то еще отношение объема переписанной информации к размеру файла. Однако зачем такие сложности? Ведь у API-функции CopyFi 1е, осуществляющей простое копирование файла, есть расширенный вариант – функция CopyFileEx, в которую встроена поддержка отображения процесса копирования (и не только это). Вот прототип функции CopyFileEx:

function CopyFileEx(lpExistingFileName, lpNewFileName: PChar;

lpProgressRoutine: TFNProgressRoutine; lpData: Pointer;

pbCancel: PBool; dwCopyFlags: DWORD): BOOL; stdcall;

Итак, кроме пути исходного и конечного файлов, а также флагов (последний параметр), функция принимает ряд дополнительных параметров: адрес функции обратного вызова (IpProgressRoutine), указатель на данные, передаваемые в функцию обратного вызова (lpData), а также адрес переменной типа BOOL (pbCancel), при установке значения которой в True копирование прерывается.

Пример использования функции CopyFileEx в программе приведен в листинге 4.32. Здесь подразумевается, что кнопка cmbCopy используется как для запуска, так и для остановки процесса копирования. Также на форме присутствуют следующие элементы управления:

• индикатор pbCopyProgress, диапазон значений которого от 0 до 100;

• текстовое поле txtFrom с именем копируемого файла;

• текстовое поле txtTo с именем файла назначения.

Листинг 4.32. Использование функции CopyFileEx

procedure TForm1.cmbCopyClick(Sender: TObject);

begin

if cmbCopy.Caption = 'Копировать' then

begin

//Запускаем копирование

progress := pbCopyProgress; //Настроен от 0 до 100 %

bCancelCopy := False;

cmbCopy.Caption := 'Отмена';

if CopyFileEx(PAnsiChar(txtFrom.Text), PAnsiChar(txtTo.Text),

Addr(CopyProgressFunc), nil, Addr(bCancelCopy),

COPY_FILE_FAIL_IF_EXISTS) = False

then

MessageBox(Handle, 'Не удается скопировать файл',

'Копирование', MB_ICONEXCLAMATION);

end

else

begin

//Останавливаем процесс копирования

bCancelCopy := True;

cmbCopy.Caption := 'Копировать';

end;

end;

Из листинга 4.32 можно увидеть, что в качестве значения последнего параметра функции CopyFileEx можно передавать константу COPY_FILE_FAIL_IF_EXISTS (функция вернет False, если файл назначения уже существует, и не будет осуществлять копирование).

На самом деле значение параметра dwCopyFlags функции CopyFileEx может быть комбинацией значений COPY_FILE_FAIL_IF_EXISTS И COPY_FILE_RES TARTABLE, то есть представляет собой битовый флаг. Последнее значение используется для того, чтобы в случае прерывания копирование файла можно было возобновить. Функция CopyFileEx в этом случае сохраняет в файле назначения информацию, достаточную для возобновления процесса копирования.

В листинге 4.32 изменяется переменная progress – глобальная переменная-ссылка на TProgressBar, которая используется в функции обратного вызова. Переменная bCancelCopy, адрес которой передается в функцию CopyFileEx, также объявлена глобальной (в пределах модуля).

Теперь, наконец, рассмотрим функцию обратного вызова, осуществляющую в нашем случае отображение хода копирования на индикаторе (листинг 4.33).

Листинг 4.33. Функция, показывающая ход копирования файла

function CopyProgressFunc( TotalFileSize: Int64;

TotalBytesTransferred: Int64;

StreamSize: Int64;

StreamBytesTransferred: Int64;

dwStreamNumber: DWORD;

dwCallbackReason: DWORD;

hSourceFile: THandle;

hDestinationFile: THandle;

lpData: Pointer): DWORD; stdcall;

begin

progress.Position := 100 * TotalBytesTransferred div

TotalFileSize;

Application.ProcessMessages; //Чтобы не «зависал»

//интерфейс приложения

CopyProgressFunc := PROGRESS_CONTINUE;

end;

Пусть вас не смущает большое количество параметров функцииСоруРгодгеззЕипс. Применять их все далеко не обязательно (но они должны быть объявлены), хотя ничего сложного здесь нет. В листинге 4.33 использование параметров реализовано наиболее простым (на наш взгляд) и очевидным образом: значения параметров TotalBytesTransferred и TotalFileSize применяются для определения доли скопированной информации.

В листинге 4.33 вызов метода ProcessMessages объекта Application используется потому, что функция CopyFileEx возвращает управление программе только после завершения (или прерывания) копирования. Иначе пришлось бы создавать для копирования отдельный поток, усложняя листинг и отвлекая вас от главной цели этого примера.

Теперь несколько слов о возвращаемых функцией CopyProgressFunc значениях (в нашем примере используется только одно из четырех доступных значений). Список целочисленных констант, значения которых может возвращать функция CopyProgressFunc, таков:

• PROGRESS_CONTINUE – продолжать процесс копирования;

• PROGRESS_CANCEL – отмена копирования;

• PROGRESS_STOP – остановка копирования (можно возобновить);

• PROGRESS_QUIET – при возврате этого значения система перестает вызывать функцию CopyProgressFunc.

Внешний вид формы при копировании большого файла приводится на рис. 4.9.

Рис. 4.9. Копирование большого файла

Только не нужно забывать останавливать копирование при закрытии приложения или в прочих экстренных ситуациях. Так, если не предусмотреть обработку события CloseQuery для формы (рис. 4.9), то закрыть ее в ходе копирования обычным способом не удастся. Зато после завершения копирования (или при нажатии кнопки Отмена) форма тут же исчезнет. Странное поведение, не правда ли? Вариант более-менее адекватной реакции на закрытие формы приводится в листинге 4.34.

Листинг 4.34. Остановка копирования при закрытии формы

procedure TForm1.FormCloseQuery(Sender: TObject; var CanClose:

Boolean);

begin

//Останавливаем процесс копирования

bCancelCopy := True;

end;

Как вариант, можно запретить закрытие формы (установить CanClose в False), не останавливая копирования.

В том случае, когда копируется несколько файлов, можно ввести дополнительный элемент управления Progress Ваг, отображающий ход всего процесса копирования. Только при этом придется заранее определить общий размер копируемых файлов.

Определение значков, ассоциированных с файлами

Рассмотрим еще один интересный пример, позволяющий получить значок файла, показываемый, например, в Проводнике Windows. Приведенная в листинге4.35 функция принимает в качестве параметра путь файла и флаг, определяющий, какой нужен значок – малый или большой. Она возвращает дескриптор экземпляра значка, ассоциированного с файлом. Реализация функции находится в модуле ShellFunctions, расположенном на диске, прилагаемом к книге, в папке с названием раздела.

Листинг 4.35. Определение значка файла

function GetFileIcon(filename: String; small: Boolean = False ): HICON;

var

info: SHFILEINFO;

flags: Cardinal;

begin

flags := SHGFI_ICON;

if small then

//Получение малого значка

flags := flags or SHGFI_SMALLICON

else

//Получение большого значка

flags := flags or SHGFI_LARGEICON;

ZeroMemory(Addr(info), SizeOf(info));

//Получение значка

SHGetFileInfo(PAnsiChar(filename), 0, info, SizeOf(info), flags);

GetFileIcon := info.hIcon;

end;

Используемая в листинге 4.35 API-функция SHGetFilelnfo объявлена в модуле ShellApi. Там же объявлена структура SHFILEINFO.

В листинге 4.36 приведен пример использования функции GetFilelcon: здесь полученные значки сохраняются в элементах управления Image (по одному для большого и малого значков).

Листинг 4.36. Пример получения значка заданного файла (или папки)

procedure TForm1.cmbLoadIconClick(Sender: TObject);

begin

//Определение большого и малого значков файла

imgLarge.Picture.Icon.Handle := GetFileIcon(txtFile.Text);

imgSmall.Picture.Icon.Handle := GetFileIcon(txtFile.Text, True);

end;

Пример определения значка файла приводится на рис. 4.10.

Рис. 4.10. Определение значка, ассоциированного с файлом

На самом деле функция из листинга 4.35 может определять значки не только файлов, но и каталогов, дисков и виртуальных папок (Мой компьютер, Рабочий стол, Панель управления и т. д.). Правда, в последнем случае используемая в листинге API-функция SHGetFilelnfo требует первый параметр специального вида (не строка). Частично работа с таким представлением путей рассмотрена в подразд. «Окно выбора папки» разд. 2.4.

В заключение скажем несколько слов о прочих полезных возможностях API-функции SHGetFileInf о. Недаром она называется не SHGetFilelcon или что-то подобное: она позволяет получить гораздо больше информации, нежели просто значок файла. Эта информация зависит от набора флагов, передаваемых в функцию в качестве последнего параметра. Но сначала рассмотрим, из каких полей состоит структура SHFILEINFO, потому что результат (за редким исключением) помещается именно в ее поля:

• hIcon (типа HICON) – дескриптор значка заданного путем объекта (первый параметр функции SHGetFilelnfo);

• iIcon (типа Integer) – номер значка в системном компоненте ImageList;

• dwAttributes (типа DWORD) – атрибуты заданного путем объекта;

• szDisplayName (типа array [0. МАХ_РАТН-1] of AnsiChar) – буфер для имени заданного объекта (например, сочетание имени и метки диска, отображаемое в Проводнике Windows);

• szTypeName (типа array [0..79] of AnsiChar) – буфер для названия типа файла (например, Документ Microsoft Word).

На полях dwAttributes и ilcon подробно останавливаться не будем, зато рассмотрим, как заставить функцию SHGetFilelnfo заполнить остальные поля структуры (их проще всего использовать в Delphi). Вот используемые для этого флаги (имена целочисленных констант):

• SHGFIICON – поле hlcon заполняется дескриптором значка, ассоциированного с объектом; если при использовании дескриптор не сохраняется в каком-либо контейнере или прочем объекте, автоматически удаляющем ненужные значки (как в листинге 4.36), то после использования значок нужно удалить вручную (API-функция Destroylcon);

• SHGFI_LARGEICON, SHGFI_SMALLICON – ОНИ применяются В комбинации с SHGFIICON для получения большого или малого значков соответственно; использование флагов вместе не имеет смысла (будет получен малый значок);

• SHGFI_DISPLAYNAME—при наличии этого флага поле szDisplayName будет содержать дружественное пользователю имя объекта (например, System (С:));

• SHGFI_EXETYPE – при наличии этого атрибута полез zTypeName будет заполнено текстовым описанием типа файла.

Значения в приведенном списке можно, если не сказано иное, комбинировать при помощи операции битового ИЛИ (or).

Извлечение значков из ЕХЕ– и DLL-файлов

Наверняка вы знаете, что исполняемый файл, помимо кода программы, данных и прочей системной информации, может содержать также ресурсы. Так, из секции ресурсов берутся значки для ЕХЕ-файлов. Также в ЕХЕ– или DLL-файлах помещаются значки, используемые для ассоциированных с приложениями документов. Итак, в завершение главы рассмотрим еще один графический пример: создадим программу, способную извлекать упомянутые значки из DLL– или ЕХЕ-файлов (работает также и для ICO-файлов).

Пусть мы имеем путь файла, а также два списка (TImageList) для больших и малых значков соответственно. Тогда процедура, заполняющая списки значками, извлеченными из файла, может выглядеть следующим образом (листинг 4.37).

Листинг 4.37. Составление списков значков

procedure LoadIcons(filename: String; lgImages,

smImages: TImageList);

var

icon: TIcon;

smIconHandle, lgIconHandle: HICON;

i: Integer;

begin

//Загрузка каждого значка (неоптимально, но просто)

i := 0;

while Integer(

ExtractIconEx(PAnsiChar(filename), i, lgIconHandle,

smIconHandle, 1)

) > 0 do

begin

Inc(i);

//Большой значок

icon := TIcon.Create;

icon.Handle := lgIconHandle;

lgImages.AddIcon(icon);

//Малый значок

icon := TIcon.Create;

icon.Handle := smIconHandle;

smImages.AddIcon(icon);

end;

end;

В листинге 4.37 для извлечения значков из файла используется очередная полезная функция модуля ShellApi – Extract IconEx. Прототип функции таков:

function ExtractIconEx(lpszFile: PChar; nIconIndex: Integer;

var phiconLarge, phiconSmall: HICON;

nIcons: UINT): UINT;

Функция ExtractlconEx принимает следующие параметры:

• lpszFile – путь файла, из которого извлекаются значки;

• nIconlndex – номер первого извлекаемого значка; нумерация начинается с нуля (если номер равен -1 и параметры piconLarge и piconSmall нулевые, то функция возвращает количество значков в файле);

• piconLarge, piconSmall – ссылки на переменные типа HI CON (либо на первые элементы массива array. of HICON) для помещения в них дескрипторов больших и малых значков соответственно;

• nIcons – количество извлекаемых значков (по сути, может быть количество элементов в передаваемых в функцию массивах: лишние элементы не будут заполнены).

Функция возвращает количество значков, извлеченных из файла, или количество значков в файле при соответствующем значении параметра nlconlndex.

В листинге 4.36 используется не совсем оптимальный способ извлечения значков из файла – по одному. Однако он подойдет для большинства случаев. Другой (но не единственный) вариант – использование массива. Тогда функцииЕх^асИсопЕх передаются первые элементы массивов для дескрипторов значков (функции нужен адрес начала массива), а в качестве последнего параметра – количество элементов в массиве. Таким образом, если количество значков в файле превзойдет количество элементов в массиве, то вызов функции ExtractlconEx можно будет повторить, передав в качестве параметра nlconlndex значение, возвращенное функцией ExtractlconEx, умноженное на номер вызова функции (начиная с нуля).

Можно также использовать динамический массив, предварительно установив его размер, вызвав функцию ExtractlconEx с параметром nlconlndex, равным -1. Установить значения параметров piconLarge, piconSmall в ноль (не меняя объявления функции) можно, объявив указатель на HICON (AHICON), присвоив ему значение nil и передав его в качестве упомянутых параметров в функцию.

На рис. 4.11 приводится внешний вид формы приложения после извлечения значков из файла Explorer. ехе.

Обработчик нажатия кнопки Загрузить значки представленной н а рис. 4.11 формы приводится в листинге 4.38.

Рис. 4.11. Пример извлеченных из ЕХЕ-файла значков

Листинг 4.38. Составление списков значков и их отображение

procedure TForm1.cmbLoadIconClick(Sender: TObject);

var

i: Integer;

item: TListItem;

begin

lvwIconsLg.Clear;

lvwIconsSm.Clear;

//Загрузка значков в ImageList

ImageListLg.Clear;

ImageListSm.Clear;

LoadIcons(txtFile.Text, ImageListLg, ImageListSm);

//Создание элементов в ListView с большими и малыми значками

for i := 0 to ImageListLg.Count – 1 do

begin

item := lvwIconsLg.Items.Add();

item.Caption := 'Icon' + IntToStr(i+1);

item.ImageIndex := i;

item := lvwIconsSm.Items.Add();

item.Caption := 'Icon' + IntToStr(i+1);

item.ImageIndex := i;

end;

end;

Подразумевается, что имена элементов управления ListView: для отображения больших значков – lvwIconLg и для отображения малых lvwIconSm. На форме также расположены два элемента управления ImageList: ImageListLg для хранения больших и ImageListSm для хранения малых значков.

С помощью окна Object Inspector список ImageListLg назначен в качестве источника больших изображений (свойство Largelmages) для lvwIconLg. Соответственно, список ImageListSm назначен в качестве источника малых изображений (свойство Smalllmages) для lvwIconSm.


    Ваша оценка произведения:

Популярные книги за неделю