Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
УМК ПП1 (C++ БД).doc
Скачиваний:
1
Добавлен:
01.03.2025
Размер:
5.01 Mб
Скачать

3.3. Многострочные окна

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

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

В системе Builder для этой цели разработаны несколько компонентов, простейшим из которых является ListBox.

Компонент выбора ListBox

Назначение:

Основное: выбор одного значения из предоставляемого множества.

Побочное: отображение на экране многострочных списков строк, формирование вторичных списков за счет копирования и переноса строк из ведущего списка.

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

Инспектор объектов показывает в своем окне 54 свойства компонента ListBox; некоторые свойства, используемые в данном пособии, представлены на рис.32. С остальными свойствами студент может ознакомиться самостоятельно по литературе. Визуальное представление компонента изображено на рис. 33.

Рис. 32. Некоторые свойства компонента ListBox

На рисунке показаны 24 наиболее употребляемые свойства, а их смысл объясняется в следующей таблице.

Свойство

Описание

Action

Действие из списка ActionList (см. Раздел 1), которое связывается с данным компонентом

Aligne

Выравнивание компонента (по левому краю, по правому и т.д.)

Color

Цвет окна

Columns

Число колонок для представления cтрок

Count

Количество реально существующих строк в свойстве Items (доступно только из программы )

Enabled

Возможность воздействия на компонент типа щелчка

ExtendedSelect

Позволяет отмечать группу подряд расположенных строк, удерживая клавишу Sheft

Font

Параметры шрифта

Height

Высота окна

Hint

Текст всплывающей подсказки, появляющейся через полсекунды после наведения курсора на окно компонента

ItemHeight

Высота строки в пикселях

ItemIndex

Целое число, указывающее на индекс строки. Строки индексируются, начиная с нуля

Items

Набор строк типа AnsiString, содержащихся в памяти компонента

Left

Расстояние до левого края формы

MultiSelect

Возможность выделить несколько строк одновременно

Name

Программное имя компонента

PopupMenu

Выпадающее меню, вызываемое щелчком правой клавиши

SelCount

Количество одновременно выделенных строк (если MultiSelect=true)

ShowHint

Разрешение (true) на вывод всплывающей подсказки, текст которой записан в свойстве Hint

Sorted

Возможность сортировки списка

TabOrder

Порядковый номер компонента на форме. Клавиша Tab производит перемещение курсора по компонентам в порядке этих номеров

Top

Расстояние до верхнего края формы

Visible

Управляет видимостью компонента

Width

Ширина окна

Основным свойством является Items (пункты). В области памяти, отведённой для этого свойства, записываются строки типа AnsiString, представляющие собственно ту информацию, ради которой компонент и разрабатывался. Эти строки выводятся автоматически в прямоугольное окно компонента на экране.

Cовокупность таких строк имеет предопределённый тип данных TStrings, представляющих собою цепной однонаправленный список элементов типа AnsiString (см. [4]). Поскольку к отдельному элементу списка TStrings можно обращаться по его индексу, может показаться, что ListBox содержит массив строк. Пользователь может не знать о существовании цепных списков и не замечать этого, поскольку к каждой строке возможен доступ по её индексу , то есть порядковому номеру строки в списке, что типично для массивов. Так что для начинающего пользователя безразлично, какова внутренняя структура хранения информации и он может работать со списком как с массивом. Следует просто отдавать отчёт, что обработка списков всегда более длительная операция по сравнению с обработкой массивов, и в тех случаях, когда скорость работы программы играет роль, надо избегать непосредственной обработки строк в компоненте ListBox.

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

Заполнить список Items во время проектирования можно, нажав кнопку с многоточием около этого свойства в окне Инспектора объектов (см. рис. 32). Во время выполнения работать с этим свойством можно, пользуясь свойствами и методами класса TStrings Clear, Add и другими.

В компоненте ListBox имеется свойство MultiSelect, разрешающее пользователю множественный выбор в списке (на рис. 32 это свойство установлено в false). Если MultiSelect = false (значение по умолчанию), то пользователь может выбрать только один элемент списка. В этом случае можно узнать индекс выбранной строки из свойства Itemlndex, доступного только во время выполнения. Если ни одна строка не выбрана, то Itemlndex=1.

Например, следующий код проверяет выбор пользователя.

if (ListBoxl->ItemIndex < 0)

ShowMessage ("Выбор не сделан");

else ShowMessage ("Ваш выбор " +

IntToStr (ListBoxl->ItemIndex + 1) + ": " +

ListBoxl->Items->Strings [ListBoxl->ItemIndex] ) ;

Если выбор не сделан, появляется сообщение «Выбор не сделан». Если один из элементов списка выбран, то появляется сообщение вида «Ваш выбор ...: ...», где вместо первого многоточия отображается номер выбранной строки, а вместо второго многоточия текст выбранной строки.

Начальное значение Itemlndcx невозможно задать во время проектирования. По умолчанию Itemlndex = -1. Это означает, что ни один элемент списка не выбран. Если вы хотите задать этому свойству какое-то другое значение, т.е. установить выбор по умолчанию, который будет показан в момент начала работы приложения, то сделать это можно, например, в обработчике события OnActivate формы, введя в него оператор вида

