Главная » Delphi » Диалог с установкой нескольких параметров и сохранение установок через INI-файлы

2

Пусть мы хотим в одном диалоге устанавливать сразу несколько вещей — в нашем приложении, кроме интервала таймера, можно устанавливать цвет фона "экрана" (т. е. компонента paneii). Кроме того, будем все это запоминать в качестве пользовательских установок. Как простейшим способом сохранять установки пользователя с использованием INl-файла, мы уже знаем из главы 7, но на этот раз для разнообразия поступим по правилам: для работы с INI-файлом используем класс TiniFile. (Между прочим, DSK-файл проекта Delphi, который я предлагаю при переносе править вручную, именно такой INl-файл и есть.) Заодно провернем следующую операцию— введем пункт выбора папки в дополнение к открытию файла (указывать конкретный файл или папку целиком— концептуально разные действия), причем опя’1ь же для разнообразия используем компоненты Delphi. Но мало того, покажем язык в сторону Редмонда и будем в том же lNI-файле запоминать еще и последнюю просмотренную папку, с тем, чтобы при загрузке она устанавливалась автоматически.

Перенесем созданный проект SlideShow из папки Glava9\2 в новую папку (Glava9\3) и изменим версию на 1.30. Начнем с задания папки. В секции объявления переменных добавляем переменную-строку stpath и сразу инициализируем ее значением ‘С л1:

stpath: string = ‘C:V;

Теперь добавим в процедуру Forml.create такую строку:

ChDir(stpath); {устанавливаем текущую папку)

Если сделать диалог выбора папки на невидимой панели, как установку интервала таймера, нам придется вручную отключать доступ к меню и кнопке— иначе пользователь может предпринять еще какие-то действия, не закрывая диалога, что в данном случае неправильно. Поэтому нам придется расположить диалог на отдельной форме с вызовом ее в модальном режиме — хотя это и проще, но у автора имеется хроническая нелюбовь к модальным формам, которые выскакивают, как чертик из табакерки, и обязывают пользователя что-то предпринять, о чем он думать вовсе не хочет. Диалог на панели куда дружелюбнее, но запутаться, когда и что именно включать и отключать, очень просто, поэтому способ с формой надежнее и не столь хлопотен.

Присоединим к проекту новую форму (File | New | Form), она получит наименование Form3, а модуль для нее назовем papka.pas. У нее установим свойства BorderStyle В bsDialog, Position В poDesktopCenter, a Caption В Выбор папки. Что же касается довольно многочисленных компонентов для задания папки, о которых мы говорили в предыдущей главе, то наименее проблематичным, с точки зрения автора, является использование сочетания shellcomboBox с закладки Samples и старого DirectoryListBox с закладки Win3.1 (в новом аналогичном компоненте Directoryoutiine есть досадные "фичи", которые приходится преодолевать шаманскими способами, к тому же достаточно лишних свойств, которые еще надо устанавливать). Вместо She 1 lComboBox логичнее было бы использовать также старый DriveComboBox, но он имеет настолько неэстетичный вид, что лучше потерпеть присутствие явно лишних для нашей цели пунктов вроде Панели управления в раскрывающемся меню shellcomboBox. Для выхода из диалога в данных компонентах не предусмотрено штатных методов (по щелчку мыши это делать нельзя, т. к. есть неоднозначность в использовании одинарных и двойных щелчков — например, DirectoryListBox по праву рождения использует только двойной щелчок для перехода на уровень ниже, в то время как в Windows 95 и выше это может устанавливаться системно). По такой причине мы внизу присовокупим кнопку Ok, роль кнопки Отмена будет играть единственная оставшаяся в соответствии со стилем bsDialog системная кнопка-крестик (рис. 9.3).

Рис. 9.3. Форма диалога для выбора папки проекта SlideShow (вид в работвющей программе)

Свойство Root для sheiiComboBoxi установим в rfMyComputer. После слова implementation в модуле papka.pas, чтобы получить доступ к глобальным неременным основного модуля, допишем строку uses slide; и создадим такой обработчик события onCreate:

