Delphi выбор нескольких файлов

Обновлено: 07.07.2024

В этом уроке мы с вами ознакомимся с основными принципами программной организации поиска файлов. Для начала определимся, зачем нам это может быть нужно. Например, вам нужно при запуске программы на выполнение просканировать определенный каталог на присутствие DOC файлов, и при наличии таковых открыть их на редактирование или напечатать. А как вам такая идея: фоновый поиск EXE файла в сети, и при обнаружении новой версии, автоматическое обновление.


Многим известны программы, где можно искать файлы, правила поиска файла. Файлы можно искать как с файловых командирах (нортон, волков, дос навигатор, фар), так в любой операционной системе. В операционной системе windows диалоговое окно поиска файла вызывается "Пуск" - "Поиск" - "Файлы и папки". В открывшимся окне необходимо задать условие искомого файла (название, маска) и путь начального поиска (каталог). На других вкладках этого диалогового окна можно расширить возможности поиска по дате изменения, по содержащемуся тексту, по размеру.

Вспомним правила поиска файлов. Вы можете задать как имя искомого файла, так и его маску, если название неизвестно или необходимо найти несколько. Т.е. применяя специальный шаблон поиска, вы можете организовать условия выборки найденных файлов. Сразу оговорюсь, что поиск можно применять как к файлам, так и к каталогам. Будем их называть элементами файловой системы. В шаблон маски искомых элементов может входить:
1. Буквы и цифры в названии и расширении.
2. Символ * (звездочка, математический знак "умножить"), заменяющий любое количество всевозможных букв и цифр в названии или расширении.
3. Символ ? (знак вопроса), заменяющий одну букву или цифру в названии или расширении искомого элемента.
Например, вы ищите все текстовые файлы с расширением TXT. В поле имени искомого файла вам нужно ввести "*.TXT" (пишется без кавычек) и система найдет все такие файлы в указанном диске или каталоге. Если вам надо найти все файлы с названием semen, то в поле поиска файла нужно ввести "semen.*". Если вам нужно найти элементы с третьей буквой k и с первой буквой t в расширении, то вводите "??k*.t*". Здесь знак вопроса указывает на любой символ, третьим символом по порядку идет буква k, далее название файла (каталога) может состоять из любого количества букв и цифр, указываем звездочку. В расширении первая буква t, дальше следует любое расширение.
Примечание: файлы и каталоги в операционной системе windows ищутся без учета регистра, т.е. строчние и прописные буквы не различаются.

Теперь рассмотрим программный поиск файлов с помощью языка программирования object pascal.

Вся организация цикла поиска, а именно это и есть цикл с продолжением поиска, сводится к:
1. Задание условий поиска. Это каталог и маска искомого элемента или элементов, атрибуты элемента(ов). При задании условий поиска сразу происходит поиск первого подходящего под условие. Это функция FindFirst.
2. Продолжение поиска следующего элемента по заданным в первом пункте условиям. Это функция FindNext и она может вызываться сколько угодно раз, пока все файлы и каталоги, удовлетворяющие условию, не будут найдены.
3. Закрытие поиска и освобождение памяти, выделяемую системой под поиск. Команда FindClose.

FindFirst (КАТАЛОГ_ПОИСКА_И_МАСКА_ФАЙЛА , АТРИБУТЫ_ИСКОМОГО_ФАЙЛА , ПОИСКОВОЯ_ПЕРЕМЕННАЯ);

где: Каталог для поиска и маска искомого элемента - строковая величина, имеющая тип String, может, например, содержать 'c:\*.*' - все элементы в корне диска С. Обратите внимание, что указывается полный путь для поиска.

Атрибуты искомого элемента это пользовательские или системные атрибуты, которые может иметь файл (каталог, метка диска). Вот их перечень:
faReadOnly - Файлы "только чтение". Такой атрибут устанавливается на файлы, которые не рекомендовано изменять, удалять. Такой атрибут имеют файлы, например, записанные на компакт-дисках.
faHidden - Скрытые файлы. При обычных установках браузера и командира эти файлы невидимы.
faSysFile - Системные файлы.
faVolumeID - Файл метки диска. Такой элемент в своем имени имеет название диска (максимум 11 символов).
faDirectory - Атрибут признака каталога.
faArchive - Обычный файл. По умолчанию устанавливается на заново создаваемых файлах.
faAnyFile - Если установить в качестве атрибута искомых элементов, то будет произведен поиск по всем вышесказанным атрибутам.
Эти вам нужно искать только элементы, имеющие атрибут "каталог" и "скрытый", то можно применить знак математического сложения, например faDirectory + faHidden.

