Перетаскивание объектов – технология Drag&Drop

 

Список изображений – компонент ImageList (находится на странице Win32 библиотеки компонентов) представляет собой набор изображений одинаковых размеров (например, пиктограмм), на которые можно ссылаться по индексам, начинающимся с нуля. Во многих компонентах, в том числе и в компонентах отображения иерархических данных, имеются свойства, представляющие собой ссылки на компонент ImageList.

Изображения в компонент ImageList могут быть загружены в процессе проектирования с помощью редактора списков изображений. Окно редактора вызывается двойным щелчком на компоненте ImageList или щелчком правой кнопки мыши и выбором команды контекстного меню Редактор ImageList. В окне редактора можно добавить в списки изображения (кнопка Добавить), удалить изображение из списка кнопкой Удалить, очистить весь список кнопкой Очистить.

При добавлении изображения в список, которое начинается с нажатия кнопки Добавить, открывается окно открытия файлов изображений, в котором можно выбрать нужный файл. Множество изображений, размещаемых обычно на кнопках, содержится в папке …\Program Files\Common Files\Borland Shared\Images\Buttons.

Следует помнить, что размер всех изображений в списке должен быть одинаковым. Как правило, это размер, используемый для пиктограмм в меню, списках, кнопках. При добавлении в список изображений для кнопок надо иметь в виду, что они часто содержат не одно, а два и более изображений. В этих случаях после выбора имени файла изображений при щелчке на кнопке Открыть задается вопрос: “Bitmap dimensions for … are greater then imagelist dimensions. Separate into … separate bitmaps?” (“Размерность изображения … больше размерности списка. Разделить на … отдельные изображения?”). Если ответить отрицательно, то все изображения уменьшатся в горизонтальном размере и лягут как одно изображение. Использовать его в дальнейшем будет невозможно. Поэтому на заданный вопрос надо ответить положительно. Тогда загружаемая битовая матрица автоматически разделится на отдельные изображения, а затем те из них, которые не нужны, удаляют.

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

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

 

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

 

TreeView (окно дерева данных) Win32 Просмотр структуры иерархических данных в виде дерева в стиле Windows 95/98/2000
Outline (окно дерева данных) Win3.1 Просмотр структуры иерархических данных в виде дерева в стиле Windows 3.x
ListView (список данных в стиле Windows 95) Win32 Отображение в стиле папок Windows списков в виде колонок или пиктограмм

 

Компоненты TreeView и Outlineслужат для отображения иерархических данных в виде дерева, в котором пользователь может выбрать нужный ему узел или узлы. С каждым узлом дерева могут быть связаны некоторые данные. Возможности компонента TreeView несколько шире, чем компонента Outline. Поэтому изучим только компонент TreeView.

Основным свойством TreeView, содержащим информацию об узлах дерева, является Items. Свойство Items объектного типа TTreeNodes содержит список узлов дерева. Доступ к информации об отдельных узлах осуществляется через свойство Items->Item[int Index]. Например, TreeView1–> Items–>Item[0]– это узел дерева с индексом 0 (первый узел дерева). Каждый узел является объектом типа TTreeNodes, обладающим своими свойствами и методами.

 

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

Сначала рассмотрим формирование дерева в процессе проектирования.

1.Создайте для проекта приложения каталог (папку Windows), запустите C++Builder 6, создайте новый проект, назовите форму ДЕМОНСТРАЦИЯ КОМПОНЕНТА TREEVIEW_1 и командой Сохранить все сразу сохраните файл модуля и проект под разными именами.

2.Разместите на пустой форме компонент TreeView1 со страницы Win32 и просмотрите открывшиеся в Инспекторе Объектов свойства.

3.Во время проектирования формирование дерева осуществляется в окне редактора элементов компонента TreeView1 (узлов дерева). Вызовите окно двойным щелчком на компоненте или нажатием кнопки с многоточием около свойства Itemsв окне Инспектора Объектов. Окно редактора содержит две панели: Элементы и Свойства элемента. Панель Элементы содержит группу кнопок.