ListBoxl->IteraIndex = 0;

Тогда курсор будет указывать на первую видимую в окне строку.

Если допускается множественный выбор (MultiSelect = true), то значение Itemlndex соответствует тому элементу списка, который находится в фокусе. При множественном выборе можно проверить, выбран ли данный элемент по свойству Selected[int Index] типа bool. Например, следующий код отображает сообщения вида "Выбрана строка .,.: ..." обо всех отмеченных строках:

for ( i=0; i < ListBoxl->Items->Count ;i++}

{if (ListBoxl->Selected[i] )

ShowMessage ("Выбрана строка " + IntToStr (i+1)' + ": " +

ListBoxl->Items->Strings [i] ) ;

}

На способ множественного выбора при MultiSelect = true влияет еще свойство ExtendedSelect. Если ExtendedSelect = true, то пользователь может выделить интервал элементов, выбрав один из них, затем нажав клавишу Shift и переведя курсор к другому элементу. Выделить не прилегающие друг к другу элементы пользователь может, если будет удерживать во время выбора нажатой клавишу Ctrl. Если же ExtendedSelect = false, то клавиши Shift и Ctrl при выборе не работают.

Свойство Columns определяет число столбцов, в которых будет отображаться список, если он не помещается целиком в окне компонента ListBox. Свойство Sorted позволяет упорядочить список по алфавиту. При Sorted =true новые строки в список добавляются не в конец, а по алфавиту.

Рассмотрим пример: имеется два списка ListBox1 и ListBox2. В первом списке записаны строки, второй список пуст. Требуется заполнить второй список элементами из первого списка, пользуясь такими типовыми операциями:

  1. скопировать выделенную строку;

  2. перенести выделенную строку;

  3. скопировать выделенную группу строк;

  4. перенести выделенную группу строк.

Для каждой операции предусмотрим отдельную кнопку, а в метки, расположенные над компонентами ListBox1 и ListBox2, будем выводить количество реально существующих строк каждого списка. Исходное состояние представлено на рис. 33-а.

а) б) в)

Рис. 33. Пример работы с ListBox:

а) – исходное состояние;

б) – скопированы строки 3, 6 и снова 3;

в) – перенесены строки 5 и 8.

Формирование исходного состояния. Начальное заполнение видимого окна ListBox происходит при помощи редактора строк свойства Items – кнопка с многоточием на рис. 32. По событию формы OnActivate устанавливаем ItemIndex=0 и тем самым подсвечиваем первую строку в списке 1, а фокус устанавливаем на первую кнопку командой

Button1->SetFocus() (см. рис. 33-а).

Листинг программы представлен на рис. 34. Команды установки начальных значений записаны в блоке 5 – :FormActivate.

#include <vcl.h>

#pragma hdrstop

#include "uListBox.h"

#pragma package(smart_init)

#pragma resource "*.dfm"

TForm2 *Form2;

//6 собственная процедура вывода на экран количества строк в обоих списках

void ShowKolichestvo()

{Form2->Label3->Caption="Кол-во="+IntToStr(Form2->ListBox1->Items->Count);

Form2->Label4->Caption="Кол-во="+IntToStr(Form2->ListBox2->Items->Count);

}

//конструктор формы – формируется автоматически-------------------------------------------

__fastcall TForm2::TForm2(TComponent* Owner)

: TForm(Owner)

{ }

//5 установка начальных значений ------------------------------------------

void __fastcall TForm2::FormActivate(TObject *Sender)

{ ListBox1->ItemIndex=0;

Button1->SetFocus();

ShowKolichestvo();// вызов собственной процедуры вывода на экран количества

//строк в обоих списках

}

//--------------------------------------------------

void __fastcall TForm2::Button5Click(TObject *Sender)

{Close();//завершение сеанса

}

void __fastcall TForm2::Button1Click(TObject *Sender)

{//1 копирование строки

int n;

n=ListBox1->ItemIndex;

ListBox2->Items->Add(ListBox1->Items->Strings[n]); //Добавление строки с индексом n

//из списка 1 к списку 2

ShowKolichestvo();// вызов собственной процедуры

}

//---------------------------------------------------------------------------

Рис. 34. Листинг программы, иллюстрирующей работу с ListBox (начало)

void __fastcall TForm2::Button2Click(TObject *Sender)

{//2 перенос строки

int n;

n=ListBox1->ItemIndex;

ListBox2->Items->Add(ListBox1->Items->Strings[n]);//копирование строки

ListBox1->Items->Delete(n);/скопированная строка удаляется из списка 1

ShowKolichestvo();

}

//3 копрование группы отмеченных строк-------------------------------------------

void __fastcall TForm2::Button3Click(TObject *Sender)

{int n,i;

n=ListBox1->Items->Count;

for (i=0;i<=n-1;i++)

if (ListBox1->Selected[i]) //проверка, является ли очередная строка отмеченной.

//если да, то она копируется

{ListBox2->Items->Add(ListBox1->Items->Strings[i]);//запись текущей строки

//списка 1 в список 2

}//конец операторjd if и for

ShowKolichestvo();

}

//4 перенос группы отмеченных строк-------------------------------------------