Поисковая переменная имеет тип TSearchRec. В нее, при успешном результате поиска, будет занесены все необходимые данные о найденном файловом элементе.

Поскольку FindFirst является функцией, то она должна сама возвращать некоторое значение. Это значение имеет тип Integer и означает результат поиска файла (код ошибки поиска). Если файл найден, то принимает нулевое значение.

Эта функция продолжает поиск, заданный в функции FindNext. Возвращает значение результата поиска (нулевое в случае успешного поиска).

Закрывает поиск и освобождает память, выделенную системой под поиск.

Теперь рассмотрим пример. Допустим, нам надо найти все файлы и каталоги в каталоге DELPHI, находящийся на диске C:. В дальнейшем, вы можете самостоятельно, изменяя маску, менять условия поиска. Для формы с компонентом ListBox1 и кнопкой Button1 реакция на OnClick по кнопке:

procedure TForm1.Button1Click(Sender: TObject);
Var SR:TSearchRec; // поисковая переменная
FindRes:Integer; // переменная для записи результата поиска
begin
ListBox1.Clear; // очистка компонента ListBox1 перед занесением в него списка файлов

FindRes:=FindFirst('c:\delphi\*.*',faAnyFile,SR); // задание условий поиска и начало поиска

While FindRes=0 do // пока мы находим файлы (каталоги), то выполнять цикл
begin
ListBox1.Items.Add(SR.Name); // добавление в список название найденного элемента
FindRes:=FindNext(SR); // продолжение поиска по заданным условиям
end;
FindClose(SR); // закрываем поиск
end;

Представленный пример кода, в принципе, является основой для организации более углубленного поиска, поиска файлов по времени создания, по содержащимся словам. Если вы запустите эту программу на выполнение, то при нажатии на кнопку Button1 вы увидите в списке в первой и второй строке элементы "." и "..". Это элементы, имеющие атрибут "каталог". Первый содержит связь с корневым каталогом диска, второй содержит связь к каталогом верхнего уровня. Со вторым вы встречаетесь в дисковых командных оболочках, например нортон, когда выбираете каталог ".." и нажимаете на "ввод". Тем самым вы попадаете в каталог на уровень выше. Естественно, в нашей поисковой программе такие элементы не надо вносить в список, поэтому мы игнорируем их нахождение. Исправляем процедуру нажатия на кнопку Button1:

procedure TForm1.Button1Click(Sender: TObject);
Var SR:TSearchRec;
FindRes:Integer;
begin
ListBox1.Clear;

FindRes:=FindFirst('c:\delphi\*.*',faAnyFile,SR);
While FindRes=0 do
begin
if ((SR.Attr and faDirectory)=faDirectory) an d // если найденный элемент каталог и
((SR.Name='.')or(SR.Name='..')) then // он имеет название "." или "..", тогда:
begin
FindRes:=FindNext(SR); // продолжить поиск
Continue; // продолжить цикл
end;

ListBox1.Items.Add(SR.Name);
FindRes:=FindNext(SR);
end;
FindClose(SR);
end;

В этом случае, при нахождении каталога с именем "." или с именем ".." программа продолжит обработку цикла поиска без вывода найденного имени элемента в компонент списка ListBox1.

Теперь рассмотрим тип TSearchRec. Он имеет в себе несколько полезных свойств:
Name - название найденного каталога (файла);
Size - размер файла в байтах;
Attr - атрибуты каталога (файла);
Time - упакованное значение времени и даты создания каталога (файла).

Все вышеперечисленные свойства мы уже рассмотрели или они понятны сразу, за исключением свойства Time. Оно имеет тип Integer и содержит в себе упакованное значение даты и времени создания файла. Распаковка производится с помощью функции FileDateToDateTime, которая в результате возвращает значение даты и времени.
Теперь добавим в нашу форму компонент DateTimePicher1 (страница Win32) и допишем несколько строк.