Кнопка Новый элемент позволяет добавить в дерево новый узел. Он будет расположен на том же уровне, на котором расположен узел, выделенный курсором в момент щелчка на кнопке Новый элемент.

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

Кнопка Удалить удаляет выделенный узел дерева.

Кнопка Загрузить позволяет загрузить структуру дерева из файла. Файл, хранящий структуру дерева – это обычный текстовый файл, содержащий тексты узлов. Уровни узлов обозначаются отступами. Например, файл проектируемого дерева должен иметь вид

Кафедра АТ

ИСУ

ИСУ-11

ИСУ-12

У

Кафедра ЭВМ

ВМ

Для каждого нового узла в панели Свойства элемента можно указать ряд свойств. Свойство Текст – это надпись, появляющаяся в дереве около данного узла. Свойства Индекс образа и Выделенный индекс определяют индекс пиктограммы, отображаемой для узла, который соответственно не выделен и выделен пользователем в данный момент. Эти индексы соответствуют списку пиктограмм (в общем случае – изображений, образов), хранящихся в отдельном компоненте ImageList. Указание на этот компонент задается в свойстве Images компонента TreeView. Индексы начинаются с нуля. Если указать индекс -1 (значение по умолчанию), пиктограммы изображаться не будут. Последнее свойство – Индекс статус-образа позволяет добавить еще одну пиктограмму в данный узел, не зависящую от состояния узла. Подобная пиктограмма может просто служить дополнительной характеристикой узла. Индекс, указанный как Индекс статус-образа, соответствует списку пиктограмм, хранящихся в отдельном компоненте ImageList, указанном в свойстве StateImages компонента TreeView.

4.Разместите на форме два компонента – ImageList1и ImageList2. Загрузите в первый компонент не менее 14 пиктограмм, во второй – не менее 7 пиктограмм.

5.Выделите компонент TreeView1. В свойстве Imagesзадайте ImageList1, а в свойстве StateImagesImageList2.

6.Спроектируйте дерево согласно содержимому приведенного выше файла, задавая для каждого узла по 3 пиктограммы. Следует учесть, что Индекс статус-образа не должен быть равным 0. Например, для первого узла можно задать индексы пиктограмм – 0, 1, 1, для второго узла – 2, 3, 2 и т.д.

7.В правом верхнем углу формы поместите кнопку Button1 и назовите ее КОНЕЦ. В обработчик щелчка кнопки впишите Close();.

8.Сохраните все и запустите приложение на выполнение. Убедитесь в работоспособности приложения.

 

Теперь рассмотрим формирование дерева во время выполнения приложения. Для этого имеется ряд методов объектов типа TTreeNodes.

 

Следующие методы позволяют вставлять в дерево новые узлы:

 

Add(TTreeNode* Node, const System::AnsiString S) Добавляет новый узел с текстом S как последний узел уровня, на котором расположен Node
AddFirst(TTreeNode* Node, const System::AnsiString S) Вставляет новый узел с текстом S как первый из узлов уровня, на котором находится Node. Индексы последую-щих узлов увеличиваются на 1
Insert(TTreeNode* Node, const System::AnsiString S) Вставляет новый узел с текстом S сразу после узла Node на тот же уро-вень. Индексы последующих узлов увеличиваются на 1
AddChild(TTreeNode* Node, const System::AnsiString S) Добавляет узел с текстом S как последний дочерний узла Node
AddChildFirst(TTreeNode* Node, const System::AnsiString S) Вставляет новый узел с текстом S как первый из дочерних узлов узла Node. Индексы последующих узлов увели-чиваются на 1

 

С каждым узлом может быть связан некоторый объект. Добавление таких узлов осуществляется методами, аналогичными приведенным выше, но содержащими в качестве параметра еще указатель на объект:

 