void __fastcall TForm2::Button4Click(TObject *Sender)

{int n,i;

n=ListBox1->Items->Count;

for (i=n-1;i>=0;i--)//просмотр списка строк идет снизу вверх (i--),т.к. в противном

// случае параметр цикла i всегда выйдет за границы индекса

// и возникнет аварийное завершение

if (ListBox1->Selected[i]) //проверка, является ли очередная строка отмеченной.

{ ListBox2->Items->Add(ListBox1->Items->Strings[i]);//копирование строки в список 2.

ListBox1->Items->Delete(i);//удаление скопировнанной строки из списка 1

}//конец операторjd if и for

ShowKolichestvo();

}

//конец программы -----------------------------------------------------------

Рис. 34. Листинг программы, иллюстрирующей работу с ListBox (окончание, насало на предыдущей странице)

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

При щелчке по кнопке Button1 «Скопировать строку» инициируется блок 1 программы и возникает ситуация, изображённая на рис. 33-б. Здесь кнопка была нажата три раза, когда были последовательно отмечены строки 3, 6 и снова 3. В списке 2 появились копии этих строк, количество строк в списке 2 увеличилось до 3, а в списке 1 количество строк не изменилось – оно осталось равным 10.

На рис. 33-в показана ситуация, когда пользователь в списке 1 выбрал строку 5 и нажал кнопку Button2 «Перенести строку», а затем то же самое проделал со строкой 8. В результате строки 5 и 8 оказались в списке 2, а из списка 1 они были удалены. Количество строк списка 1 уменьшилось до 8, а число строк в списке 2 увеличилось до 5. Алгоритм переноса строк записан в блоке 2 листинга программы.

Чтобы скопировать или перенести группу отмеченных в списке 1 строк, необходимо прежде всего установить свойство MultiSelect в значение true. Затем, удерживая нажатой клавишу Shift, надо в произвольном порядке выделить щелчком нужные строки. При этом свойство Selected выделенной строки приобретает значение true. Это обстоятельство и позволяет построить циклический алгоритм, в котором в цикле просматриваются все строки, а копируются только те из них, у которых свойство Selected имеет значение true – смотреть блоки 3 и 4 листинга программы. Результаты копирования группы строк представлены на рис. 33-б, а переноса – на рис. 33-в. Обратите внимание, что при копировании количество строк исходного списка 1 не меняется, а при переносе оно уменьшается.

а) б)

Рис. 35. Копирование и перенос группы строк:

а) – копирование группы отмеченных строк;

б) – перенос группы отмеченных строк.

Наиболее прихотливым является перенос группы выделенных строк. Дело в том, что при переносе строки в исходном списке уменьшается общее количество строк, и если построить обычный цикл, когда управляющий индекс пробегает значения от 0 до count-1, где count – количество строк до первого удаления, то возникает реальная вероятность того, что индекс превысит реальное количество строк. Чтобы этого избежать, надо просматривать список в порядке «от конца – к началу» – смотреть блок 4 листинга.

Более подробно компонент ListBox рассматривается в рамках лабораторных работ (см. ниже).

Редактор простого текста компонент Memo

Назначение:

Основное: редактирование текста в формате txt непосредственно в окне компонента (ввод и вывод символов, поиск, вставка и удаление подстроки).

Побочное: отображение на экране многострочных списков строк, обращение к строке по индексу, формирование вторичных списков за счет копирования и переноса строк из ведущего списка.

Визуальное представление компонента изображено на рис. 36. Вообще Инспектор объектов показывает в своем окне многочисленные свойства компонента Memo. Те из них, которые используются в данном пособии, представлены на рис. 36.

С остальными свойствами студент может ознакомиться самостоятельно по литературе.

Рис. 36. Некоторые свойства компонента Memo

На рисунке показаны наиболее распространённые свойства компонента Memo, смысл которых разъясняется в следующей таблице. Большинство свойств совпадают по именам и по смыслу со свойствами компонента ListBox.

Свойство

Описание

Action

Действие из списка ActionList (см. Раздел 1), которое связывается с данным компонентом

Aligne

Выравнивание компонента (по левому краю, по правому и т.д.)

CaretPos.x

Когда курсор установлен на символ, значение «x» указывает порядковый номер символа в данной строке. Свойство доступно только из программы

CaretPos.y

Указывает порядковый номер строки, в которой находится символ под курсором. Свойство доступно только из программы

Color

Цвет окна

Count

Количество реально существующих строк в свойстве Lines. Доступно только из программы

Enabled

Возможность воздействия на компонент типа щелчка

Font

Параметры шрифта

Height

Высота окна

Hint

Текст всплывающей подсказки, появляющейся через полсекунды после наведения курсора на окно компонента

Left

Расстояние до левого края формы

Lines

Набор строк типа AnsiString, содержащихся в памяти компонента

Name

Программное имя компонента

PopupMenu

Выпадающее меню, вызываемое щелчком правой клавиши

ReadOnly

Определяет, может ли пользователь изменять текст в окне компонента

ScrollBar

Определяет полосу прокрутки

SelLength

Указывает количество отмеченных курсором символов. Доступно только из программы

SelStart

