Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Курс лекций по дисциплине Объектно-ориентирован...doc
Скачиваний:
33
Добавлен:
01.07.2025
Размер:
14 Мб
Скачать
☆

2. Перетаскивание и встраивание объектов — Drag&Doc. Плавающие окна

Начиная с Delphi 4 реализована новая технология перетаскивания и встраивания оконных объектов — Drag&Doc. Она предоставляет пользователю полную свободу в перестройке интерфейса. Отдельные окна могут объединяться вместе, создавать многостраничное окно с закладками, затем опять разъединяться и т.д. Посмотрим, как подобный подход можно реализовать в своем приложении.

У оконных компонентов введено свойство DockSite, которое по умолчанию равно false, но если его установить в true, то компонент становится контейнером: приемником, способным принимать переносимые в него компоненты — клиенты. Еще одно свойство приемника — UseDockManager. Если оно равно true (это принято по умолчанию), то процессом встраивания клиента автоматически управляет диспетчер встраивания, соответствующий тому компоненту, который является приемником. Если же задать UseDockManager равным false, то встраивание клиентов ложится на плечи программиста.

В компонентах — потенциальных клиентах надо установить свойство DragKind в dkDock. Кроме того, если вы хотите, чтобы процесс перетаскивания начинался автоматически, то, так же, как и в технологии Drag&Drop, надо установить свойство DragMode в dm Automatic. Если оставить значение DragMode равным dmManual по умолчанию, то управление процессом Drag&Doc осуществляется так же, как описывалось ранее для процесса Drag&Drop.

Посмотрим, к чему все это может привести. Давайте построим тестовое приложение, в котором осуществим Drag&Doc, не написав ни одной строчки кода.

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

Теперь посмотрим, как можно управлять из компонента-приемника всеми этими процессами.

У приемника имеется свойство DockClients, объявленное как

DockClients[Index: Integer] : TControl;

которое позволяет получить доступ к каждому клиенту, перенесенному в приемник. Свойство DockClientCount содержит число клиентов. Оба свойства — DockClients и DockClientCount только для чтения.

В технологии определены соответствующие события, которые позволяют приемнику управлять процессом встраивания. Они генерируются только в том случае, если в компоненте установлено DockSite = true.

Первое из этих событий — OnGetSitelnfo возникает в момент начала перетаскивания и повторяется непрерывно в процессе перетаскивания. Заголовок соответствующего обработчика имеет вид:

procedure TForml.PanellGetSitelnfo(Sender: TObject; DockClient: TControl; var InfluenceRect: TRect; MousePos: TPoint; var CanDock: Boolean);

В качестве параметров в обработчик передается DockClient — перетаскиваемый объект, InfluenceRect — прямоугольник рамки перетаскиваемого объекта, который можно изменять, MousePos — положение курсора мыши и CanDock — разрешение перетаскивания. Если в обработчике указать CanDock = false, это будет означать, что приемник отказывается принимать компонент.

Еще одно событие — OnDockOver возникает и повторяется в момент, когда компонент-клиент перемещается над компонентом-приемником, точнее, когда в поле приемника войдет курсор мыши, буксирующий клиента. Это событие аналогично событию OnDragOver в технологии Drag&Drop. Заголовок соответствующего обработчика имеет вид:

procedure TForml.PanellDockOver(Sender: TObject; Source: TDragDockObject; X,Y: Integer; State: TDragState; var Accept: Boolean);

В этом обработчике вы можете, как и в описанном выше, проверить, хотите ли вы, чтобы данный оконный компонент принял перетаскиваемый компонент, определяемый параметром Source. Если принимать не надо, то задается значение Accept = false.

Объект перетаскивания Source типа TDragDockObject имеет свойство DockRect типа TRect, описывающее перемещаемую рамку. Вы можете управлять ею.

Если в процессе буксировки панели Panels ее координаты выходят за пределы клиентской области приемника, то панель вытягивается вдоль соответствующего края приемника, а ее ширина равна 25. При перемещении панели внутри окна она представляется квадратом со стороной 100.

Это будет нормально срабатывать, если для приемника, например, для панели Panel1, установить UseDockManager равным false. В противном случае сначала сработает диспетчер встраивания, растянув рамку клиента, а затем наступит событие OnDockOver, так что его обработчик будет иметь дело с уже измененной рамкой, что приведет к ошибке встраивания. Поэтому надо отключить диспетчер встраивания для клиента Panel2, не отключая его для остальных клиентов. Это легко сделать, включив в обработчик события OnGetSitelnfo оператор

Panell .UseDockManager := DockClientoPanel2

