В первых двух строках кода создаются списки маленьких и больших иконок. Свойства Largelmages и Smalllmages имеют тип TimageList, НО сразу ПОСЛе создания компонента они равны nil. Поэтому они создаются, присваивая результат вызова TimageList. create (self). Когда они проинициализированы, необходимая память будет выделена.
Раньше мы подключали картинки визуально, добавляя на форму компонент TimageList, потому что брали их с диска. В этом примере изображения нужно получить из системы Windows и поэтому ставить визуальный компонент нет смысла, в редакторе форм мы картинок не увидим. Они будут существовать в списке только во время выполнения программы.
Можно поступить немного другим способом — поставить на форму два компонента TimageList и просто указать их в соответствующих свойствах. В этом случае не пришлось бы ничего инициализировать, потому что компоненты, стоящие на форме, инициализируются автоматически. Но здесь так не делается с целью показать, что объектное свойство можно сразу заставить работать без использования дополнительных компонентов.
В дальнейшем мы запрашиваем у системы список больших иконок. Для этого используется функция SHGetFileinfo, которая возвращает информацию о файле, папке или диске. Первый параметр — путь к файлу. Второй — атрибуты. Третий — указатель на tshfileinfo. Четвертый— размер tshfileinfo. Последний параметр — это флаги, указывающие на тип информации, запрашиваемой у системы.
Теперь о том, как выглядит функция в нашем примере. Первые два параметра пустые, это означает, что нужны глобальные данные, а не информация о конкретном файле. Если указать здесь реальные значения файла, то получим информацию о нем, а если указать нулевые значения, то будет получена системная информация.
В качестве флагов указывается shgfi_sysiconindex и shgfi_largeicon. shgfi_jsysiconindex означает, что нужно вернуть указатель на системный список иконок (imageList). Второй флаг говорит, что нужны большие иконки.
При втором вызове этой функции (чуть ниже в коде) мы запрашиваем маленькие иконки (shgfi__smallicon). Функция возвращает указатель на соответствующий системе SysImageList, КОТОрЫЙ МЫ впоследствии присваиваем ListViewl.Largelmages.Handle. После этого присваивания ListViewl.Largelmages содержит все системные иконки размера 32x32. Почему? Потому что свойство Handle— это указатель на выделенную для картинок память. Этот указатель мы изменили на тот, который получили из системы, и теперь наш список картинок указывает на системный.
Системный список иконок (imageList) содержит все иконки, установленные в системе и ассоциированные с разными типами файлов. Эти иконки вы можете видеть в проводнике (Windows Explorer) у файлов doc, txt, ini, zip и др.
На следующем этапе разработки программы создадим обработчик события onshow. В нем вызовем процедуру AddFile, которая считывает все файлы из текущей папки:
AddFile('С:/*.*',faAnyFile);
Процедуру AddFile нужно объявить в разделе private нашей формы Forml следующим образом:
Объявите эту процедуру так же, а потом нажмите комбинацию клавиш <Ctrl>+<Shift>+<C>, и Delphi создаст заготовку для будущей процедуры. В эту заготовку напишите содержимое листинга:
Процедура получилась достаточно большая и надо подробно'ее рассмотреть. Делать это будем по частям программного кода, чтобы было легче воспринимать объяснения.
После имени процедуры идет объявление локальных переменных. Это все понятно, и мы не раз уже такое делали. Но после объявления переменных вместо начала процедуры begin стоит объявление другой локальной функции:
Конечно же, эту функцию можно написать как полноценную, но в данном случае мы закрепляем на практике локальные процедуры. Этот код будет нужен только внутри функции AddFile и больше нигде, а значит, нет смысла выделять отдельную функцию.
Если одна процедура или функция (внутренняя) объявлена внутри другой (внешней), то внутренняя процедура может быть вызвана только из внешней. Весь остальной код программы не будет знать о существовании где-то внутренней процедуры.
После объявления и описания внутренней процедуры идет начало внешней процедуры. Вот тут начинается самое интересное. В самом начале вызываются два метода компонента
Первый метод BeginUpdate говорит о том, что начинается обновление элементов списка. После этого вызова никакие изменения, вносимые в элементы, не будут отражаться на экране, пока не будет вызван Endupdate.
Когда вы хотите произвести незначительное изменение, то не надо вызывать эти методы, но когда предполагается, что элементы списка будут изменяться очень сильно, то лучше все изменения заключить между вызовами BeginUpdate и Endupdate. Это связано с тем, что когда вы вносите хоть какое-то изменение, оно сразу отображается на экране. Логично? А что если вам нужно удалить все элементы и потом в цикле добавить в список 1000 новых элементов. В этом случае после удаления и каждого добавления нового элемента будет происходить прорисовка компонента. Вот тут и возникает вопрос: "Зачем после каждого добавления рисовать?".
В этом случае намного эффективнее будет добавить все элементы, а только потом их прорисовать, причем все сразу. Вот именно для этого и существуют своеобразные операторные скобки BeginUpdate И Endupdate:
После вызова BeginUpdate производится очистка текущего списка элементов С ПОМОЩЬЮ вызова ListViewl. Items .Clear.
Далее идет цикл поиска файлов, с которым мы уже немного познакомились в предыдущих статьях. Вспомним этот процесс.
FindFirst — открывает поиск. В качестве первого параметра выступает маска поиска. Если вы укажете конкретный файл, то система найдет его. Второй параметр— атрибуты включаемых в поиск файлов. Здесь мы используем faAnyFile, чтобы искать любые файлы. Последний параметр — это структура, в которой нам вернется информация о поиске, а именно: имя найденного файла, размер, время создания и т. д.
После вызова FindFirst мы проверяем корректность найденного файла. Если все в норме, то запускается цикл Repeat - until. Этот цикл выполняет операторы, расположенные между repeat и until, пока условие, расположенное после слова until, является верным (имеет значение true). Как только условие нарушается, цикл прерывается.
Напоминаю, что функция поиска может возвращать в качестве найденного имени в структуре SearchRec (параметр Name) точку или две точки. Если встречаются такие имена, то их просто отбрасывают.
Далее идет вызов функции SlashSep:
FileName := SlashSep(Editl.Text, SearchRec.Name);
Эта функция и FileTimeToDateTimestr будут написаны позже и объявлены в разделе var после объявления объекта:
Здесь функция SlashSep объявлена не внутри объекта, значит, она никому не принадлежит.
Вообще-то самостоятельные функции не обязательно где-либо объявлять. Вы можете без проблем просто реализовать ее и нигде не описывать. Однако здесь надо учитывать, что если где-то необходимо использовать эту функцию, то реализация обязательно должна быть раньше использования. Пример правильного использования самостоятельной процедуры или функции показан в листинге:
В этом примере объявлена самостоятельная процедура Examp и метод объекта Forml— Examp2. Из метода Ехатр2 мы вызываем самостоятельную процедуру ехатр. Этот код правильный, потому что процедура сначала реализуется, а потом уже используется.
А теперь посмотрите на неправильный код, который показан в листинге:
В этом примере происходит попытка вызвать процедуру, которая реализована после вызова, поэтому компилятор выдаст ошибку. Чтобы этого избежать, самостоятельные процедуры можно описывать в разделе var, как это показано в листинге:
А теперь давайте посмотрим, как же выглядит функция siashsep:
Функцию siashsep уже один раз использовали, но вспомним, что здесь происходит. Эта функция получает два параметра — путь к файлу и имя файла, которые она должна соединить в одну строку, чтобы получился полный путь к файлу. Но сначала мы должны проверить, заканчивается ли путь (первый полученный параметр — Path) знаком V. Переменная Path — это строка типа string, а значит, мы можем к ней обращаться как к массиву символов. Чтобы получить доступ к первому символу, мы должны написать Path.
Нам нужно проверить последний символ, поэтому В квадратных скобках используется Length (Path). Функция Length возвращает длину переданной ей строковой переменной, а это значит, что в квадратных скобках мы указываем длину строки, т. е. последний символ.
Если бы нужен был предпоследний символ, то мы бы написали Path [Length (Path) -1]. В этом случае из длины строки вычитается единица и в результате получается индекс предпоследнего символа.
Если последний символ не равен \\ добавляем сначала его, а потом имя файла. Если этот символ в строке имеет место, то нужно только добавить имя файла и записать в переменную Result, чтобы функция вернула полный путь к файлу.
С этой функцией покончено, и пора вернуться к нашему перечислению файлов. Следующим идет вызов системной функции SHGetFileinfo. Она возвращает информацию о файле. Не будем на ней сейчас останавливаться. В принципе, она простая и вы, наверное, сможете ее понять по коду, а если нет, то к ней мы вернемся немного позже.
Сейчас нас больше интересует работа с компонентом Listview. Следующий код добавляет в список новый элемент: Listviewi.items.Add. Это делается внутри конструкции with, значит, все последующие действия между begin и end будут выполняться с новым элементом. А именно — изменяется заголовок нового элемента:
У каждого элемента есть свойство Subitems, которое хранит дополнительную информацию. Когда компонент находится в режиме отображения иконок, то дополнительная информация не видна. Но если выбрать в свойстве viewstyle значение vsReport, то компонент будет выглядеть в виде таблицы, где каждый столбец отображает дополнительную информацию, как это показано на рис. 11.38.
Чтобы добавить дополнительные колонки к новому элементу, надо выполнить оператор:
Subitems.Add('значение');.
Чтобы колонки отображались, нужно в свойстве columns указать имена колонок. Если имена колонок не указаны, то ничего отображаться не будет. И не забывайте, что при описании колонок первая — это заголовок элементов, остальные — это дополнительные параметры в порядке их добавления с помощью subitems.Add.
И последнее, ЧТО надо еще рассмотреть,— ЭТО функция FileTimeToDateTimeStr, которая переводит время/дату из системного формата в строку. Ее код можно увидеть в листинге:
В самом начале мы приводим дату файла в универсальное глобальное время (по Гринвичу). Для ЭТОГО используется функция FileTimeToLocalFileTime, у КОТОрОЙ два параметра:
- переменная типа TFileTime, которую нужно перевести;
- переменная, в которую будет записан результат.
Вторым этапом мы переводим универсальное глобальное время в системное время вашего компьютера. Для этого нужна функция FileTimeToSystemTime, у которой также два параметра:
- универсальное глобальное время;
- переменная результата.
Теперь у нас дата хранится в переменной sysFTime и имеет тип TSystemTime. Переменная SysFTime имеет тип структуры и следующие свойства:
- wYear — год;
- wMonth — месяц;
- wDay — день;
- wHour — часы;
- wMinute — минуты;
- wsecond — секунды;
- wMilli seconds — миллисекунды.
Все это числа, и нам надо превратить их в строку, но так, чтобы строка даты выглядела в соответствии с локальными настройками системы. Если мы пишем программу, которая будет выполняться в однотипной системе (например, в русской версии Windows с российским представлением даты и времени), то вы можете воспользоваться функцией IntToStr, чтобы превратить все поля в строки и отформатировать их по своему усмотрению.
Однако сейчас поступим более универсально. Сначала данные превратим в формат DateTime. Для этого есть функции EncodeDate и EncodeTime. Эти функции создают переменные типа TDate и TTime на основе переданных им числовых значений.
Получив эти переменные, мы объединяем их в более общий формат DateTime простым сложением и переводим в строку с помощью функции DateTimeToStr. Эта функция переводит дату в строку в соответствии с локальными настройками региона в ОС Windows. Вы же можете воспользоваться и функцией FormatDate, которая может создать строку даты в любом виде.