Указывает номер первого символа в отмеченном участке текста в свойстве Text. Доступно только из программы

SelText

Указывает выделенную последовательность символов в тексте окна компонента. Доступно только из программы

ShowHint

Разрешение (true) на вывод всплывающей подсказки, текст которой записан в свойстве Hint

TabOrder

Порядковый номер компонента на форме. Клавиша Tab производит перемещение курсора по компонентам в порядке этих номеров

Text

Представляет собою длинную строку, составленную как соединение (конкатенацию) всех строк свойства Lines. При этом в Text включены и скрытые на экране управляющие символы «Перевод строки» (код 10) и «Возврат каретки» (код 13). Свойство доступно только из программы

Top

Расстояние до верхнего края формы

Visible

Управляет видимостью компонента

Width

Ширина окна

Основным свойством является Lines (линии, строки). В области памяти, отведённой для этого свойства, хранятся строки типа AnsiString, представляющие текст, записанный в окне. Эти строки имеют тип TStrings и, следовательно, являются цепным однонаправленным списком элементов [4]. Как и в компоненте ListBox, здесь можно обращаться к строке по её индексу, например команда

ShowMessage(Memo1->Lines->Strings[4]);

выведет на экран сообщение вида (рис. 37):

Рис. 37. Обращение по индексу 4 к набору строк компонента Memo1.

Однако компонент Memo предназначен не для того, чтобы интерпретировать записанную в нём информацию как массив строк, что имело место в ListBox. Поэтому в компоненте Memo не предусмотрены такие свойства, как ItemIndex, Selected, MultiSelect, SelCount, ExtendedSelect. Предметом основного интереса программирования является именно свойство Text, подсвойства и методы которого позволяют изменять символы в окне в достаточно широких пределах, чтобы получить редактор текста, аналогичный Блокноту (NotePad).

Общая характеристика свойств

Компонент Memo является окном редактирования многострочного текста, которое снабжено многими функциями, свойственными большинству редакторов. Предусмотрены типичные комбинации «горячих» клавиш: Ctrl-C – копирование выделенного текста в буфер обмена Clipboard (команда Copy), Ctrl-X – вырезание выделенного текста в буфер Clipboard (команда Cut), Ctrl-V – вставка текста из буфера Clipboard в позицию курсора (команда Paste), Ctrl-Z – отмена последней команды редактирования.

В компоненте Memo формат (шрифт, его атрибуты, выравнивание) одинаков для всего текста и определяется свойством Font. Если вы сохраните в файле текст, введенный или отредактированный пользователем, то будет создан текстовый файл, содержащий только символы и не содержащий элементов форматирования, то есть текст в формате txt. При последующем чтении этого файла в Memo формат будет определяться текущим состоянием свойства Font компонента Memo, а не тем, в каком формате ранее вводился текст.

Свойство Alignment определяет выравнивание текста. Установка свойства ReadOnly в true задает текст только для чтения.

Свойство ScrollBars определяет наличие полос прокрутки текста в окне. По умолчанию ScrollBars = ssNone, что означает их отсутствие. Пользователь может в этом случае перемещаться по тексту только с помощью курсора. Можно задать свойству ScrollBars значения ssHorizontal, ssVertical или ssBoth, что будет соответственно означать наличие горизонтальной, вертикальной или обеих полос прокрутки.

Основное свойство окон Memo Lines – содержание текста окна в виде списка строк, имеющих тип TStrings. Начальное значение текста можно установить в процессе проектирования, нажав кнопку с многоточием около свойства Lines в окне Инспектора объектов.

Во время выполнения приложения вы можете заносить текст в окно редактирования с помощью методов свойства Lines типа TStrings. Здесь коротко укажем только на его основные свойства и методы, используемые в свойстве Lines.

Как уже говорилось, весь текст, представлен одной строкой типа String, внутри которой используются разделители типа символов «возврата каретки» и «перевода строки». Эта строка хранится в свойстве Text.

Доступ к отдельной строке текста вы можете получить с помощью свойства AnsiString Strings[int Index]. Индексы, как и везде в C++ Builder, начинаются с 0. Так что Memo1->Lines->Strings[0]  это текст первой строки.

Свойство только для чтения Count указывает число строк в тексте. Для очистки текста в окне надо выполнить процедуру Clear. Этот метод относится к самому окну, а не к его свойству Lines.

Для занесения новой строки в конец текста окна редактирования можно воспользоваться методами Add или Append свойства Lines. Для загрузки текста из файла применяется метод LoadFromFile. Сохранение текста в файле осуществляется методом SaveToFile.

Свойство SelStart компонента Memo указывает позицию курсора в тексте или начало выделенного пользователем текста. Выделенный фрагмент окрашивается на экране в тёмно-синий цвет. Снять отметку можно, пользуясь методом HideSelection.

Свойство CaretPos указывает на структуру типа record (запись), поле x которой содержит индекс символа в строке, перед которым расположен курсор, а поле y  индекс строки, в которой находится курсор. Таким образом, учитывая, что индексы начинаются с 0, значения Memo1-> CaretPos.+ 1 и Memo1->CaretPos.+ 1 определяют соответственно номер строки и символа в ней, перед которым расположен курсор.