procedure TForm3.FormCreate(Sender: TObject); begin

{для первого раза}

DirectoryListBoxl.Directory:=stpath; {текущая директория) SheiiComboBoxi.Path:=stpath[l]+stpath[2]+’\'; {текущий диск) end;

Добавим в меню Файл на форме Forml пункт OpenFolder (Перейти к папке, горячая клавиша для него пусть будет <Ctrl>+<Alt>+<0>) и создадим следующий обработчик события щелчка на этом пункте:

procedure TForml.OpenFolderClick(Sender: TObject); begin {открыть папку}

Form3.ShowModal; end;

И, наконец, в модуле papka.pas создадим следующие обработчики по щелчку на SheliComboBox и на кнопке Buttons (Ok):

procedure TForm3.ShellComboBoxlClick(Sender: TObject);

begin {изменили диск)

try

DirectoryListBoxl.Directory:=ShellComboBoxl.Path; except

Application.MessageBox(‘Диск недоступен’,’Ошибка’,mb_OK); SheiiComboBoxi.Path:=stpathf1]+stpath[2]+’\'; {текущий диск) end; end;

prooedure TForm3.Button3Click(Sender: TObject); begin {Ок для выбора папки)

stpath:=DirectoryListBoxl.GetItemPathfDirectoryListBoxl.Itemlndex);

ChDir(stpath); {устанавливаем текущую папку)

DirectoryListBoxl.Directory:=stpath; {на следующее открытие)

Fo rm3.Close;

end;

Для диалога открытия файла openDia]ogl ничего специально устанавливал» не надо — он будет открываться на текущей папке (установленной у нас через процедуру ChDir).

Теперь займемся остальными установками. Перенесем пункт inti (Интервал) из меню Демонстрация в самый низ меню Файл (это можно сделать, просто перетащив его мышью в окне установок MainMenui), отделим его от двух имеющихся пунктов промежутком (т. е. пунктом с заголовком "-") и назовем по-иному: seti. В компонент Labell на панели установок введем новый более вразумительный текст, который для компактности запишем в две строки и потому будем его устанавливать динамически, вставив в процедуру по событию onCreate формы Formi такую строку:

Labell.Caption:=’Установить интервал для’+#13+’показа слайдов, с';

Саму панельку растянем по вертикали, перенесем кнопку Ok вниз, установим компонент ColorBox И справа добавим Label2 С заголовком Установить цвет рамки. Для свойства Style компонента ColorBoxl установим В True пункт cbstandartCoioc, остальные пункты — в False. Как это должно выглядеть в программе, показано на рис. 9.4.

Рис. 9.4. Панель установок для SiideShow (вид в работающей программе)

Заметки на полях

Компонент ColorBox представляат собой типичный пример того, насколько формально можно подойти к разработке компонентов. Этот компонент используется в самой Delphi, в пункте выбора цветов для свойства Color любого компонента. Отличается он, однако, тем, что произвольно формировать список цветов вам не позволят, в крайнем случае можно использовать не очень удобный механизм задания цвета для пункта, определяемого флагом с 1 IncludeDeГаи 11 в свойстве Style (флаг clSystemColors также должен быть установлен), который сам теряется во множестве других вариантов, а, например, только среди трех-четырех или даже шестнадцати (основных) цветов его включить нельзя, не говоря уж о том, чтобы поставить его. например, в нвчало ипи конец списка. Логика примерно та же, как если бы мы в своих установках клавиш для переключения раскладки (см. главу 7) распахивали бы полный список асех доступных клавиш, в то время как польэоаателю нужно всего-то четы- ре-пять реально удобных вариантоа, остальное он может и вручную расстараться ввести при необходимости. Напьзя здесь и поставить нормальные русские названия цветов. Для того чтобы сдепать аккуратный компонент для удобного на практике выбора цветов, следует самому формировать список с картинками, либо на основе стандартного ComboBox (там можно, не выводя квадратиков, нвпример, просто покрасить текст с названием цвета или фон ячейки), либо сделать свой ColorBox на основе ConboBoxEx (вкладка Win32). Но наша задача сайчас нв в том, чтобы демонстрировать работу со списками, поэтому мы ограничились 16 основными цветами, которых для фона картинки на практике более чем достаточно.

