![]() |
![]() ![]() |
![]() |
IUnknown |
![]()
Сообщение
#1
|
![]() a.k.a. volvo877 ![]() ![]() ![]() ![]() ![]() Группа: Пользователи Сообщений: 1 013 Пол: Мужской Репутация: ![]() ![]() ![]() |
А не внести ли нам разнообразие в раздел?
![]() Недавно на форуме поднималась подобная тема (Вот тут), помню, было еще несколько тем с подобными вопросами (что-то про написание железнодорожной или авиа-справочной, найти что-то не получилось). Обычно подобная задача возникает при написании баз данных. Когда ищутся записи, удовлетворяющие какому-либо критерию, и таких записей находится больше 25, на экран все одновременно они уже не помещаются. Поэтому было бы неплохо научиться делать вывод результатов таким образом, чтоб его можно было прокручивать в любую сторону, и выбирать любую запись из представленных на экране. Проще всего для этой цели использовать реализацию меню. Я взял свой старый модуль, который использовался не в одном десятке программ, и чуть-чуть его подкорректировал. Почему понадобилось корректировать - попробую объяснить. Дело в том, что изначально у меня заголовок функции Menu был вот таким: function Menu(const s : array of string; Ystart, Yfinish : integer) : integer; , то есть в нее передавался массив строк, с которым функция работала внутри так, как ей было нужно: выводила на экран разные строки в нужных местах. Это не очень подходит для описываемого случая, потому что тогда перед вызовом меню надо будет запускать поиск, где-то сохранять его результаты, передавать их в Menu, а потом, после того, как Menu отработает - еще и удалять результаты поиска, чтобы не было утечек памяти, и чтобы память не расходовалась зря. Я решил пойти другим путем: передать в Menu не сам массив строк, а функцию, с помощью которой можно будет получить результаты поиска: type А уже внутри модуля MenuUnit происходит именно то, что я описал выше: там создается временный массив, в котором хранятся строки, а после завершения работы меню этот массив автоматически удаляется. Собственно, вот этот модуль MenuUnit: ![]() Посмотрим, что он умеет делать. Для примера - возьмем ту самую БД, которая разрабатывалась по первой ссылке. Там этот модуль может использоваться в каждой из процедур WriteAll, Edit, DeleteRecord и Search. К примеру, WriteAll должна вывести абсолютно все записи, присутствующие в БД. Для этого можно написать такую функцию: Var Теперь возьмем пример посложнее - Edit. Здесь кроме того, что выводятся все записи, так добавляется еще один пункт: "Отмена редактирования" (в случае, если пользователь не хочет больше редактировать никакую запись, он выбирает этот пункт). Этот вариант тоже реализуется очень просто с помощью вот такой функции: Function IterateEdit (i : Integer; Var Optional; Ну, и теперь кое-что посложнее: поиск по определенному полю в БД. Поскольку все поля у топикстартера имеют тип String, я немного подкорректировал описание структуры Database: Type , теперь к полю SecondNumber можно обратиться и напрямую (Massive[ i ].SecondNumber), и через Massive[ i ].sArray[1]; то же самое касается и всех остальных полей. Так вот, теперь о поиске. Для реализации поиска недостаточно просто перебрать все записи, нужно еще сравнивать определенное поле каждой записи с некоторым образцом. Для этого я и добавил в TDataFunc параметр Optional. И именно по этой причине он - бестиповый, то есть, туда, в зависимости от необходимости, можно передать либо целочисленное значение, либо какую-нибудь структуру, либо строку, все, что угодно, поэтому можно реализовывать запросы практически любой сложности. Я покажу самый простой: допустим, надо найти все записи, в определенном поле которых присутствует какое-либо значение. Делаем так: var Функция IterateFields может потребовать дополнительных комментариев: здесь используется локальная типизированная константа, которая сохраняет значение между вызовами функций, поэтому при втором вызове функция продолжит просмотр там, где она остановилась в прошлый раз. Именно по этой причине понадобилось обнулять Count при i = 1 (если этого не сделать то первый запрос отработает нормально, а вот при втором уже будет вылет за границы массива) Через Optional передается номер поля, в котором будем искать строку. Можно было передать и саму искомую строку через этот же параметр (например, объединив номер поля и строку в структуру), но это уже будет задача для самостоятельного решения... ![]() Подменю, как видите, тоже создается элементарно, через передачу CreateSubmenu в функцию Menu. Надеюсь, этот модуль, и приведенные примеры его использования, будут для кого-то полезны. Замечания/предложения по усовершенствованию/информация о багах - приветствуются. |
IUnknown |
![]()
Сообщение
#2
|
![]() a.k.a. volvo877 ![]() ![]() ![]() ![]() ![]() Группа: Пользователи Сообщений: 1 013 Пол: Мужской Репутация: ![]() ![]() ![]() |
С учетом новых возможностей FPC 2.6.0 хочу дополнить еще буквально одним предложением. Теперь необязательно выносить
Procedure WriteAll;. Все, что для этого понадобится - добавить в начало программы (и в начало модуля MenuUnit) директиву {$modeswitch NestedProcVars} , а также подправить описание типа TDataFunc: type , тогда в качестве аргумента процедурного типа можно будет использовать как глобальную, так и локальную функцию... Сообщение отредактировано: IUnknown - 15.01.2012 23:36 |
![]() ![]() |
![]() |
Текстовая версия | 12.09.2024 13:07 |