Рассмотрим пример: имеется окно редактирования Memo1 и окно Edit1. В Memo1 записан текст, а в Edit1 – некая последовательность символов, для краткости называемая кортежем. Требуется выполнить такие типовые операции редактирования:

  1. Определить, является ли кортеж подстрокой текста – «Найти подстроку».

  2. Удалить подстроку.

  3. Заменить подстроку.

  4. Вставить подстроку.

Кроме операции «Найти», остальные функции могут быть выполнены известными действиями на клавиатуре типа «выделить фрагмент», «записать в буфер» и «взять из буфера». Наша задача состоит в том, чтобы показать, как программируются эти действия.

На рис. 38 показаны результаты выполнения операции «Найти».

а) б)

Рис. 38. Выполнение операции «Найти»:

а) – кортеж найден;

б) – кортеж не найден.

На рис. 38-а представлен положительный результат, когда поиск увенчался успехом, на рис. 38-б показана реакция программы, когда объект поиска не найден. Полный текст программы приведён на рис. 39. Алгоритм поиска описан в блоке 1, обратите внимание, что собственно поиск производится строковой функцией AnsiPos (опреатор 1), определяющей факт вхождения подстроки в строку. Подстрокой или запросом на поиск служит кортеж, взятый из Edit1->Text, строкой является полный текст компонента – Memo1->Text.

#include <vcl.h>

#pragma hdrstop

#include "uMemoRab.h"

#pragma package(smart_init)

#pragma resource "*.dfm"

TForm1 *Form1;

Рис. 39. Листинг программы работы с компонентом Memo (см.также следующую страницу)

__fastcall TForm1::TForm1(TComponent* Owner)

: TForm(Owner)

{//Конструктор формы

}

void __fastcall TForm1::bExitClick(TObject *Sender)

{Close();// Завершение сеанса

}

//Блок 1 - поиск кортежа в тексте ---------------------------------------------------

void __fastcall TForm1::bPoiskClick(TObject *Sender)

{AnsiString sPoisk; int p,len;

sPoisk=Trim(Edit1->Text); len=sPoisk.Length();

p=AnsiPos(sPoisk,Memo1->Text);//1- поиск вхождения подстроки sPoisk в строку Memo1->Text

if (p>0)

{// кортеж найден

Memo1->HideSelection=false;

Memo1->SelStart=p-1;//2- установить курсор компонента Memo на начало

//найденного фрагмента

Memo1->SelLength=len; // 3-указать длину отмечаемого фрагмента

MessageDlgPos( "Кортеж <"+sPoisk+"> найден \nи окрашен в синий цвет",

mtInformation, TMsgDlgButtons()<<mbOK,0,95,175);

}

else

{// кортеж не найден

MessageDlgPos("Кортеж <"+sPoisk+"> не найден",mtInformation,

TMsgDlgButtons()<<mbOK,0,95,175);

Memo1->HideSelection=true;

}

}

// Блок 2 - удаление фрагмента --------------------------------------------------------------------

void __fastcall TForm1::bDelClick(TObject *Sender)

{int p,n; AnsiString s;

p=Memo1->SelStart+1; n=Memo1->SelLength;

s=Memo1->Text;

s.Delete(p,n);

Memo1->Text=s;

//Метод Text.Delete(p,n) непосредственно к Memo1->Text неприменим

}

//Блок 3 - втавка кортежа---------------------------------------------------------------------------

void __fastcall TForm1::bInsertClick(TObject *Sender)

{int p; AnsiString s;

s=Memo1->Text;

p=Memo1->SelStart+1;

s.Insert(Edit1->Text,p);

Memo1->Text=s;

}

Рис. 39. Листинг программы работы с компонентом Memo (см.также следующую страницу)

// Блок 4 – найти кортеж и заменить ---------------------------------------------------------------------

void __fastcall TForm1::bZamenaClick(TObject *Sender)

{AnsiString sPoisk, s; int p,len;

sPoisk=Trim(Edit1->Text);

len=sPoisk.Length();

p=AnsiPos(sPoisk,Memo1->Text); // поиск кортежа, записанного в Edit1->Text

if (p>0)

{// здесь кортеж найден. Чтобы заменить его, надо сначала

// его удалить, а затем вставить другую подстроку

// - - - удаление - - - - - - - - - - - - - - - - - - - - -

s=Memo1->Text;

s.Delete(p,len);// удаляем len симолов c найденной позиции

//- - - вставка - - - - - - - - - - - - - - - - - - - - -

s.Insert(Edit2->Text,p);// вставляем подстроку из Edit2->Text

Memo1->Text=s;

}

else

{// кортеж не найден

MessageDlgPos("Кортеж <"+sPoisk+"> не найден",mtInformation,

TMsgDlgButtons()<<mbOK,0,95,175);

Memo1->HideSelection=true;

}

}

//---------------------------------------------------------------------------