AddObject(TTreeNode* Node, const System::AnsiString S, void* Ptr) Добавляет новый узел с текстом Sи объектом Ptrкак последний узел уровня, на котором расположен Node
AddObjectFirst(TTreeNode* Node, const System::AnsiString S, void* Ptr) Вставляет новый узел с текстом S и объектом Ptr как первый из уз-лов уровня, на котором находится Node. Индексы последующих уз-лов увеличиваются на 1
InsertObject(TTreeNode* Node, const System::AnsiString S, void* Ptr) Вставляет новый узел с текстом S и объектом Ptr сразу после узла Node на тот же уровень. Индексы последующих узлов увеличивают-ся на 1
AddChildObject(TTreeNode* Node, const System::AnsiString S, void* Ptr) Добавляет узел с текстом Sи объ-ектом Ptr как последний дочерний узла Node
AddChildObjectFirst(TTreeNode* Node, const System::AnsiString S, void* Ptr) Вставляет новый узел с текстом S и объектом Ptr как первый из до-черних узлов узла Node. Индексы последующих узлов увеличивают-ся на 1

 

Каждый из приведенных выше методов возвращает вставленный узел.

 

1.Откройте новый проект и командой Сохранить все сразу сохраните файл модуля и проект под разными именами. Назовите форму ДЕМОНСТРАЦИЯ КОМПОНЕНТА TREEVIEW_2. На форму перенесите компоненты TreeView1, Button1 (назовите КОНЕЦButton2(назовите ЗАДАТЬ ДЕРЕВО).

2.Обработчики щелчков кнопок задайте соответственно такими:

 

void __fastcall TForm1::Button1Click(TObject *Sender)

{

Close();

}

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

 

void __fastcall TForm1::Button2Click(TObject *Sender)

{

TreeView1->Items->Clear();

TreeView1->Items->Add(NULL,"Кафедра АТ");

TreeView1->Items->AddChild(TreeView1->Items->Item[0],"ИСУ");

TreeView1->Items->AddChild(TreeView1->Items->Item[0],"У");

TreeView1->Items->Add(TreeView1->Items->Item[0],"Кафедра ЭВМ");

TreeView1->Items->AddChild(TreeView1->Items->Item[3],"ВМ");

TreeView1->Items->AddChild(TreeView1->Items->Item[1],"ИСУ-11");

TreeView1->Items->AddChild(TreeView1->Items->Item[1],"ИСУ-12");

}

 

3.Запустите приложение на выполнение и убедитесь в его работоспособности. Сохраните приложение.

 

Текст, связанный с некоторым узлом, можно найти с помощью его свойства Text. Например, TreeView1->Items->Item[1]->Text – это надпись ”ИСУ".

Объект, связанный с некоторым узлом, можно найти с помощью его свойства Data. Например, TreeView1->Items->Item[1]->Data.

Для удаления узлов есть два метода: Clear(void), очищающий все дерево, и Delete(TTreeNode* Node), удаляющий указанный узел Node и все его узлы-потомки. При удалении узлов, связанных с объектами, объекты не удаляются.

Реорганизация дерева, связанная с созданием или удалением многих узлов, может вызывать неприятное мерцание изображения. Избежать этого можно с помощью методов BeginUpdate (запрещает перерисовку дерева) иEndUpdate (разрешает перерисовку дерева). Таким образом, изменение структуры дерева может осуществляться так:

TreeView1->Items->BeginUpdate();

<операторы изменения дерева>

TreeView1->Items->EndUpdate();

Если метод BeginUpdate применен подряд несколько раз, то перерисовка дерева произойдет только после того, как столько же раз будет применен метод EndUpdate.

Свойство узла Count – это число узлов, управляемых данным, т.е. дочерних узлов, их дочерних узлов и т.п. Если значение Count равно 0, узел является листом дерева.

Важным свойством компонента TreeViewявляется Selected. Это свойство указывает узел, который выделен пользователем. Пользуясь этим свойством, можно запрограммировать операции, которые надо выполнить для выбранного пользователем узла. Если ни один узел не выделен, значение Selected равно NULL.

При выделении пользователем нового узла происходят события OnChanging (перед изменением выделения) и OnChange– после выделения. В обработчик события OnChanging передаются параметры TTreeNode* Node – узел, который выделен в данный момент, и bool &AllowChange – разрешение на перенос выделения. Если в обработчике задать AllowChange=false, то переключение выделения не произойдет. В обработчик события OnChange передается параметр TTreeNode* Node – выделенный узел. В этом обработчике предусматривают действия, которые должны производиться при выделении узла.

Ряд событий компонента TreeViewсвязан с развертыванием и свертыванием узлов. При развертывании узла происходят события OnExpanding(перед развертыванием) иOnExpanded (после развертывания). В обработчики этих событий передается параметр TTreeNode* Node – развертываемый узел. Кроме того, в обработчик OnExpanding передается параметр bool &AllowExpansion, который нужно задать равным false, чтобы запретить развертывание. При свертывании узла происходят события OnCollapsing (перед свертыванием) иOnCollapsed (после свертывания). Так же, как и в событиях, связанных с развертыванием, в обработчики передается параметр TTreeNode* Node – свертываемый узел, а в обработчик OnCollapsing дополнительно передается параметр bool &AllowCollapse, разрешающий или запрещающий свертывание.

Свойство ReadOnly компонента TreeView позволяет запретить пользователю редактировать отображаемые данные. Если редактирование разрешено, то при редактировании возникают события OnEditing иOnEdited, аналогичные рассмотренным ранее (в обработчике OnEditing параметр bool &AllowEdit позволяет запретить редактирование).

Ряд свойств компонента TreeView: ShowButtons, ShowLines, ShowRoot –позволяют отображать или убирать из дерева кнопки, показывающие раскрытия узла, линии, связывающие узлы, и корневой узел.

Свойство SortType позволяет автоматически сортировать ветви и узлы дерева. По умолчанию это свойство равно stNone, что означает, что дерево не сортируется. Если установить SortType равным stText, то узлы будут автоматически сортироваться по алфавиту. Возможно также проводить сортировку по связанным с узлами объектам Data (значение SortTypeравно stData), одновременно по тексту и объектам Data (значение SortType равно stBoth) или любым иным способом.

Для использования этих возможностей сортировки надо написать обработчик события OnCompare, в который передаются параметры TTreeNode*Node1 иTTreeNode*Node2 –сравниваемые узлы, и параметр int &Compare, в который надо заносить результат сравнения: отрицательное число, если узел Node1должен располагаться ранее Node2, нуль, если эти узлы считаются эквивалентными, и положительное число, если узел Node1 должен располагаться в дереве после Node2. Например, следующий оператор в обработчике события OnCompare обеспечивает обратный алфавитный порядок расположения узлов:

OnCompare = -AnsiStrIComp(Node1->Text.c_str(),Node2->Text.c_str());

События OnCompare наступают после задания любого значения SortType, отличного от stNone, и при изменении пользователем свойств узла (например, при редактировании им надписи узла), если значение SortTypeне равно stNone. После сортировки первоначальная последовательность узлов в дереве теряется. Поэтому последующее задание SortType=stNoneне восстанавливает начальное расположение узлов, но исключает дальнейшую генерацию событий OnCompare, т.е. автоматическую перестановку узлов, например, при редактировании их надписей. Если же требуется изменить характер сортировки или провести сортировку с учетом новых созданных узлов, то надо сначала задать значение SortType=stNone, а затем задать любое значение SortType, отличное от stNone. При этом будут сгенерированы новые обращения к обработчику событий OnCompare.

Имеются и другие возможности сортировки. Например, метод AlphaSort(void) обеспечивает алфавитную последовательность узлов независимо от значения SortType, но при отсутствии обработчика событий OnCompare (если обработчик есть, то при выполнении метода AlphaSort происходит обращение к этому обработчику). Отличие метода AlphaSort от задания значения SortType=stText заключается в том, что изменение надписей узлов приводит к автоматической пересортировке дерева только при SortType=stText.

При разработке дополнения в нашем проекте удобно использовать обработчики событий OnKeyDownиOnKeyUp. Событие OnKeyDown/OnKeyUp наступает, если компонент находится в фокусе, при нажатии/отжатии пользователем любой клавиши, включая функциональные и вспомогательные, такие, как Shift, Alt и Ctrl. В обработчики событий передаются, кроме обычного параметра Sender, указывающего на компонент, в котором произошло событие, параметры Keyи Shift. Параметр Key определяет нажатую/отжатую клавишу клавиатуры. Параметр Shift является множеством, которое может быть пустым или включать в себя следующие элементы: ssShift, ssAlt, ssCtrl(нажата клавиша Shift, Alt, Ctrl соответственно).

 

Компонент StatusBar(находится на странице Win32) представляет собой ряд панелей, отображающих полосу состояния в стиле Windows. Обычно эта полоса размещается внизу формы.

Свойство SimplePanel определяет, включает ли полоса состояния одну или множество панелей. Если SimplePanel= true, то вся полоса состояния представляет собой единственную панель, текст которой задается свойством SimpleText. Если же SimplePanel = false, то полоса состояния является набором панелей, задаваемых свойством Panels. В этом случае свойство SizeGrip определяет, может ли пользователь изменять размеры панелей в процессе выполнения приложения.

Каждая панель полосы состояния является объектом типа TStatusPanels. Свойства панелей задают специальным редактором наборов, который можно вызвать тремя способами: из Инспектора Объектов кнопкой с многоточием около свойства Panels, двойным щелчком на компоненте StatusBar или из контекстного меню, выбрав команду Panels Editor. В окне редактора можно перемещаться по панелям, добавлять новые или уничтожать существующие панели. При этом в окне Инспектора Объектов будут видны их свойства.

Основное свойство каждой панели – Text, в который заносится отображаемый в панели текст. Его можно занести в процессе проектирования, а затем можно изменять программно во время выполнения. Другое существенное свойство – Width (ширина).

Программный доступ к текстам отдельных панелей возможен через свойство Panels и его индексированное подсвойство Items. Например, оператор:

StatusBar1->Panels->Items[0]->Text=”текст 1”;

выведет текст “текст1” в первую панель.

Количество панелей полосы состояния можно определить из подсвойства Count свойства Panels. Поэтому тексты всех панелей можно очистить так:

for(int i=0; i<StatusBar1->Panels->Count; i++)

{ StatusBar1->Panels->Items[i]->Text=””;}

 

4.Вернемся к сохраненному выше проекту. Перенесем на форму компонент StatusBar1, его свойство SimplePanelустановим равным true. В код модуля добавим глобальную переменную bool ShiftPressed и обработчики событий OnChange, OnKeyDown, OnKeyUp:

 

void __fastcall TForm1::TreeView1Change(TObject *Sender, TTreeNode *Node)

{

String pn="",cn="";

TTreeNode*ParentNode=Node->Parent;

if(ParentNode) pn="..Родитель: "+ParentNode->Text;

TTreeNode*ChildNode=Node->getFirstChild();

if(ChildNode) cn="..Наследник:.."+ChildNode->Text;

Caption=pn+cn;

}

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

 

void __fastcall TForm1::TreeView1KeyDown(TObject *Sender, WORD &Key,

TShiftState Shift)

{

TTreeNode*Node=TreeView1->Selected;

switch(Key)

{ case VK_DELETE:

if(Node)

{ StatusBar1->SimpleText="Удалить узел: "+Node->Text;

Node->Delete();

} ShiftPressed=false; break;

case VK_INSERT:

if(Node)

{ StatusBar1->SimpleText="Создать узел: "+Node->Text;

TreeView1->Items->Insert(Node,"Новый узел");

} ShiftPressed=false; break;

case VK_SHIFT:

ShiftPressed=true; return;

}

TreeView1->Update();

}

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

 

void __fastcall TForm1::TreeView1KeyUp(TObject *Sender, WORD &Key,

TShiftState Shift)

{

ShiftPressed=false;

}

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

 

5.Убедитесь в работоспособности дополненного приложения, добавляя новые узлы клавишей Insert и удаляя выделенный узел клавишей Delete. Сохраните приложение.

 

Перейдем к рассмотрению процесса перемещения с помощью мыши информации из одного объекта в другой (технология Drag&Drop), коротко называемого перетаскиванием. Считается, что любой профессиональный программный продукт должен обладать этой полезной способностью.

Все свойства, методы и события, связанные с процессом перетаскивания, определены в классе TControl, являющемся прародителем всех визуальных компонентов C++Builder. Поэтому они являются общими для всех компонентов.

Начало процесса перетаскивания определяется свойством DragMode, которое может устанавливаться в процессе проектирования или программно равным dmManualили dmAutomatic. Значение dmAutomatic (автоматическое) определяет автоматическое начало процесса перетаскивания при нажатии пользователем кнопки мыши над компонентом. Значение же dmManual (ручное) говорит о том, что начало процесса перетаскивания должен определять программист. Для этого он должен в соответствующий момент вызвать метод BeginDrag. Например, он может поместить вызов этого метода в обработчик события OnMouseDown, наступающего в момент нажатия кнопки мыши. В этом обработчике он может проверить предварительно какие-то условия (режим работы приложения, нажатие тех или иных кнопок мыши и вспомогательных клавиш) и при выполнении этих условий вызвать BeginDrag.

Во время перетаскивания объекта обычный вид курсора изменяется. Пока курсор перемещается над формой или компонентами, которые не могут принять информацию из перетаскиваемого объекта, он обычно имеет тип crNoDrop: «кольцо с перемычкой». Если же курсор перемещается над компонентом, готовым принять информацию из перетаскиваемого объекта, то он приобретает вид, определяемый свойством перетаскиваемого объекта DragCursor. По умолчанию это свойство равно crDrag, что соответствует изображению «стрелка из квадрата». Подчеркнем, что вид курсора определяется свойством DragCursor перетаскиваемого объекта, а не того объекта, над которым перемещается курсор.

В процессе перетаскивания компоненты, над которыми перемещается курсор, могут сообщать о готовности принять информацию от перетаскиваемого объекта. Для этого в компоненте должен быть предусмотрен обработчик события OnDragOver, наступающего при перемещении над данным компонентом курсора, перетаскивающего некоторый объект. В этом обработчике надо проверить, может ли данный компонент принять информацию перетаскиваемого объекта, и если не может, задать значение false передаваемому в обработчик параметру Accept. По умолчанию этот параметр равен true, что означает возможность принять перетаскиваемый объект.

Значение параметра Accept, задаваемое в обработчике события OnDragOver, определяет вид курсора, перемещающегося при перетаскивании над данным компонентом. Если в компоненте не описан обработчик события OnDragOver, то считается, что данный компонент не может принять информацию перетаскиваемого объекта.

Процедура приема информации от перетаскиваемого объекта записывается в обработчике события OnDragDropпринимающего компонента. Это событие наступает, если после перетаскивания пользователь отпустил кнопку мыши над данным компонентом. В обработчик этого события передаются параметры Source (объект-источник) и Xи Yкоординаты курсора.

После завершения или прерывания перетаскивания наступает событие OnEndDrag, в обработчике которого можно предусмотреть какие-то дополнительные действия. Имеется также связанное с перетаскиванием событие OnStartDrag, которое позволяет произвести какие-то операции в начале перетаскивания. Это событие полезно при автоматическом начале перетаскивания, когда иным способом этот момент нельзя зафиксировать.

 

Вернемся к дополненному выше проекту.

6.Свойство DragMode компонента TreeView1установим равным dmAutomatic.

7.В код модуля добавим обработчики событий OnDragOver, OnDragDrop:

 

void __fastcall TForm1::TreeView1DragOver(TObject *Sender, TObject *Source,

int X, int Y, TDragState State, bool &Accept)

{

Accept=Source->ClassNameIs("TTreeView");

}

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

 

void __fastcall TForm1::TreeView1DragDrop(TObject *Sender, TObject *Source,

int X, int Y)

{

if(TreeView1->Selected==NULL) return;

THitTests HT=TreeView1->GetHitTestInfoAt(X,Y);

TNodeAttachMode AttachMode;

TTreeNode*Node=TreeView1->GetNodeAt(X,Y);

if(HT.Contains(htOnItem)) AttachMode=naAddChild;

else if(HT.Contains(htNowhere)) AttachMode=naAdd;

else if(HT.Contains(htOnIndent)) AttachMode=naInsert;

else return;

if(ShiftPressed)

{ StatusBar1->SimpleText="Копировать узел: "+ TreeView1->Selected->Text;

Node->Assign(TreeView1->Selected);

}

else

{ StatusBar1->SimpleText="Переместить узел: "+ TreeView1->Selected->Text;

TreeView1->Selected->MoveTo(Node,AttachMode);

}

TreeView1->Update();

}

 

8.Убедитесь в работоспособности дополненного приложения, используя для перемещения узлов левую кнопку мыши, а для копирования узлов – левую кнопку мыши и нажатую клавишу Shift.

 

Компонент ListView позволяет отображать в стиле Windows данные в виде списков, таблиц, крупных и мелких пиктограмм. Стиль отображения определяется свойством ViewStyle, которое может устанавливаться в процессе проектирования или программно во время выполнения. Свойство может принимать значения: vsIcon– крупные значки, vsSmallIcon – мелкие значки, vsList – список, vsReport – таблица.

 

Рассмотрим использование компонента ListViewв процессе проектирования.

1.Создайте новый проект, назовите форму ДЕМОНСТРАЦИЯ КОМПОНЕНТА LISTVIEW и командой Сохранить все сразу сохраните файл модуля и проект под разными именами.

2.Разместите на форме два компонента – ImageList1и ImageList2. Загрузите в первый компонент не менее 11 пиктограмм, во второй – не менее 2 пиктограмм.

3.Разместите на пустой форме компонент ListView1 со страницы Win32 и просмотрите открывшиеся в Инспекторе Объектов свойства.

4.Основное свойство компонента, описывающее состав отображаемой информации – Items. Рядом с этим свойством в окне Инспекторе Объектов имеется кнопка с многоточием, щелчком по которой вызовем Редактор элементов ListView.

5.В редакторе задайте структуру:

ИСУ

ИСУ-11

ИСУ-12

У

У-11

У-12

У-13,

устанавливая индексы образа равными 0, 1, …6, а индексы статус-образа (для узлов ИСУ и У) – 0 и 1. Для перехода из одного окна в другое пользуйтесь клавишей Tab.

6.Выделите ListView1. Свойствам LargeImages и SmallImages задайте значение ImageList1, а свойству StateImagesImageList2.

7.Свойство Columns определяет список заголовков таблицы в режиме vsReport. Двойным щелчком но компоненте ListView1 или щелчком на кнопке с многоточием рядом со свойством Columnsвызовите редактор заголовков (Правка List). Крайняя левая кнопка позволяет добавить новую секцию в заголовок, вторая слева – удалить секцию, а две кнопки со стрелками позволяют изменять последовательность секций. После того, как секция добавлена и на ней установлен курсор, в окне Инспектора Объектов появится множество свойств этого объекта. В свойстве Caption последовательно задавайте тексты секций заголовка – специальность, группы 1, группы 2, группы 3, а в свойстве ImageIndexукажите индексы пиктограмм, которые появятся перед текстами секций заголовка – 7, 8, 9, 10 соответственно. Свойства MinWidth и MaxWidth определяют соответственно минимальную и максимальную ширину заголовка в пикселах. Только в этих пределах пользователь может изменять ширину заголовка курсором мыши. Для всех заголовков значение MaxWidth поставьте равным 300. Значение ширины по умолчанию задается значением свойства Width.

8.Выполните приложение при значениях свойства ViewStyle: vsIcon– крупные значки, vsSmallIcon – мелкие значки, vsList – список, vsReport – таблица. Курсором мыши поварьируйте шириной заголовков. Сохраните приложение для последующего дополнения.

 

Дополненное приложение предоставит пользователю возможность изменять вид списка в окне ListView1и, кроме того, позволит при стилях vsIconи vsSmallIcon перетаскивать пиктограммы мышью в любое место окна.

 

9.На форму сохраненного приложения со страницы Стандарт перенесите компонент MainMenu1. Это, как и ImageList, невизуальный компонент, т.е. место его размещения на форме в процессе проектирования не имеет никакого значения для пользователя – во время выполнения приложения он увидит не сам компонент, а только меню, сгенерированное им.

10.Основное свойство компонента MainMenu1 – Items. Его заполнение производится с помощью Конструктора Меню, вызываемого двойным щелчком на компоненте MainMenuили нажатием кнопки с многоточием рядом со свойством Items в окне Инспектора Объектов. В открывшемся окне нужно спроектировать меню с разделами Крупные значки (имя MIcon), Мелкие значки (имя MSmallIcon), Список (имя MList), Таблица (имя MReport). Название раздела (курсив) заносится в свойство Caption, а имя – в свойство Nameсоответствующего раздела. Новые разделы можно вводить, помещая курсор в рамку из точек, обозначающую место расположения нового раздела. Если раздел ввелся не на нужном месте, его можно отбуксировать мышью в нужное место.

11.Установите во всех разделах одинаковый, не равный нулю, индекс GroupIndexи свойства RadioItemв true, чтобы обеспечить работу каждого из разделов в режиме радиокнопки с другими разделами.

12.Впишите обработчики щелчков для разделов меню и для событий OnDragOver, OnDragDrop компонента ListView1:

 

void __fastcall TForm1::MIconClick(TObject *Sender)

{

ListView1->ViewStyle=vsIcon;

MIcon->Checked=true;

ListView1->DragMode=dmAutomatic;

}

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

 

void __fastcall TForm1::MSmallIconClick(TObject *Sender)

{

ListView1->ViewStyle=vsSmallIcon;

MSmallIcon->Checked=true;

ListView1->DragMode=dmAutomatic;

}

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

 

void __fastcall TForm1::MListClick(TObject *Sender)

{

ListView1->ViewStyle=vsList;

MList->Checked=true;

ListView1->DragMode=dmAutomatic;

}

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

 

void __fastcall TForm1::MReportClick(TObject *Sender)

{

ListView1->ViewStyle=vsReport;

MReport->Checked=true;

ListView1->DragMode=dmAutomatic;

}

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

 

void __fastcall TForm1::ListView1DragOver(TObject *Sender, TObject *Source,

int X, int Y, TDragState State, bool &Accept)

{

Accept=(Source=ListView1);

}

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

 

void __fastcall TForm1::ListView1DragDrop(TObject *Sender, TObject *Source,

int X, int Y)

{

((TListView*)Sender)->Selected->Position=Point(X,Y);

}

 

13.Выполните построенное приложение. Убедитесь, что с помощью меню можно изменять вид списка в окне ListView1и, кроме того, при стилях vsIconи vsSmallIcon можно перемещать пиктограммы мышью в любое место окна.

14.Метод Arrange:

void _fastcall Arrange(TListArrangement Code);

позволяет упорядочить пиктограммы в режимах vsIconи vsSmallIcon. Параметр Code определяет способ упорядочивания:

 

arAlignBottom выравнивание вдоль нижнего края области
arAlignLeft выравнивание вдоль левого края области
arAlignRight выравнивание вдоль правого края области
arAlignTop выравнивание вдоль верхнего края области
arDefault выравнивание (по умолчанию) вдоль верхнего края области
arSnapToGrid размещение каждой пиктограммы в ближайшем узле сетки

 

Добавьте в меню раздел Выравнивание и в обработчик щелчка на нем запишите оператор:

ListView1->Arrange(arAlignTop);

Тогда после перемещения элементов всегда можно упорядочить их расположение, выбрав этот раздел меню.