Создадим обработчик события ColorBoxldick и вставим в него пока единственную строку:

Panell.Color:=ColorBoxl.Selected;

Сделаем еще одно усовершенствование в диалоге-панельке. Чтобы работа с клавиатуры действительно, как и положено, ускоряла процесс, удобно действовать так: пусть при первом запуске фокус ввода устанавливается в Editl, затем по нажатию <Enter> переносится в ColorBox (которым можно управлять стрелками вниз-вверх), потом по следующему нажатию <Enter> переносится на кнопку Ok для выхода из диалога. Даже в нашем случае всего трех позиций, когда, казалось бы, можно обойтись и клавишей <ТаЬ>, это намного удобнее, а в случае наличия большого количества однотипных полей- редакторов переключение именно через <Enter> принципиально облегчает жизнь. Для этого мы отменим ввод значения интервала через нажатие <Enter> на редакторе Editl (здесь это уже неактуально), и заменим текст в обработчике события onKeyDown для него на следующий:

procedure TForml.EditlKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);

begin

{нажатие на Enter после очередного ввода) if key=vk_Return then

FindNextControl(Sender as TWinControl, true, true, false).SetFocus;

and;

Объявим для обработчика события onKeyDown компонента coiorBoxi ту же самую процедуру EditlKeyDown, как мы это делали раньше, и позаботимся о том, чтобы свойства Taborder всех трех компонентов шли по порядку (у Editl должно стоять TabOrder=o). Теперь все будет работать гак, как мы задумали.

Проверим наши усовершенствования (при выборе цвета мышью или клавишами-стрелками в ColorBox сразу должен меняться цвет поля вокруг картинки) и перейдем к вопросу о создании и формировании FNI-файла. Нам надо запоминать всего три пункта, поэтому секция у нас пусть будет всего одна, назовем ее Main, а сам файл, естественно, SlideShow.ini (если указать просто имя, то INI-файл будет создан, как глобальный, в папке Windows, поэтому его надо указывать с полным путем). В список глобальных переменных добавим переменную iniFiie:TiniFiie, а в предложение uses модулей slide.pas и

papka.pas модуль IniFiles. Обработчик onCreate формы Formi будет тогда выглядеть так (я сразу устанавливаю и параметры нужных компонентов, считанные из lNl-файла, если он уже существовал):

procedure TForral.FormCreate(Sender: TObject);

var x:integer;

begin

Application.onMinimize := OnMinimizeProc; Labell.Caption:=

‘Установить интервал для’+#13+’показа слайдов, с'; IniFile:=TIniFile.Create{ChangeFileExt(ParamStr(0),’ .ini’)) ;

(если не было – создаем, иначе открываем) with IniFile do begin

if SectionExists(‘Main’) then

{если файл и секция в нем уже есть) begin

Timerl.Interval:=ReadInteger(‘Main’,’Timer interval’,3000);