procedure TForm1.Button1Click(Sender: TObject);
Var SR:TSearchRec;
FindRes:Integer;
begin
ListBox1.Clear;

FindRes:=FindFirst('c:\delphi\*.*',faAnyFile,SR);
While FindRes=0 do
begin
if ((SR.Attr and faDirectory)=faDirectory) and
((SR.Name='.')or(SR.Name='..')) then
begin
FindRes:=FindNext(SR);
Continue;
end;
if FileDateToDateTime(SR.Time) // если у файла (каталога) дата создания меньше, чем установлено в DateTimePicker1, то
begin
FindRes:=FindNext(SR); // продолжить поиск
Continue; // продолжить цикл
end;

ListBox1.Items.Add(SR.Name);
FindRes:=FindNext(SR);
end;
FindClose(SR);
end;

Как вы уже заметили, мы отбираем файлы и каталоги по дате создания, начиная с указанной в компоненте DateTimePicker1.

Теперь попробуем организовать поиск файлов во всех вложенных каталогах. Это не так просто, как может показаться на первый взгляд. Нам придется вручную организовывать весь цикл входа-выхода из каталога, перебор файлов. Немного сложноватый материал, но возможно те из вас, кто уже работал с языком программирования pascal или другим, знакомы с технологией многократности и многовложенности использования одного и того же программного кода. Коротко объясню алгоритм работы такой программы.
1. Задание начальных условий поиска, поиск первого элемента.
2. Если найден файл, то выводим его и соответственно обрабатываем (выводим в список, открываем, удаляем и т.п.).
3. Если найден каталог, то начинаем новую процедуру поиска. Но программный код остается прежним. Мы просто заново вызываем и входим в эту же процедуру поиска.
4. Обрабатываем таким же образом все вложенные в этот каталог файлы и каталоги (начинаем новый поиск в обнаруженном каталоге).
5. Если элементов во вложенном каталоге больше нет, то обработка процедуры поиска в нем завершается, и мы выходим из нее. При этом мы оказываемся в том же месте, откуда и вызвали эту процедуру. Но она была вызвана из этой же процедуры. Поэтому программа продолжает свое выполнение дальше с момента возврата.
Таким образом, сколько витков программа наматывает на так называемый клубок, столько витков она и размотает. Программа на выполнении проходит все дерево вложенных каталогов, выполняя один и тот же кусок программного кода! И при этом данные условий поиска не перепутываются, и для каждой уникальной процедуры они сохраняются.

Рассмотрим пример. Создайте новый проект. Для создания отдельной процедуры поиска нам нужно объявить ее в соответствующем разделе (создаем ее вручную, поэтому и самостоятельно объявляем).

В разделе public пишем строку:

А в разделе кода программы, до слова "end." вставляем пустой каркас процедуры

procedure TForm1.FindFile(Dir:String);
begin

На форму вставляем компонент списка ListBox1, Button1, Edit1. Для компонента Edit1 свойство Text устанавливаем в "c:\delphi\". Обратите внимание на последний символ, знак "\", присутствие которого в начальном пути поиска обязательно. Дальше процедура OnClick для кнопки Button1 выглядит следующим образом:

procedure TForm1.Button1Click(Sender: TObject);
begin
ListBox1.Clear; // очистка списка файлов
FindFile(Edit1.Text); // поиск файлов с начальными условиями, заданных в Edit1
end;

Созданная нами вручную процедура поиска:

procedure TForm1.FindFile(Dir:String);
Var SR:TSearchRec;
FindRes:Integer;
begin
FindRes:=FindFirst(Dir+'*.*',faAnyFile,SR);
While FindRes=0 do
begin
if ((SR.Attr and faDirectory)=faDirectory) and
((SR.Name='.')or(SR.Name='..')) then
begin
FindRes:=FindNext(SR);
Continue;
end;