Этот оператор задаст значение UseDockManager равным true для всех потенциальных клиентов, кроме Panels.

Еще одно событие, возникающее в компоненте-приемнике — OnDockDrop. Это событие возникает в момент окончания перетаскивания клиента над приемником. Оно аналогично событию OnDragDrop в технологии Drag&Drop. Заголовок соответствующего обработчика имеет вид:

procedure TForml.Panel2DockDrop(Sender: TObject;Source: TDragDockObject; X, Y: Integer);

Этот обработчик позволяет разместить клиента Source тем или иным образом в зависимости от его имени, типа, местоположения — координат X и Y. Например, в этот момент можно что-то изменить в надписях приемника, сигнализируя пользователя о приеме клиента и числе клиентов.

Событие, возникающее в компоненте-приемнике в момент, когда пользователь перетаскивает клиента из приемника — OnUnDock. Заголовок соответствующего обработчика имеет вид:

procedure TForml.PanellUnDock(Sender: TObject; Client: TControl; NewTarget: TWinControl; var Allow: Boolean);

В обработчик передаются как параметры Client — компонент, который был клиентом, NewTarget — новый приемник, в который пользователь перетаскивает клиента, и Allow — параметр, определяющий, можно ли перетащить клиента.

У оконных компонентов есть свойство Floating (только для чтения), которое показывает, является компонент свободно плавающим окном, или размещен в другом оконном компоненте-приемнике.

При переводе ранее размещенного компонента в состояние плавающего окна и при встраивании плавающего окна можно использовать свойства, в которых запоминаются размеры компонента в предыдущем состоянии:

UndockHeight - Высота компонента, которая была в последний раз, когда он отображался плавающим окном.

UndockWidth - Ширина компонента, которая была в последний раз, когда он отображался плавающим окном.

TBDockHeigh - Высота компонента, когда он в последний раз размещался в контейнере вертикально.

LRDockWidth - Ширина компонента, когда он в последний раз размещался в контейнере горизонтально.

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

В технологии существует метод ManualFloat, который переводит размещенный компонент в состояние плавающего окна. Он объявлен следующим образом:

function ManualFloat(ScreenPos: TRect): Boolean;

Параметр функции ScreenPos определяет положение и размер компонента после его перевода в состояние плавающего окна.

И последний метод, который мы рассмотрим — ManualDock, размещающий компонент в указанном приемнике. Его описание:

function ManualDock(NewDockSite: TWinControl;

DropControl: TControl = nil;

ControlSide: TAlign = alNone): Boolean;

Параметр NewDockSite определяет приемник, в котором должен размещаться клиент. Второй параметр DropControl определяет другой компонент в приемнике NewDockSite, который должен разместить данного клиента. Как правило, этот параметр задается равным nil. Параметр ControlSide определяет выравнивание клиента внутри приемника. Если это единственный клиент в приемнике, то значение этого параметра безразлично. Но если там уже есть клиенты, то задание, например, ControlSide = alLeft приведет к перестановке, если только данный клиент не самый левый в приемнике.

Имеется, однако, особенность, не оговоренная в документации: метод ManualDock срабатывает только в случае, если компонент-клиент был ранее в состоянии плавающего окна. Так что вам придется предварительно один раз перевести Рапеl в это состояние или щелчком на панели, или с помощью кнопки, соответствующей ранее рассмотренному методу ManualFloat. Другой способ обойти эту трудность автоматически — ввести соответствующий метод ManualFloat в обработчик события формы OnCreate.

И еще одно нестандартное использование методов ManualFloat и ManualDock. Пусть, например, вы хотите иметь форму, содержащую несколько панелей, которые пользователь мог бы перемещать, не переводя их в состояние плавающего окна и не изменяя размеров. Тогда вы можете в форме установить DockSite = true, сделать в панелях соответствующие установки свойств DragKind в dkDock и DragMode в dm Automatic, а в событии формы OnCreate выполнить для каждой панели методы ManualFloat и ManualDock, назначив приемником в ManualDock форму. Вы получите приложение, в котором пользователь сможет перемещать панели мышью. Это может быть полезно в каких-то приложениях, связанных с проектированием топологии или конструированием.

Контрольные вопросы:

  1. Где реализуются технологии Drag&Doc и Drag&Drop?

  2. С какого метода начинается перетаскивание информации из одного объекта в другой?

  3. Перечислите события, которые участвуют в процессе перетаскивания информации.

  4. Перечислите события, которые участвуют в процессе встраивания объектов.

  5. Какое свойство определяет режим перетаскивания?

Рекомендуемая литература:1.1, 1.3, 2.1.