Рис. 39. Листинг программы работы с компонентом Memo (окончание, начало см. на предудущих страницах

В блоке 1 программы производится поиск кортежа в тексте. Если подстрока найдена, то переменная p приобретает значение, равное индексу символа, начиная с которого найдено совпадение запроса на поиск и фрагмента текста, то есть p становится положительным, иначе p=0. Это обстоятельство и позволяет по-разному реагировать на положительный и отрицательный результаты поиска. Если подстрока найдена, выполняются операторы ветви «кортеж найден». Здесь используются два свойства компонента: Memo1->SelStart и Memo1->SelLengt. Первое свойство указывает системе начало отмечаемого фрагмента, а второе – его длину (операторы 2 и 3).

Удаление найденного фрагмента осуществляется не совсем стандартно – см.блок 2 программы. Вообще для удаления n символов из строки s, начиная с позиции p, следует воспользоваться методом Delete класса AnsiString и тогда оператор удаления примет вид:

s.Delete(p,n);

Формально свойство Memo1->Text имеет тип AnsiString и к нему может быть применён метод Delete, однако по неизвестным причинам он не работает и приходится применять часто распространённый в программировании трюк (см. блок 2 листинга программы) :

  1. присвоить вспомогательной переменной s типа AnsiString текст компонента;

  2. удалить из вспомогательной строки отмеченный фрагмент;

  3. сделать обратную замену: тексту компонента присвоить новое значение вспомогательной строки.

Обратите внимание, что параметры удаляемого фрагмента – p и n – определяются программно как

p=Memo1->SelStart+1; n=Memo1->SelLength;

При этом установить отмеченный фрагмент можно как автоматически по кнопке «Поиск», так и вручную, пользуясь мышкой или клавишами. Операция удаления представлена на рис. 40.

а) б)

Рис. 40. Удаление отмеченного фрагмента в компоненте Memo:

а) – отмечена подстрока «трока 4»

б) – отмеченная подстрока удалена

Обратите внимание на правую часть рисунка: нажата кнопка «Удалить» (обведена жирным прямоугольником) и подстрока «трока 4» исчезла.

Блок 3 программы обеспечивает вставку подстроки в точку, указанную курсором, при этом текст раздвигается, и его общая длина увеличивается на объём вставки (рис. 41).

Рис. 41. Вставка подстроки из окна «Подстрока для замены»

По ходу выполнения данного примера курсор был установлен на втором символе строки с индексом 4 (см. рис. 40). Именно в эту позицию и помещена строка «***», взятая из окна «Подстрока для замены».

В блок 4 программы описана процедура «Найти и заменить». Этот алгоритм состоит из трёх ранее описанных действий:

  1. «Найти».

  2. «Удалить».

  3. «Вставить».

В блоке 4 эти действия вновь описаны и прокомментированы, что делается в чисто учебных целях для упрощения понимания программы. Разумеется, для улучшения качества программы все типовые действия следовало бы описать в компоненте ActionList в виде подпрограмм низкого уровня (см. раздел 1) , а здесь пользоваться только вызовом этих процедур. Однако цель получения наилучшей программы не ставилась, поэтому её общая структура упрощена, но не оптимизирована.

Поведение программы при выполнении функции «Найти и заменить» представлено на рис. 42.

Рис. 42. Выполнение функции «Найти и заменить»

При нажатии кнопки «Найти и заменить» отыскивается подстрока «рока 7», записанная в окне «Кортеж для поиска» и заменяется на «***», занесённые в окно «Подстрока для замены».

Более подробно компонент Memo рассматривается в рамках лабораторных работ (см. ниже).

Редактор текста в обогащенном формате компонент Richedit

Назначение:

Основное: редактирование текста в обогащенном формате rtf непосредственно в окне компонента и их вывод на принтер.

Большинство свойств этого объекта совпадает со свойствами компонента Memo, приведём наиболее часто используемые :

Action

Aligne

CaretPos.x

CaretPos.y

Color

Count

Enabled

Font

Height

Hint

Left

Lines

Name

PopupMenu

ReadOnly

ScrollBar

SelLength

SelStart

SelText

ShowHint

TabOrder

Text

Top

Visible

Width

В следующей таблице приведены свойства, присущие только компоненту RichEdit.

Свойство

Подсвойство

Описание

SelAttriutes

атрибуты текста

Color

цвет шрифта

Name

наименование шрифта

Size

размер шрифта

Style

стиль шрифта

Paragraph

отступы и выравнивание абзаца

Alignment

выравнивание

taLeftJustify

влево

taCenter

(по центру)

taRightJustify

вправо

FirstIndent

число пикселей отступа красной строки

Numbering

вставка маркеров, как в списках значение

nsNone

отсутствие маркеров

nsBullet

маркеры ставятся

LeftIndent

отступы в пикселях от левого поля

RightIndent

отступы в пикселях от правого поля

Компонент RichEdit являются окном редактирования многострочного текста. Он так же, как и окно Edit, снабжён многими функциями, свойственными большинству редакторов. В них предусмотрены типичные комбинации «горячих» клавиш: Ctrl-C  – копирование выделенного текста в буфер обмена Clipboard (команда Copy), Ctrl-X  – вырезание выделенного текста в буфер Clipboard (команда Cut), Ctrl-V  – вставка текста из буфера Clipboard в позицию курсора (команда Paste), Ctrl-Z  – отмена последней команды редактирования.