if ((SR.Attr and faDirectory)=faDirectory) then // если найден каталог, то
begin
FindFile(Dir+SR.Name+'\'); // входим в процедуру поиска с параметрами текущего каталога + каталог, что мы нашли
FindRes:=FindNext(SR); // после осмотра вложенного каталога мы продолжаем поиск в этом каталоге
Continue; // продолжить цикл
end;

ListBox1.Items.Add(SR.Name);
FindRes:=FindNext(SR);
end;
FindClose(SR);
end;

Если вы в компоненте Edit1 в качестве начального условия поиска файлов зададите корневую папку диска, например "С:\", то вы получите полный перечень всех файлов на данном диске. Обратите внимание на скорость поиска файлов и скорость работы вашей программы.

То, что мы узнали в предыдущей части урока, позволяет работать с файлами по адресу, жёстко записанному в тексте программы. Мы же хотим иметь возможность открывать любые файлы и работать с файлами по нашему выбору. Естественно, Delphi предоставляет нам такую возможность. Рассмотрим компоненты, позволяющие в работающей программе осуществлять выбор файлов. Delphi диалоги выбора файла позволяют указать програме, с каким файлом мы хотим работать.

На вкладке палитры компонентов Dialogs находятся компонент Delphi OpenDialog и компонент Delphi SaveDialog. Все Delphi диалоги, находящиеся на этой вкладке, в том числе и Delphi диалоги выбора файла, невизуальные, т.е. при переносе их на Форму в работающей программе их не видно, они видны только на этапе конструирования. Компонент Delphi OpenDialog позволяет открыть в нашей программе стандартное Windows-окно диалога открытия файла, компонент Delphi SaveDialog - окно диалога сохранения.

Delphi компоненты выбора файла


Delphi диалоги выбора файла сами по себе ничего не делают, а только предоставляют настройки, сделанные пользователем при выборе файла. Самый важный метод Delphi диалогов - Execute. Он срабатывает в момент нажатия кнопки "открыть" или "сохранить" в окне выбора файла. Для примера давайте введём в программу возможность выбора файла для загрузки в редактор Memo, и сохранения после редактирования.

Итак, кидаем на Форму оба Delphi диалога, текстовый редактор Memo, и три кнопки Button. В свойство Caption одной из них записываем "Открыть. ", другой - "Сохранить", третьей - "Сохранить как. "

Работа с диалогами выбора файла


В обработчике OnClick кнопки "Открыть. " пишем:

if OpenDialog1.Execute then
Memo1.Lines.LoadFromFile(OpenDialog1.FileName);

В результате выбора файла свойство FileName компонента OpenDialog получает значение полного адреса выбранного файла, который мы и вставляем в функцию загрузки файла компонента Memo.
Всё это хорошо, но только в данном случае, когда записанное выражение записывается в одну строку. Если программа использует несколько раз выражение OpenDialog1.FileName, то писать руками устанешь. В Delphi для такого случая есть так называемый "оператор присоединения" with. Он используется для любых объектов, имеющих длинный "хвост" из свойств, которые приходится записывать многократно. Вот как он записывается:

with Объект do
begin

Свойства Объекта внутри логических скобок begin/end можно записывать непосредственно. Допускается перечислять через запятую несколько объектов. Естественно, в случае, когда внутри скобок находится один оператор, они необязательны. Перепишем фрагмент загрузки файла с использованием оператора присоединения:

with OpenDialog1, Memo1 do
if Execute then
Lines.LoadFromFile(FileName);

Запись получается более компактной.
Так как свойства компонентов OpenDialog и SaveDialog одинаковы, сохранение текста выглядит абсолютно аналогично. Создаём обработчик нажатия кнопки "Сохранить как. " и пишем:

with SaveDialog1, Memo1 do
if Execute then
begin
Lines.SaveToFile(FileName);
OpenDialog1.FileName:=FileName; // Чтобы исправленный текст не затёр источник
end;

Наконец, для кнопки "Сохранить" пишем:

Memo1.Lines.SaveToFile(OpenDialog1.FileName); // Сохраняем туда, откуда считали

(В предыдущей строчке была ошибка. Как справедливо заметил в комментариях Oraculum - OpenDialog1.FileNam e нужно писать без кавычек.)

При работе этих фрагментов можно заметить, что выбирать приходится из всех файлов в нужной директории. Удобнее видеть только, например, текстовые файлы, или другой тип файлов по нашему выбору. Для этого используются фильтры, свойство Filter в наших компонентах. Настраивается оно в Инспекторе Объектов. При выборе его можно перейти в редактор фильтров:

В колонке FilterName записываем имена фильтров, в колонке Filter - список масок файлов, разделённых точкой с запятой. Маска файла в данном случае выглядит как

* . расширение_файла ;

Теперь, пользуясь всем пройденным материалом, можно решить задачку на тему: как определить размер файла

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

Компонент OpenDialog реализует диалог открытия файла. При запуске этого диалога появляется окно (см. скриншот), в котором можно выбрать имя открываемого файла. В случае успешного закрытия диалогового окна (нажатием кнопки Open) в качестве результата возвращается выбранное имя файла.


Компонент SaveDialog предлагает стандартный диалог сохранения файла, который отличается от диалога открытия файла только своим заголовком.

Основные свойства компонентов OpenDialog и SaveDialog

Далее перечислены основные свойства компонентов OpenDialog и SaveDialog.

FileName типа String — указывает имя и полный путь файла, выбранного в диалоге. Имя файла отображается в строке редактирования списка Имя файла и является результатом диалога.

Title типа String— задает заголовок окна. Если свойство Title не установлено, то по умолчанию используется заголовок Open для OpenDialog и заголовок Save — для SaveDialog.

InitiaLDir типа String— определяет каталог, содержимое которого отображается при вызове диалогового окна. Если каталог не задан, то отображается содержимое текущего каталога.

DefaultExt типа String — задает расширение, автоматически используемое в имени файла, если пользователь не указал расширение.

Filter типа String— задает маски имен файлов, отображаемых в раскрывающемся списке Тип файлов. В диалоговом окне видны имена файлов, совпадающие с указанной маской (см. скриншот) это файл с расширением jpeg). По умолчанию значением Filter является пустая строка, что соответствует отображению имен файлов всех типов.