{по умолчанию Зс) x:=ReadInteger(‘Main’,’Color’,0); {по умолчанию цвет 0) if х>15 then {вдруг кто-то вручную исправилj begin

х:=0; {исправляем ошибку) Writelnteger(‘Main’,’Color’,0); end;

ColorBoxl.Selected:=ColorBoxl.Colors[x];

{устанавливаем цвет} Panel1.Color:=ColorBoxl.Selected; stpath: =ReadString (‘Main’, ‘Path’, ‘C:V) ; {по умолчанию диск С:I

try

ChDir(stpath); {устанавливаем текущую папкуI except stpath:=’C:\';

ChDir(stpath); {если не получается – диск С:I end;

end else {если секция еще не создана I begin

Writelnteger(‘Main’,’Timer interval’,Timer1.Interval); Writelnteger!’Main’,’Color’,0);

{под номером 0 в списке ColorBox – черный цвет) WriteString(‘Main’,’Path’,stpath); end;

Destroy; end; (IniFile) end;

Обратите внимание, что при установке в качестве текущей папки той, что задана в INI-файле, мы обрабатываем возможную исключительную ситуацию — за время между запусками программы папка могла исчезнуть (например, вытащили диск из CD-ROM).

Функция ChangeFileExt (ParamStr (0) , ‘ .ini’) сразу формирует нам ИМЯ INI-файла с полным путем к нему из названия исполняемого файла программы. Для формирования имени существует несколько способов получить папку, где находится программа, и это один из них— в следующей главе мы применим для получения текущей папки функцию ExtractFiieDir(Application.ExeNaroe). Если вы хотите организовать глобаль- ный INI-файл в директории Windows, то нужно указать сразу конкретное имя •SlideShow.ini* без пути к нему. Кстати, никто не запрещает и просто создать собственную секцию в одном из заведомо существующих IN I-файлов (например, win.ini или system.ini, которые имеются во всех версиях Windows).

Теперь добавим в обработчик Button2ciick в конец следующие строки:

IniFile:=TIniFile.Create(ChangeFileExt(ParamStr(0),’ .ini’)); {открываем

i nil

IniFile.Writelnteger(‘Main’,’Timer interval’,Timerl.Interval); IniFile.Destroy;

Созданный ранее обработчик события onclick для компонента CoiorBox будет выглядеть так:

procedure TForml.ColorBoxlClick(Sender: TObject); begin

Paneii.Color:=ColorBoxl.Selected;

IniFile:=TIniFile.Create(ChangeFileExt(ParamStr(0),’.ini’));

/открываем ini} IniFile.Writelnteger(‘Main’,’Color’,ColorBoxl.Itemlndex); IniFile.Destroy; end;

А в модуле papka.pes добавим при его закрытии (перед строкой Form3.close), соответственно:

IniFile:=TIniFile.C.reate(ChangeFileExt (ParamStr (0),’ . ini’));

fоткрываем ini}

IniFile.WriteString(‘Main’, ‘Path’,stpath) ; IniFile.Destroy;

Как видите, получилось лишь немногим сложнее, чем если бы мы создавали файл по собственному усмотрению, зато удобно и единообразно. На самом деле заниматься самодеятельностью имеет большой смысл только, если INI- файл уж очень простой, или если вы хотите скрыть его содержимое от посторонних взоров — в последнем случае не поможет даже переход к расположению параметров в реестре, т. к. опытный человек все равно их найдет. А вот если ваш файл будет содержать невнятный текст типа "$А0 $С2…" или вообще бинарные числа, то вероятность, что его расшифруют, резко уменьшается. Позже мы увидим, как можно создавать текстовые файлы, зашифрованные собственным шифром — правда, для серьезного криптографа расколоть наш шифр скорее всего будет, что рюмку чая проглотить, но 9999 из 10 ООО пользователей даже не поймут, как к этому подступиться.

В главе 11 мы продолжим эту тему, выполнив одно давнее обещание— дадим пользователю выбрать, демонстрировать заставку или нет, а также сворачивать ли приложение в иконку при минимизации и при закрытии.

Источник: Ревнч Ю. В.  Нестандартные приемы программирования на Delphi. — СПб.: БХВ-Петербург, 2005. — 560 е.: ил.

По теме:

  • Комментарии

Комментариев 2

  1. На счет формального подхода к ComboBox:
    Не вижу проблем.
    Кто мешает самостоятельно ввести русские названия цветов?
    Есть же свойство Items.
    Combobox1.Items[0]:=’Черный';
    и т.д.

  2. В предыдущем посте конечно-же имел ввиду ColorBox.
    Остальное все верно.