Компонент RichEdit работает с текстом в обогащенном формате RTF. При желании изменить атрибуты вновь вводимого фрагмента текста нужно задать свойство SelAttributes. Это свойство в свою очередь имеет подсвойства: Color (цвет), Name (имя шрифта), Size (размер), Style (стиль) и ряд других. Например, введите на форму компонент RichEdit, диалог выбора шрифта FontDialog и кнопку Button, которая позволит пользователю менять атрибуты текста.

В обработчик щелчка кнопки введите текст:

if (FontDialog1->Execute())

RichEdit->SelAttributes->Assign(FontDialog1->Font) ;

RichEdit->SetFocus() ;

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

За выравнивание, отступы и т.д. в пределах текущего абзаца отвечает свойство Paragraph. Этот тип в свою очередь имеет ряд свойств: Alignment (выравнивание), принимающее значения taLeftJustify (влево), taCenter (по центру) или taRightJustify (вправо); FirstIndent  число пикселей отступа красной строки; Numbering  вставка маркеров, как в списках (значение nsNone  отсутствие маркеров, значение nsBullet  маркеры ставятся); LeftIndent и RightIndent  отступы в пикселях от левого и правого поля.

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

RnchEdit1->Pragraph->Alignement=tsCenter;

осуществляет выравнивание текущего абзаца по центру. А оператор

RnchEdit1->Pragraph->Numbering=nsBullet;

приведет к тому, что текущий абзац будет отображаться как список с маркерами.

Установка свойства ReadOnly в true задает текст только для чтения. Свойство ScrollBars определяет наличие полос прокрутки текста в окне. По умолчанию ScrollBars = ssNone, что означает их отсутствие. В этом случае перемещаться по тексту можно только с помощью курсора. Можно задать свойству ScrollBars значения ssHorizontal, ssVertical или ssBoth, что будет соответственно означать наличие горизонтальной, вертикальной или обеих полос прокрутки.

Во время выполнения приложения вы можете заносить текст в окно редактирования с помощью методов свойства Lines, полностью подобных аналогичным методам компонента Memo. Весь текст, представленный одной строкой типа String, внутри которой используются разделители типа символов возврата каретки и перевода строки, представлен свойством Text.

Доступ к отдельной строке текста вы можете получить с помощью свойства Strings класса AnsiString. Индексы, как и везде в C++ Builder, начинаются с 0. Так что Memo1->Lines->Strings[0]  это текст первой строки. Учтите, что если окно редактирования изменяется в размерах при работе с приложением и свойство WordWrap = true, то индексы строк будут изменяться при переносах строк, так что в этих случаях индекс мало о чем говорит.

Свойство только для чтения Count указывает число строк в тексте. Для очистки текста в окне надо выполнить процедуру Clear. Этот метод относится к самому окну, а не к его свойству Lines.

Для занесения новой строки в конец текста окна редактирования следует воспользоваться методами Add или Append свойства Lines. Для загрузки текста из файла применяется метод LoadFromFile. Сохранение текста в файле осуществляется методом SaveToFile.

Загрузка в окно RichEdit1 форматированного текста из файла может осуществляться командой

RichEdit1->Lines->LoadFromFile(“FileA.rtf”);

В окно редактирования может быть загружен и неформатированный текст:

RichEdit1->Lines->LoadFromFile(“FileB.txt”)

Сохранение текста формата txt в файле может осуществляться командой

RichEdit1->Lines->SaveToFile(“FileB.txt”)

Свойство SelStart компонента RichEdit указывает позицию курсора в тексте или начало выделенного пользователем фрагмента. Свойство CaretPos указывает на структуру типа record, поле x которой содержит индекс символа в строке, перед которым расположен курсор, а поле y  индекс строки, в которой находится курсор. Таким образом, учитывая, что индексы начинаются с 0, значения

RichEdit1->CaretPos.y + 1 и

RichEdit1->CaretPos.x + 1

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

Более подробно компонент RichEdit рассматривается в рамках лабораторных работ (см. ниже), а здесь рассмотрим пример, в котором показаны типичные действия по форматированию текста в окне RichEdit. Программа выполняет функции, указанные пунктами главного меню (рис. 43). Компонент ActionList не используется, поэтому программируются обработчики щелчков пунктов меню.

Рис. 43. Главное меню редактора RichEdit:

а) – меню группы «Файл»

б) – меню группы «Шрифт»

в) – меню группы «Стиль»

Интерфейс программы изображён на рис. 44, а её текст (листинг) – на рис. 48.

Рис. 44. Интерфейс программы «Редактор RichEdit»

В двух групповых панелях GroupBox1 и GroupBox2 находятся диалоговые окна ComboBox1 и ComboBox2, с помощью которых пользователь устанавливает наименование шрифта и его размер (см. пункт «Шрифт» Главного меню). Панели нужны для того, чтобы было удобно управлять видимостью компонентов. При старте программы обе панели скрываются (блок 1 листинга программы на рис. 48). Они становятся видимыми, когда пользователь щёлкает по пунктам меню «Наименование» (блок 5 листинга) или «Размер» (блок 6 листинга) – см. рис. 45.

Рис. 45. Реакция программы на щелчок по пункту меню «Размер»