FilterIndex типа Integer — указывает, какая из масок фильтра отображается в списке. По умолчанию свойство FilterIndex имеет значение 1 (используется первая маска).

Options

Options типа TOpenOptions — применяется для настройки параметров, управляющих внешним видом и функциональными возможностями диалога. Каждый параметр (флажок) может быть установлен или снят. Свойство Options имеет около двух десятков параметров, наиболее важные перечислены ниже:

Основные параметры свойства Options

  • ofAllowMultiSelect (из списка можно выбрать одновременно более одного файла);
  • ofCreatePrompt (при вводе несуществующего имени файла выдается запрос на создание файла);
  • ofNoLongNames (имена файлов отображаются как короткие, не более 8 символов для имени и 3 символов для расширения);
  • ofOldStyleDialog (создает диалоговое окно в стиле Windows 3.11).

Фильтр представляет собой последовательность значений, разделенных знаком |. Каждое значение состоит из описания и маски, также разделенных знаком |. Описание представляет собой обычный текст, поясняющий пользователю данную маску. Маска является шаблоном отображаемых файлов и состоит из имени и расширения. Если для одного описания приводится несколько масок, то они разделяются знаком ;.

Здесь фильтр формируется с двумя масками — маской для текстовых файлов и маской для всех файлов (под текстовыми понимаются файлы типов doc и txt).

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

Фильтр обычно формируется при проектировании приложения. Для этого из окна Инспектора объектов щелчком в области значения свойства Filter вызывается Редактор фильтра (Filter Editor).

Рабочее поле окна Редактора фильтра представляет собой таблицу, состоящую из двух столбцов с заголовками Filter Name и Filter. Значения фильтра вводятся построчно, при этом в левом столбце указывается описание фильтра, а в правом — соответствующая маска (скриншот уже был выше).

Стандартные диалоговые окна выбора имени файла для открытия или сохранения файла вызываются на экран методом Execute. Эта функция в качестве результата возвращает логическое значение, позволяющее определить, как закрыт диалог. Если пользователь в процессе диалога нажал кнопку Open или Save, то диалог считается принятым, и функция Execute возвращает значение True. Если диалог был закрыт любым другим способом, то он считается не принятым, и функция возвращает значение False.