В первый момент окно редактора пусто, чтобы начать работу, в него надо загрузить файл для редактирования. Можно выбрать файлы как в формате txt, так и в формате rtf (см. блоки 2 и 3 листинга программы на рис. 48).

Рис. 46. Загрузка окна редактирования неформатированным текстом

После загрузки файла его можно редактировать – выбирать тип шрифта, его размер, расположение на экране. Запомните, что редактированию подлежит любой отмеченный курсором фрагмент, вплоть до одной буквы (рис. 47).

Рис. 47. Текст после редактирования

Полный текст программы представлен на рис. 48.

#include <vcl.h>

#pragma hdrstop

#include "uRichEditRab.h"

//---------------------------------------------------------------------------

#pragma package(smart_init)

#pragma resource "*.dfm"

TForm1 *Form1;

//---------------------------------------------------------------------------

__fastcall TForm1::TForm1(TComponent* Owner)

: TForm(Owner)

{

}

//---------------------------------------------------------------------------

void __fastcall TForm1::FormActivate(TObject *Sender)

{//блок 1 - начальные установки при актвации формы

GroupBox1->Hide();GroupBox2->Hide();

}

//конец сеанса ------------------------------------------------------------------

void __fastcall TForm1::N4Click(TObject *Sender)

{Close();

}

//---------------------------------------------------------------------------

Рис. 48. Листинг программы работы с компонентом RichEdit (Начало, продолжение см. на следующей странице)

void __fastcall TForm1::XT1Click(TObject *Sender)

{//блок 2 загрузить файл в формате TXT

RichEdit1->Lines->LoadFromFile("File1.txt");

}

//---------------------------------------------------------------------------

void __fastcall TForm1::RTF1Click(TObject *Sender)

{// блок 3 загрузить файл в формате RTF

RichEdit1->Lines->LoadFromFile("File2.rtf");

}

//---------------------------------------------------------------------------

void __fastcall TForm1::RTF2Click(TObject *Sender)

{//блок 4 сохранить файл в формате RTF

RichEdit1->Lines->SaveToFile("File2.rtf");

}

//---------------------------------------------------------------------------

void __fastcall TForm1::N6Click(TObject *Sender)

{// блок 5 наименование шрифта - показать панель выбора GroupBox2

GroupBox1-> Show ();GroupBox2-> Hide ();

GroupBox1->SetFocus();

}

//---------------------------------------------------------------------------

void __fastcall TForm1::N5Click(TObject *Sender)

{//блок 6 размер шрифта - показать панель выбора GroupBox1

GroupBox1->Hide(); GroupBox2->Show();

GroupBox2->SetFocus();

}

//---------------------------------------------------------------------------

void __fastcall TForm1::ComboBox2Change(TObject *Sender)

{// блок 7 установка размера шрифта по значению ComboBox2->Text

int h;

h=StrToInt(ComboBox2->Text);

RichEdit1->SelAttributes->Size=h;

GroupBox2->Hide();

}

//---------------------------------------------------------------------------

void __fastcall TForm1::ComboBox1Change(TObject *Sender)

{// блок 8 установка наименования шрифта по значению ComboBox1->Text

RichEdit1->SelAttributes->Name=ComboBox1->Text;

GroupBox1->Hide();;//при выборе значения спрятать список выбора

}

//---------------------------------------------------------------------------

void __fastcall TForm1::FormClick(TObject *Sender)

{// блок 9 – при щелчке по форме спрятать окна выбора

GroupBox1->Hide();GroupBox2->Hide();

}

//---------------------------------------------------------------------------

Рис. 48. Листинг программы работы с компонентом RichEdit (Продолжение, далее см. на следующей странице)

void __fastcall TForm1::N8Click(TObject *Sender)

{// блок 10 стиль по умолчанию

RichEdit1->SelAttributes->Assign(RichEdit1->DefAttributes);

RichEdit1->Paragraph->Alignment=taLeftJustify;;

}

//---------------------------------------------------------------------------

void __fastcall TForm1::N7Click(TObject *Sender)

{// блок 11 свой стиль

RichEdit1->SelAttributes->Style=

RichEdit1->SelAttributes->Style<<fsBold;

RichEdit1->Paragraph->Alignment=taRightJustify;

}

//---------------------------------------------------------------------------

Рис. 48. Листинг программы работы с компонентом RichEdit (окончание, начало см. на предыдущей странице)

Вопросы для самопроверки

  1. Перечислите основные события компонента TForm, генерируемые системой при запуске программы. Укажите порядок их наступления.

  2. В чем состоят обычный и модальный способы вызова дочерних форм?

  3. Приведите примеры многостраничных форм и случаи их применения.

  4. Приведите примеры многострочных компонентов и случаи их применения.

  5. Сделайте сравнительный анализ конструкций RadioGroup и CheckListBox.

  6. Объясните логику выполнения и синтаксис оператора вызова функции MessageDlg.

  7. Объясните логику выполнения и синтаксис основных системных диалогов (OpenDialog, SaveDialog, OpenPictureDialog, SavePictureDialog) .

  8. Сделайте сравнительный анализ многострочных окон ListBox, Memo и RicgEdit.