При нажатии кнопки Button2 появляется диалоговое окно OpenDialog1 выбора имени файла для открытия. При выборе имени текстового файла его содержимое загружается в компонент Memo1. Напомним, что многострочный редактор Memo позволяет работать с текстами в коде ANSI. При отмене диалога OpenDialog1 открытие файла не происходит.

Компоненты OpenPictureDialog и SavePictureDialog вызывают стандартные диалоги открытия и сохранения графических файлов. Эти стандартные диалоги отличаются от OpenDialog и SaveDialog видом окон (см. скриншот) и установленными значениями свойства Filter.

Так, по умолчанию свойство Filter установлено для отображения графических файлов следующих форматов:

  • JPEG (*.jpg);
  • JPEG (*.jpg);
  • растровое изображение (*.bmp);
  • значок (*.ico);

При использовании диалогов OpenPictureDialog и SavePictureDialog, а также OpenDialog и SaveDialog значение свойства Filter можно установить ранее рассмотренными способами, а также с помощью функции GraphicFilter (GraphicClass: TGraphicCiass) : String.

Параметр GraphicClass принадлежит к одному из графических классов: TBitmap, TGraphic (и его потомки), TIcon, TMetafile или TJPEGimage. Для работы с классом TJPEGimage нужно подключить модуль jpeg. В качестве результата функция GraphicFilter () возвращает строку с фильтрами для указанного графического класса:

TBitmap — Bitmaps (*.bmp) |*.bmp;

TIcon—Icons (* . ico) I * . ico;

TMetafile — All (*.emf;*.wmf) |*.emf;*.wmf|Enhanced Metafiles(*.emf)| *.emf|Metafiles(*.wmf) |*.wmf;

TJPEGimage — All (*.jpg;*.jpg) | *.jpg;*.jpg| JPEG Image File (*.jpg) |*.jpg| JPEG Image File (*.jpg)| *.jpg;

TGraphic — All (*.jpg;*.jpg;*.bmp;*.ico;*.emf;*.wmf) | *.jpg;*.jpg; *.bmp;*.ico;*.emf; *.wmf|JPEG Image File (*.jpg) | *.jpg| JPEG Image File (*.jpg) |*.jpgI Bitmaps (*.bmp) | *.bmp| Icons(*.ico) |*.ico | Enhanced Metafiles(*.emf)|*.emf|Metafiles(*.wmf)I*.wmf.

Если модуль jpeg в разделе uses не указан, то фильтры, соответствующие формату JPEG, будут отсутствовать.

Например, фильтр, заданный как OpenDialog1.Filter := GraphicFilter(TGraphic); позволяет отображать имена графических файлов допустимых форматов.

Диалог для открытия файла. Для добавления в программу стандартного диалога для открытия файла добавьте на форму компонент OpenDialog с вкладки Палитры компонентов.

Также добавьте на форму обычную кнопку. Именно при щелчке по ней и будут показываться наше диалоговое окно для открытия файла.

Для того чтобы при нажатии на кнопку диалоговое окно появилось, добавьте в ее обработчик, следующий код:

procedure TForm1.Button1Click(Sender: TObject);

Разумеется, для работы с файлом не достаточно только вызвать наше диалоговое окно. Необходимо еще извлечь из него имя выбранного файла. Вот как это делается (в приведенном примере содержимое выбранного пользователем текстового файла показывается в компоненте Memo1 типа TMemo , который надо разместить на форме).

if OpenDialog1.Execute then

Таким образом для извлечения файла мы используем свойство FileName нашего диалога. Обратите внимание, что с помощью if мы проверяем, выбрал ли пользователь вообще что-нибудь. Если пользователь ничего не выбрал, то OpenDialog1.Execute возвращает false , и последующий код не выполняется. Вот еще несколько полезных свойств для нашего диалога для открытия файлов.

Для открытия в диалоге заранее определенной папки используем свойство InitialDir :

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

Затем просто заполните левый и правый столбцы. Левый определяет, что за надпись увидит пользователь, а правый - что за файлы будут показываться.

Обратите внимание, что мы можем для одного описания задать несколько масок, разделяя их точкой с запятой.

Свойство FilterIndex диалогового окна определяет номер активного фильтра (т. е. того, который будет выбран при вызове диалога). Нумерация при этом начинается с единицы.

Читайте также: