Стандартные технологии программирования


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

  •  интерфейс переноса Drag-and-Drop;
  •  механизм Drag-and-Dock;
  •  усовершенствованное масштабирование;
  •  как правильно передавать фокус между элементами управления;
  •  управление мышью;
  •  ярлыки.

 

Интерфейс переноса Drag-and-Drop


Интерфейс переноса и приема компонентов появился достаточно давно. Он обеспечивает взаимодействие двух элементов управления во время выполнения приложения. При этом могут выполняться любые необходимые операции. Несмотря на простоту реализации и давность разработки, многие программисты (особенно новички) считают этот механизм малопонятным и экзотическим. Тем не менее использование Drag-and-Drop может оказаться очень полезным и простым в реализации. Сейчас мы в этом убедимся.
Для того чтобы механизм заработал, требуется настроить соответствующим образом два элемента управления. Один должен быть источником (Source), второй — приемником (Target). При этом источник никуда не перемещается, а только регистрируется в качестве такового в механизме.
Примечание 
Один элемент управления может быть одновременно источником и приемником.
Пользователь помещает указатель мыши на нужный элемент управления, нажимает левую кнопку мыши и, не отпуская ее, начинает перемещать курсор ко второму элементу. При достижении этого элемента пользователь отпускает кнопку мыши. В этот момент выполняются предусмотренные разработчиком действия. При этом первый элемент управления является источником, а второй — приемником.
После выполнения настройки механизм включается и реагирует на перетаскивание мышью компонента-источника в приемник. Группа методов-обработчиков обеспечивает контроль всего процесса и служит для хранения исходного кода, который разработчик сочтет нужным связать с перетаскиванием. Это может быть передача текста, значений свойств (из одного редактора в другой можно передать настройки интерфейса, шрифта и сам текст); перенос файлов и изображений; простое перемещение элемента управления с места на место и т. д. Пример реализации Drag-and-Drop в Windows — возможность переноса файлов и папок между дисками и папками.
Как видите, можно придумать множество областей применения механизма Drag-and-Drop. Его универсальность объясняется тем, что это всего лишь средство связывания двух компонентов при помощи указателя мыши. А конкретное наполнение зависит только от фантазии программиста и поставленных задач.
Весь механизм Drag-and-Drop реализован в базовом классе TControl, который является предком всех элементов управления. Рассмотрим суть механизма.
Любой элемент управления из Палитры компонентов Delphi является источником в механизме Drag-and-Drop. Его поведение на начальном этапе переноса зависит от значения свойства
type TDragMode = (dmManual, dmAutomatic); 
property DragMode: TDragMode;
Значение dmAutomatic обеспечивает автоматическую реакцию компонента на нажатие левой кнопки мыши и начало перетаскивания — при этом механизм включается самостоятельно.
Значение dmManual (установлено по умолчанию) требует от разработчика обеспечить включение механизма вручную. Этот режим используется в том случае, если компонент должен реагировать на нажатие левой кнопки мыши как-то иначе. Для инициализации переноса используется метод
procedure BeginDrag(Immediate: Boolean; 
Threshold: Integer = -1);
Параметр immediate = True обеспечивает немедленный старт механизма. При значении False механизм включается только при перемещении курсора на расстояние, определенное параметром Threshold.
О включении механизма сигнализирует указатель мыши — он изменяется на курсор, определенный в свойстве
property DragCursor: TCursor;
Еще раз напомним, что источник при перемещении курсора не изменяет собственного положения, и только в случае успешного завершения переноса сможет взаимодействовать с приемником.
Приемником может стать любой компонент, в котором создан метод-обработчик
procedure DragOver(Source: TObject; X, Y: Integer; State: TDragState; 
var Accept: Boolean);
Он вызывается при перемещении курсора в режиме Drag-and-Drop над этим компонентом. В методе-обработчике можно предусмотреть селекцию источников переноса по нужным атрибутам.
Если параметр Accept получает значение True, то данный компонент становится приемником. Источник переноса определяется параметром source. Через этот параметр разработчик получает доступ к свойствам и методам источника. Текущее положение курсора задают параметры X и Y. Параметр state возвращает информацию о характере движения мыши:
type TDragState = (dsDragEnter, dsDragLeave, dsDragMove);
dsDragEnter — указатель появился над компонентом; dsDragLeave — указатель покинул компонент; dsDragMove — указатель перемещается по компоненту.
Приемник должен предусматривать выполнение некоторых действий в случае, если источник завершит перенос именно на нем. Для этого используется метод-обработчик
type TDragDropEvent = procedure(Sender, Source: TObject; X, Y: Integer)
of object;
property OnDragDrop: TDragDropEvent;
который вызывается при отпускании левой кнопки мыши на компоненте-приемнике. Доступ к источнику и приемнику обеспечивают параметры Source и Sender соответственно. Координаты мыши возвращают параметры X и Y.
При завершении переноса элемент управления — источник — получает соответствующее сообщение, которое обрабатывается методом
type TEndDragEvent = procedure(Sender, Target: TObject; X, Y: Integer)
of object;
property OnEndDrag: TEndDragEvent;
Источник и приемник определяются параметрами Sender и Target соответственно. Координаты мыши определяются параметрами X и Y.
Для программной остановки переноса можно использовать метод EndDrag источника (при обычном завершении операции пользователем он не используется):
procedure EndDrag(Drop: Boolean);
Параметр Drop = True завершает перенос. Значение False прерывает перенос.
Теперь настало время закрепить полученные знания на практике. Рассмотрим небольшой пример. В проекте DemoDragDrop на основе механизма Drag-and-Drop реализована передача текста между текстовыми редакторами и перемещение панелей по форме (1).
Листинг 27.1. Секция implementation модуля главной формы проекта DemoDragDrop
implementation
{$R *.DFM)
procedure TMainForm.EditlMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, У: Integer);
begin if Button = mbLeft
then TEdit(Sender).BeginDrag(True); 
end;
procedure TMainForm.Edit2DragOver(Sender, Source: TObject; X, Y: Integer;
State: TDragState; var Accept: Boolean);
 begin
 if Source is TEdit
then Accept := True
else Accept :<= False; 
end;
procedure TMainForm.Edit2DragDrop(Sender, Source: TObject; X, Y:
Integer);
begin
TEdit(Sender).Text := TEdit(Source).Text;
TEdit(Sender).SetFocus;
TEdit(Sender).SelectAll;
 end;
procedure TMainForm.EditlEndDrag(Sender, Target: TObject; X, Y: Integer); 
begin if Assigned(Target)
then TEdit(Sender).Text := 'Текст перенесен в ' + TEdit(Target).Name; 
end;
procedure TMainForm.FormDragOver(Sender, Source: TObject; X, Y: Integer;
State: TDragState; var Accept: Boolean); begin if Source.ClassName = 'TPanel'
then Accept := True
else Accept := False; 
end;
procedure TMainForm.FormDragDrop(Sender, Source: TObject; X, Y: Integer); 
begin
TPanel(Source).Left := X;
TPanel(Source).Top := Y;
 end;
end.
Для однострочного редактора Edit1 определены методы-обработчики источника. В методе EditiMouseDown обрабатывается нажатие левой кнопки мыши
и включается механизм переноса. Так как свойство DragMode для Edit1 имеет значение dmManual, то компонент без проблем обеспечивает получение фокуса и редактирование текста.
Метод EditiEndDrag обеспечивает отображение информации о выполнении переноса в источнике.
Для компонента Edit2 определены методы-обработчики приемника. Метод Edit2DragOver проверяет класс источника и разрешает или запрещает прием.
Метод Edit2DragDrop осуществляет перенос текста из источника в приемник.
Примечание
Обратите внимание, что оба компонента TEdit одновременно являются источниками и приемниками. Для этого каждый из них использует методы-обработчики другого. А исходный код методов настроен на обработку владельца как экземпляра класса TEdit.
Форма, как приемник Drag-and-Drop, обеспечивает перемещение панели Panel2, которая выступает в роли источника. Метод FormDragOver запрещает прием любых компонентов, кроме панелей. Метод FormDragDrop осуществляет перемещение компонента.
Панель не имеет своих методов-обработчиков, т. к. работает в режиме dmAutomatic и не нуждается в дополнительной обработке завершения переноса.
Интерфейс присоединения Drag-and-Dock
Эта возможность появилась в Delphi 4. Она "подсмотрена" опять-таки у разработчиков из Microsoft, внедривших плавающие панели инструментов в MS Office, Internet Explorer и другие продукты (2).
Речь идет о том, что ряд элементов управления (а конкретно — потомки класса xwinControl) могут служить носителями (доками) для других элементов управления с возможностью их динамического перемещения из одного дока в другой при помощи мыши. Перетаскивать можно практически все — от статического текста до форм включительно. Пример использования техники Drag-and-Dock дает сама среда разработки Delphi — с ее помощью можно объединять на экране различные инструменты, такие как Инспектор объектов и Менеджер проекта.
Как и в случае с технологией перетаскивания Drag-and-Drop, возможны два варианта реализации техники Drag-and-Dock: автоматический и ручной. В первом случае дело сводится к установке нужных значений для нескольких свойств, а остальную часть работы берет на себя код VCL; во втором, как следует из названия, вся работа возлагается на программиста.
Итак, что же нужно сделать для внедрения Drag-and-Dock? В Инспекторе объектов необходимо изменить значение свойства DragKind на dkDock, a свойства DragMode — на dmAutomatic. Теперь этот элемент управления можно перетаскивать с одного носителя-дока на другой.
Носителем других компонентов (доком) может служить потомок TwinControl. У него есть свойство Docksite, установка которого в True разрешает перенос на него других компонентов. Если при этом еще и установить свойство AutoSize в True, док будет автоматически масштабироваться в зависимости от того, что на нем находится. В принципе, этими тремя операциями исчерпывается минимальный обязательный набор.
Естественно, для программиста предусмотрены возможности контроля за этим процессом. Каждый переносимый элемент управления имеет два события, возникающие в моменты начала и конца переноса:
type TStartDockEvent = procedure(Sender: TObject; 
var DragObject: TDragDockObject) of object;
TEndDragEvent = procedure(Sender, Target: TObject; X, Y: Integer) of object;
В первом из методов sender — это переносимый объект, a DragObject — специальный объект, создаваемый на время процесса переноса и содержащий его свойства. Во втором sender — это также переносимый объект, a Target — объект-док.
Док тоже извещается о событиях во время переноса:
type TGetSitelnfoEvent = procedure(Sender: TObject; DockClient: TControl;
var InfluenceRect: TRect; MousePos: TPoint; 
var CanDock: Boolean)
of object;
TDockOverEvent = procedure(Sender: TObject; Source: TDragDockObject;
X, Y: Integer; State: TDragState; var Accept: Boolean) of object;
TDockDropEvent = procedure(Sender: TObject;
 Source: TDragDockObject;
X, Y: Integer) of object;
TUnDockEvent = procedure(Sender: TObject; Client: TControl; NewTarget:
TWinControl; var Allow: Boolean) of object;
Как только пользователь нажал кнопку мыши над переносимым компонентом и начал сдвигать его с места, всем потенциальным докам (компонентам, свойство которых Docksite установлено в True) рассылается событие onGetsiteinfo. С ним передаются параметры: кто хочет "приземлиться" (параметр Dockclient) и где (MousePos). В ответ док должен сообщить решение, принимает он компонент (параметр CanDock) и предоставляемый прямоугольник (infiuenceRect) или нет. При помощи этого события можно принимать только определенные элементы управления, как показано в примере:
procedure TForml.PanellGetSitelnfо(Sender: TObject; DockClient: TControl; var InfiuenceRect: 
TRect; MousePos: TPoint; var CanDock: Boolean); 
begin
if DockClient is TBitBtn then CanDock := False; 
end;
Два последующих события в точности соответствуют своим аналогам из механизма переноса Drag-and-Drop). Событие onDockOver происходит при перемещении перетаскиваемого компонента над доком, OnDockDrop — в момент его отпускания. Наконец, onUnDock сигнализирует об уходе компонента с дока и происходит в момент его "приземления" в другом месте.
Между доком и содержащимися на нем элементами управления есть двусторонняя связь. Все "припаркованные" элементы управления содержатся в векторном свойстве Dockclients, а их количество можно узнать из свойства
DockClientCount:
s : = ' ' ;
for i := 0 to Panell.DockClientCount-1 
do AppendStr(s,Panell.DockClients[i].Name+#$D#$A); ShowMessage(s) ;
С другой стороны, если элемент управления находится на доке, то ссылка на док располагается в свойстве HostDocksite. С ее помощью можно установить, где находится элемент, и даже поменять свойства дока:
procedure TMyForm.ButtonlEndDock(Sender, Target: TObject; X, Y: Integer); begin
(Sender as TControl).HostDockSite.SetTextBuf(pChar((Sender as TControl).Name));
end;
Компоненты можно не только переносить с одного дока на другой, но и отпускать в любом месте. Хотя сам по себе компонент TControl и его потомки не являются окнами Windows, но специально для этого случая создается окно-носитель. Свойство FloatingDockSiteClass как раз и определяет класс создаваемого окна. По умолчанию для большинства компонентов значение этого свойства равно TCustomDockForm. Это — форма, которая обладает свойствами дока и создается в момент отпускания элемента управления вне других доков. Внешне она ничем не отличается от обычной стандартной формы. Если вы хотите, чтобы ваша плавающая панель инструментов выглядела по- особенному, нужно породить потомка от класса TCustomDockForm и связать свойство FloatingDockSiteCiass с этим порожденным классом:
TMyCustomFloatingForm = class(TCustomDockForm)
 public
constructor Create(AOwner: TComponent);
 override;
 end;
constructor TMyCustomFloatingForm.Create(AOwner: TComponent};
 begin
inherited Create(AOwner);
BorderStyle := bsNone;
 end;
procedure TForml.FormCreate(Sender: TObject);
 begin
ToolBarl.FioatingDockSiteCiass := TMyCustomFloatingForm; end;
В этом примере решена типовая задача — сделать так, чтобы несущее окно плавающей панели инструментов не содержало заголовка. Внешний вид таких панелей приведен на 3.
Переносить компоненты можно не только с помощью мыши, но и программно. Для этого есть пара методов ManualDock и ManualFioat. В приводимом ниже примере нажатие кнопки с именем BitBtnl переносит форму custForm на док MainForm.Paneil и размещает ее по всей доступной площади (параметр выравнивания alclient). Нажатие кнопки BitBtn2 снимает эту форму с дока и выравнивает ее по центру экрана. В свойствах UndockHeight и undockwidth хранятся высота и ширина элемента управления на момент, предшествующий помещению на док:
procedure TMainForm.BitBtnlClick(Sender: TObject);
 begin
GustForm.ManualDock
(MainForm.Pane11,nil,alClient);
 end;
procedure TMainForm.BitBtn2Click(Sender: TObject);
 begin
with CustForm do 
begin ManualFloat(Rect((Screen.Width-UndockWidth) div 2,
(Screen.Height-UndockHeight) div 2, (Screen.Width+UndockWidth) div 2, (Screen.Height+UndockHeight) div 2) );
 end;
Полное рассмотрение внутреннего устройства механизмов Drag-and-Dock потребовало бы расширения объема этой главы. Тем, кто хочет использовать их на все 100%, рекомендуем обратиться к свойствам useDockManager и DockManager. Последнее представляет собой СОМ-интерфейс, позволяющий расширить возможности дока, вплоть до записи его состояния в поток (класс TStream).

Усовершенствованное масштабирование


В класс TControl добавлены свойства, позволяющие упростить масштабирование форм и находящихся на них компонентов.
Свойство Anchors:
TAnchorKind = (akLeft, akTop, akRight, akBottom);
TAnchors = set of TAnchorKind; property Anchors: TAnchors;
отвечает за привязку компонентов к определенным краям формы при масштабировании. По умолчанию любой компонент привязан к верхней и левой сторонам ([akLeft, akTop]), т. е. не двигается при стандартном масштабировании. Но, изменив значение этого свойства, можно сделать так, чтобы компонент находился, к примеру, все время в нижнем правом углу.
С другой стороны, если прикрепить все четыре стороны, то получится интересный и нужный во многих случаях эффект. Такой компонент увеличивается и уменьшается вместе с формой; но в то же время сохраняется расстояние до всех четырех ее краев.
Свойство constraints представляет собой набор ограничений на изменение размеров компонента. Оно содержит четыре свойства: MaxHeight, Maxwidth, MinHeight и Minwidth. Как легко догадаться из названий, размеры компонента могут меняться только в пределах значений этих четырех свойств.
Наконец, большинство элементов управления получили свойство Autosize, позволяющее им автоматически масштабироваться при изменении содержимого (скажем, надписи на кнопке).
 
Управление фокусом
В процессе работы приложения тот или иной элемент управления получает фокус ввода в зависимости от действий пользователя. Очень часто передача фокуса между элементами управления должна быть упорядочена. Например, при вводе данных в приложениях баз данных пользователь должен иметь максимум удобств для обеспечения хорошей производительности труда. Для этого он должен работать только с клавиатурой, не отвлекаясь на лишние операции по передаче фокуса в нужный компонент при помощи мыши.
Для решения подобного рода проблем все оконные элементы управления имеют два свойства. Свойство TabOrder определяет порядок передачи фокуса между элементами управления одного владельца (формы, панели, группы) при нажатии клавиши <Таb>. Значение 0 имеет компонент, который будет получать фокус при открытии формы.
Для того чтобы свойство TabOrder работало, свойство Tabstop должно иметь значение True.
Кроме этого, все кнопки (произошедшие от TButtonControl) имеют свойство Default, которое при значении True заставляет кнопку реагировать на нажатие клавиши <Enter> как на щелчок на кнопке, даже если она не имеет фокус. Только одна кнопка на форме может иметь это свойство установленным.
Для передачи фокуса любому оконному элементу управления программными средствами можно использовать метод
procedure SetFocus; virtual;
унаследованный от класса TwinControl.
При необходимости работы в форме применяется метод
function SetFocusedControl(Control: TWinControl): Boolean; virtual;
класса TForm, в параметре указывается указатель на компонент, принадлежащий форме.
Управление мышью
Каждый элемент управления обладает набором свойств и методов, обеспечивающих управление мышью. Понятно, что это важный и нужный механизм. Рассмотрим кратко его устройство.
Воздействие мышью на интерфейсные элементы приложения разработчик может отслеживать при помощи целой группы методов-обработчиков.
На нажатие кнопки мыши реагирует метод
type
TMouseEvent = procedure (Sender: TObject; 
Button: TMouseButton;
Shift: TShiftState; X, Y: Integer) of object;
 property OnMouseDown: TMouseEvent;
В параметре Button передается признак нажатой кнопки:
type TMouseButton = (mbLeft, mbRight, mbMiddle);
Параметр shift определяет нажатие дополнительной клавиши на клавиатуре:
type TShiftState = set of (ssShift, ssAlt, ssCtrl, ssLeft, ssRight, ssMiddle, ssDouble);
Параметры х и у возвращают координаты курсора.
На отпускание кнопки мыши реагирует метод:
type
TMouseEvent = procedure (Sender: TObject;
 Button: TMouseButton;
Shift: TShiftState; X, Y: Integer) of object; 
property OnMouseUp: TMouseEvent;
Его параметры описаны выше.
При перемещении мыши можно вызывать метод-обработчик
TMouseMoveEvent = procedure(Sender: TObject;
 Shift: TShiftState; X, Y: Integer) of object;
property OnMouseMove: TMouseMoveEvent;
Если у разработчика нет необходимости так подробно отслеживать состояние мыши, можно воспользоваться двумя другими методами:
property OnClick: TNotifyEvent; 
property OnDblClick: TNotifyEvent;
Первый реагирует на щелчок кнопкой, второй — на двойной щелчок.
Каждый элемент управления может изменять внешний вид указателя мыши, перемещающейся над ним. Для этого используется свойство
property Cursor: TCursor;
Для управления дополнительными возможностями мыши для работы в Internet (ScrollMouse) предназначены три метода обработчика, реагирующие на прокрутку:

  •  property OnMouseWheel: TMouseWheelEvent;

 вызывается при прокрутке;

  •  property OnMouseWheelUp: TMouseWheelUpDownEvent;

вызывается при прокрутке вперед;

  •  property OnMouseWheelDown: TMouseWheelUpDownEvent;

вызывается при прокрутке назад.
В VCL имеется класс TMouse, содержащий свойства мыши, установленной на компьютере. Обращаться к экземпляру класса, который создается автоматически, можно при помощи глобальной переменной Mouse. Свойства класса представлены в табл. 27.1.
В качестве примера обработки управляющих воздействий от мыши рассмотрим пример DemoMouse. Он очень прост. Перемещение мыши с нажатой левой кнопкой обеспечивает выделение прямоугольного фрагмента. Такую функцию вы можете наблюдать в любом графическом редакторе, а исходный код проекта использовать в собственных разработках (листинг 27.2).
Таблица 27.1. Свойства и методы класса mouse


Объявление

Тип

Описание

property Capture: HWND;

Pu

Дескриптор элемента управления, над которым находится мышь

property CursorPos: TPoint;

Pu 

Содержит координаты указателя мыши

property Draglmmediate: Boolean;

Ro
 

При значении True реакция на нажатие выполняется немедленно

property DragThreshold: Integer;

Ro

Задержка реакции на нажатие

property MousePresent: Boolean;

Ro

Определяет наличие мыши

type UINT = LongWord; property RegWheelMessage: UINT;

Ro

Задает сообщение, посылаемое при прокрутке в ScrollMouse

property WheelPresent: Boolean;

Ro
 

Определяет наличие ScrollMouse

property WheelScrollLines : Integer;

Ro

Задает число прокручиваемых линий

 Листинг 27.2. Модуль главной формы проекта DemoMouse  
unit Main; 
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
ExtCtrls, ComCtrls;
type
TMainForm = class(TForm) ColorDlg: TColorDialog; 
StatusBar: TStatusBar; Timer: TTimer;
 procedure FormMouseDown(Sender: TObject;
 Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
procedure FormMouseUp(Sender: TObject;
 Button: TMouseButton;
 Shift: TShiftState; X, Y: Integer);
procedure FormMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
procedure TimerTimer(Sender: TObject);
 private
MouseRect: TRect;
IsDown: Boolean;
RectColor: TColor;
 public
{ Public declarations }
 end;
var
MainForm: TMainForm;
implementation {$R *.DFM}
procedure TMainForm.FormMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
 begin
if Button = mbLeft then with MouseRect do
 begin
IsDown := True; Left := X; Top := Y; Right := X; Bottom := Y;
Canvas.Pen.Color := RectColor; 
end;
if (Button = mbRight) and ColorDlg.Execute then RectColor := ColorDlg.Color;
 end;
procedure TMainForm.FormMouseUp(Sender: TObject; 
Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
IsDown := False;
Canvas.Pen.Color := Color;
with MouseRect do
Canvas.Polyline([Point(Left, Top), Point(Right, Top), Point(Right,
Bottom), Point(Left, Bottom), Point(Left, Top)]);
with StatusBar do
begin
Panels[4].Text := ''; Panels [5] .Text := ";
 end;
 end;
procedure TMainForm.FonnMouseMove(Sender: TObject; Shift: TShiftState; X,
Y: Integer);
 begin
with StatusBar do
 begin
Panels[2].Text := 'X: ' + IntToStr(X);
Panels[3].Text := 'Y: ' + IntToStr(Y);
 end;
if Not IsDown then Exit; Canvas.Pen.Color := Color; with mouserect do 
begin
Canvas.Polyline([Point(Left, Top), Point(Right, Top),
Point(Right, Bottom), Point(Left, Bottom), Point(Left, Top)]);
Right := X;
Bottom := Y;
Canvas.Pen.Color := RectColor;
Canvas.Polyline([Point(Left, Top), Point(Right, Top),
Point(Right, Bottom), Point(Left, Bottom), Point(Left, Top)]);
 end;
with StatusBar do begin
Panels [4] .Text := 'IHwpMHa: ' + IntToStr(Abs(MouseRect.Right - MouseRect.Left));
Panels[5].Text := 'BacoTa: ' + IntToStr(Abs(MouseRect.Bottom - MouseRect.Top));
end; end;
procedure TMainForm.TimerTimer(Sender: TObject); 
begin
with StatusBar do 
begin
Panels[0].Text := 'flaTa: ' + DateToStr(Now); Panels[1].Text := 'BpeMH: ' + TimeToStr(Now);
 end; 
end;
end.
При нажатии левой кнопки мыши в методе-обработчике FormMouseDown включается режим рисования прямоугольника (isDown := True) и задаются его начальные координаты.
При перемещении мыши по форме проекта вызывается метод-обработчик FormMouseMove, в котором координаты курсора и размеры прямоугольника передаются на панель состояния. Если левая кнопка мыши нажата (isDown = True), то осуществляется перерисовка прямоугольника.
При отпускании кнопки мыши в методе FormMouseUp рисование прямоугольника прекращается (isDown := False).
Если была нажата правая кнопка мыши, то метод-обработчик FormMouseDown обеспечивает отображение диалога выбора цвета, который позволяет сменить цвет линий прямоугольника.
Метод-обработчик TimerTimer обеспечивает отображение на панели состояния текущей даты и времени.
Примечание
Для рисования прямоугольника использовался метод PolyLine, который работает при перемещении курсора влево и вверх относительно начальной точки. Для стирания старого прямоугольника желательно использовать режимы XOR и NOTXOR, которые обеспечивают восстановление рисунка под линией. Подробно об этом см. гл. 10.
 
Ярлыки
Пользовательский интерфейс трудно представить без ярлычков с оперативной подсказкой (Hints). Если задержать курсор, например, над кнопкой или компонентом палитры самой среды Delphi, появляется маленький прямоугольник яркого цвета (окно подсказки), в котором одной строкой сказано о названии этого элемента или связанном с ним действии. Delphi поддерживает механизмы создания и отображения таких ярлычков в создаваемых программах.
Свойство, определяющее активность системы подсказки у элемента управления:
property ShowHint: Boolean;
Если свойство ShowHint установлено в True, и во время выполнения курсор задержался над компонентом на некоторое время, в окне подсказки высвечивается текстовая строка с подсказкой, которая задана свойством:
property Hint: string;
Подсказка компонента может быть пустой строкой — в этом случае система ищет в цепочке первый родительский компонент с непустой подсказкой.
Если в строке Hint встречается специальный символ-разделитель "Г, то часть строки до него ("короткая") передается в окно подсказки, а после ("длинная") — присваивается свойству Hint объекта Application. Ее можно использовать, например, в строке состояния внизу главной формы приложения (см. пример ниже).
Система оперативных подсказок имеет свойства и методы, общие для всех форм в приложении. Не удивительно, что они сосредоточены в Application — глобальном объекте, соответствующем работающему приложению. Все описанные ниже в этом разделе свойства относятся не к компоненту, показывающему подсказку, а именно к Application.
Фоновый цвет окна подсказки можно изменить посредством свойства
property HintColor: TColor;
У объекта Application значение свойства showHint нужно устанавливать во время выполнения, например, в обработчике OnCreate главной формы приложения. Оно является главенствующим для всей системы подсказок: если оно установлено в значение False, ярлычки не возникают.
Есть еще один способ получения подсказки. При смене текущего элемента управления (т. е. при смене текста в свойстве Hint) в объекте Application возникает событие
property OnHint: TNotifyEvent; 
Пример:
procedure TForml.AppHint(Sender: TObject);
 begin
Panell.Caption:= Application.Hint;:
 end;
procedure TForml.FormCreate(Sender: TObject);
 begin
Application.OnHint := AppHint; 
end;
В этом примере текст подсказки будет отображаться в строке состояния Panell независимо от значения showHint у любого объекта — лишь бы этот текст был в наличии. Для этого разделяйте подсказку у элементов управления вашего приложения на две части при помощи символа " |" — краткая информация появится рядом с элементом, а более полная — в строке состояния.
function GetLongHint(const Hint: string): string; function GetShortHint(const Hint: string): string;
У других компонентов свойство ShowHint интерпретируется системой так: когда курсор мыши останавливается над элементом управления или пунктом меню, и приложение не занято обработкой сообщения, происходит проверка, и если свойство showHint у элемента или у одного из его родительских элементов в иерархии равно True, то начинается ожидание.
Если в данный момент другие ярлычки не показываются, то интервал времени задается свойством HintPause:
property HintPause: Integer;
Интервал времени по умолчанию равен 500 мс. Если в данный момент уже виден ярлычок другого компонента, то интервал времени ожидания задается свойством:
property HintShortPause: Integer;
По истечении этого времени, если мышь осталась над тем же элементом управления, наступает момент инициализации окна подсказки. При этом программист может получить управление, предусмотрев обработчик события объекта Application:
property OnShowHint: TShowHintEvent;
TShowHintEvent = procedure (var HintStr: string; 
var CanShow: Boolean;
 var Hintlnfo: THintlnfo) of object;
Рассмотрим параметры обработчика события OnShowHint:

  •   Hintstr — отображаемый текст;
  •  CanShow — необходимость (возможность) появления подсказки. Если в переменной CanShow обработчик вернет значение False, то окно подсказки высвечиваться не будет;
  •  Hintinfo — структура, несущая всю информацию о том, какой элемент управления, где и как собирается показать подсказку. Ее описание:

THintlnfo = record
HintControl: TControl;
HintPos: TPoint;
HintMaxWidth: Integer;
HintColor: TColor;
CursorRect: TRect;
CursorPos: TPoint;
 end;
Для показа окна подсказки необходимо еще, чтобы у элемента управления или у его предков в цепочке строка Hint была непустой. Впрочем, это можно исправить в обработчике OnShowHint:
procedure TForml.AppShowHint(var HintStr: string; 
var CanShow: Boolean;var Hintlnfo: THintlnfo);
 begin if HintStr='' then
begin
HintStr := Hintlnfo.HintControl.Name; Hintlnfo.HintColor := clRed; CanShow := True; 
end;
 end;
Присвоив этот метод обработчику Application.OnShowHint, установив Form.showHint:=True и очистив все строки Hint, получим в качестве подсказки имя каждого элемента.
Длительность показа ярлычка задается свойством
property HintHidePause: Integer;
По умолчанию его значение равно 2500 мс.
Свойство
property HintShortCuts: Boolean;
отвечает за показ вместе с текстом ярлычка описания "горячих" клавиш данного элемента управления.
Наконец, можно вручную "зажечь" и "потушить" ярлычок. При помощи метода
procedure ActivateHint(CursorPos: TPoint);
ярлычок показывается в точке CursorPos (система координат — экранная). "Спрятать" окно подсказки можно с помощью метода:
procedure CancelHint;
Без повторного перемещения мыши на текущий элемент оно более не возникнет.
Резюме
Delphi предоставляет разработчику набор стандартных программных механизмов, позволяющих добавлять к приложениям функции пользовательского интерфейса Windows. Кроме представленных здесь, разработчик может использовать расширенный набор функций, содержащийся в библиотеке Shell API, которой посвящена гл. 31.

Усовершенствованное масштабирование


В класс TControl добавлены свойства, позволяющие упростить масштабирование форм и находящихся на них компонентов.
Свойство Anchors:
TAnchorKind = (akLeft, akTop, akRight, akBottom);
TAnchors = set of TAnchorKind; property Anchors: TAnchors;
отвечает за привязку компонентов к определенным краям формы при масштабировании. По умолчанию любой компонент привязан к верхней и левой сторонам ([akLeft, akTop]), т. е. не двигается при стандартном масштабировании. Но, изменив значение этого свойства, можно сделать так, чтобы компонент находился, к примеру, все время в нижнем правом углу.
С другой стороны, если прикрепить все четыре стороны, то получится интересный и нужный во многих случаях эффект. Такой компонент увеличивается и уменьшается вместе с формой; но в то же время сохраняется расстояние до всех четырех ее краев.
Свойство constraints представляет собой набор ограничений на изменение размеров компонента. Оно содержит четыре свойства: MaxHeight, Maxwidth, MinHeight и Minwidth. Как легко догадаться из названий, размеры компонента могут меняться только в пределах значений этих четырех свойств.
Наконец, большинство элементов управления получили свойство Autosize, позволяющее им автоматически масштабироваться при изменении содержимого (скажем, надписи на кнопке).
 
Управление фокусом
В процессе работы приложения тот или иной элемент управления получает фокус ввода в зависимости от действий пользователя. Очень часто передача фокуса между элементами управления должна быть упорядочена. Например, при вводе данных в приложениях баз данных пользователь должен иметь максимум удобств для обеспечения хорошей производительности труда. Для этого он должен работать только с клавиатурой, не отвлекаясь на лишние операции по передаче фокуса в нужный компонент при помощи мыши.
Для решения подобного рода проблем все оконные элементы управления имеют два свойства. Свойство TabOrder определяет порядок передачи фокуса между элементами управления одного владельца (формы, панели, группы) при нажатии клавиши <Таb>. Значение 0 имеет компонент, который будет получать фокус при открытии формы.
Для того чтобы свойство TabOrder работало, свойство Tabstop должно иметь значение True.
Кроме этого, все кнопки (произошедшие от TButtonControl) имеют свойство Default, которое при значении True заставляет кнопку реагировать на нажатие клавиши <Enter> как на щелчок на кнопке, даже если она не имеет фокус. Только одна кнопка на форме может иметь это свойство установленным.
Для передачи фокуса любому оконному элементу управления программными средствами можно использовать метод
procedure SetFocus; virtual;
унаследованный от класса TwinControl.
При необходимости работы в форме применяется метод
function SetFocusedControl(Control: TWinControl): Boolean; virtual;
класса TForm, в параметре указывается указатель на компонент, принадлежащий форме.
Управление мышью
Каждый элемент управления обладает набором свойств и методов, обеспечивающих управление мышью. Понятно, что это важный и нужный механизм. Рассмотрим кратко его устройство.
Воздействие мышью на интерфейсные элементы приложения разработчик может отслеживать при помощи целой группы методов-обработчиков.
На нажатие кнопки мыши реагирует метод
type
TMouseEvent = procedure (Sender: TObject; 
Button: TMouseButton;
Shift: TShiftState; X, Y: Integer) of object;
 property OnMouseDown: TMouseEvent;
В параметре Button передается признак нажатой кнопки:
type TMouseButton = (mbLeft, mbRight, mbMiddle);
Параметр shift определяет нажатие дополнительной клавиши на клавиатуре:
type TShiftState = set of (ssShift, ssAlt, ssCtrl, ssLeft, ssRight, ssMiddle, ssDouble);
Параметры х и у возвращают координаты курсора.
На отпускание кнопки мыши реагирует метод:
type
TMouseEvent = procedure (Sender: TObject;
 Button: TMouseButton;
Shift: TShiftState; X, Y: Integer) of object; 
property OnMouseUp: TMouseEvent;
Его параметры описаны выше.
При перемещении мыши можно вызывать метод-обработчик
TMouseMoveEvent = procedure(Sender: TObject;
 Shift: TShiftState; X, Y: Integer) of object;
property OnMouseMove: TMouseMoveEvent;
Если у разработчика нет необходимости так подробно отслеживать состояние мыши, можно воспользоваться двумя другими методами:
property OnClick: TNotifyEvent; 
property OnDblClick: TNotifyEvent;
Первый реагирует на щелчок кнопкой, второй — на двойной щелчок.
Каждый элемент управления может изменять внешний вид указателя мыши, перемещающейся над ним. Для этого используется свойство
property Cursor: TCursor;
Для управления дополнительными возможностями мыши для работы в Internet (ScrollMouse) предназначены три метода обработчика, реагирующие на прокрутку:

  •  property OnMouseWheel: TMouseWheelEvent;

 вызывается при прокрутке;

  •  property OnMouseWheelUp: TMouseWheelUpDownEvent;

вызывается при прокрутке вперед;

  •  property OnMouseWheelDown: TMouseWheelUpDownEvent;

вызывается при прокрутке назад.
В VCL имеется класс TMouse, содержащий свойства мыши, установленной на компьютере. Обращаться к экземпляру класса, который создается автоматически, можно при помощи глобальной переменной Mouse. Свойства класса представлены в табл. 27.1.
В качестве примера обработки управляющих воздействий от мыши рассмотрим пример DemoMouse. Он очень прост. Перемещение мыши с нажатой левой кнопкой обеспечивает выделение прямоугольного фрагмента. Такую функцию вы можете наблюдать в любом графическом редакторе, а исходный код проекта использовать в собственных разработках (листинг 27.2).
Таблица 27.1. Свойства и методы класса mouse


Объявление

Тип

Описание

property Capture: HWND;

Pu

Дескриптор элемента управления, над которым находится мышь

property CursorPos: TPoint;

Pu 

Содержит координаты указателя мыши

property Draglmmediate: Boolean;

Ro
 

При значении True реакция на нажатие выполняется немедленно

property DragThreshold: Integer;

Ro

Задержка реакции на нажатие

property MousePresent: Boolean;

Ro

Определяет наличие мыши

type UINT = LongWord; property RegWheelMessage: UINT;

Ro

Задает сообщение, посылаемое при прокрутке в ScrollMouse

property WheelPresent: Boolean;

Ro
 

Определяет наличие ScrollMouse

property WheelScrollLines : Integer;

Ro

Задает число прокручиваемых линий

 Листинг 27.2. Модуль главной формы проекта DemoMouse  
unit Main; 
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
ExtCtrls, ComCtrls;
type
TMainForm = class(TForm) ColorDlg: TColorDialog; 
StatusBar: TStatusBar; Timer: TTimer;
 procedure FormMouseDown(Sender: TObject;
 Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
procedure FormMouseUp(Sender: TObject;
 Button: TMouseButton;
 Shift: TShiftState; X, Y: Integer);
procedure FormMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
procedure TimerTimer(Sender: TObject);
 private
MouseRect: TRect;
IsDown: Boolean;
RectColor: TColor;
 public
{ Public declarations }
 end;
var
MainForm: TMainForm;
implementation {$R *.DFM}
procedure TMainForm.FormMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
 begin
if Button = mbLeft then with MouseRect do
 begin
IsDown := True; Left := X; Top := Y; Right := X; Bottom := Y;
Canvas.Pen.Color := RectColor; 
end;
if (Button = mbRight) and ColorDlg.Execute then RectColor := ColorDlg.Color;
 end;
procedure TMainForm.FormMouseUp(Sender: TObject; 
Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
IsDown := False;
Canvas.Pen.Color := Color;
with MouseRect do
Canvas.Polyline([Point(Left, Top), Point(Right, Top), Point(Right,
Bottom), Point(Left, Bottom), Point(Left, Top)]);
with StatusBar do
begin
Panels[4].Text := ''; Panels [5] .Text := ";
 end;
 end;
procedure TMainForm.FonnMouseMove(Sender: TObject; Shift: TShiftState; X,
Y: Integer);
 begin
with StatusBar do
 begin
Panels[2].Text := 'X: ' + IntToStr(X);
Panels[3].Text := 'Y: ' + IntToStr(Y);
 end;
if Not IsDown then Exit; Canvas.Pen.Color := Color; with mouserect do 
begin
Canvas.Polyline([Point(Left, Top), Point(Right, Top),
Point(Right, Bottom), Point(Left, Bottom), Point(Left, Top)]);
Right := X;
Bottom := Y;
Canvas.Pen.Color := RectColor;
Canvas.Polyline([Point(Left, Top), Point(Right, Top),
Point(Right, Bottom), Point(Left, Bottom), Point(Left, Top)]);
 end;
with StatusBar do begin
Panels [4] .Text := 'IHwpMHa: ' + IntToStr(Abs(MouseRect.Right - MouseRect.Left));
Panels[5].Text := 'BacoTa: ' + IntToStr(Abs(MouseRect.Bottom - MouseRect.Top));
end; end;
procedure TMainForm.TimerTimer(Sender: TObject); 
begin
with StatusBar do 
begin
Panels[0].Text := 'flaTa: ' + DateToStr(Now); Panels[1].Text := 'BpeMH: ' + TimeToStr(Now);
 end; 
end;
end.
При нажатии левой кнопки мыши в методе-обработчике FormMouseDown включается режим рисования прямоугольника (isDown := True) и задаются его начальные координаты.
При перемещении мыши по форме проекта вызывается метод-обработчик FormMouseMove, в котором координаты курсора и размеры прямоугольника передаются на панель состояния. Если левая кнопка мыши нажата (isDown = True), то осуществляется перерисовка прямоугольника.
При отпускании кнопки мыши в методе FormMouseUp рисование прямоугольника прекращается (isDown := False).
Если была нажата правая кнопка мыши, то метод-обработчик FormMouseDown обеспечивает отображение диалога выбора цвета, который позволяет сменить цвет линий прямоугольника.
Метод-обработчик TimerTimer обеспечивает отображение на панели состояния текущей даты и времени.
Примечание
Для рисования прямоугольника использовался метод PolyLine, который работает при перемещении курсора влево и вверх относительно начальной точки. Для стирания старого прямоугольника желательно использовать режимы XOR и NOTXOR, которые обеспечивают восстановление рисунка под линией. Подробно об этом см. гл. 10.
 
Ярлыки
Пользовательский интерфейс трудно представить без ярлычков с оперативной подсказкой (Hints). Если задержать курсор, например, над кнопкой или компонентом палитры самой среды Delphi, появляется маленький прямоугольник яркого цвета (окно подсказки), в котором одной строкой сказано о названии этого элемента или связанном с ним действии. Delphi поддерживает механизмы создания и отображения таких ярлычков в создаваемых программах.
Свойство, определяющее активность системы подсказки у элемента управления:
property ShowHint: Boolean;
Если свойство ShowHint установлено в True, и во время выполнения курсор задержался над компонентом на некоторое время, в окне подсказки высвечивается текстовая строка с подсказкой, которая задана свойством:
property Hint: string;
Подсказка компонента может быть пустой строкой — в этом случае система ищет в цепочке первый родительский компонент с непустой подсказкой.
Если в строке Hint встречается специальный символ-разделитель "Г, то часть строки до него ("короткая") передается в окно подсказки, а после ("длинная") — присваивается свойству Hint объекта Application. Ее можно использовать, например, в строке состояния внизу главной формы приложения (см. пример ниже).
Система оперативных подсказок имеет свойства и методы, общие для всех форм в приложении. Не удивительно, что они сосредоточены в Application — глобальном объекте, соответствующем работающему приложению. Все описанные ниже в этом разделе свойства относятся не к компоненту, показывающему подсказку, а именно к Application.
Фоновый цвет окна подсказки можно изменить посредством свойства
property HintColor: TColor;
У объекта Application значение свойства showHint нужно устанавливать во время выполнения, например, в обработчике OnCreate главной формы приложения. Оно является главенствующим для всей системы подсказок: если оно установлено в значение False, ярлычки не возникают.
Есть еще один способ получения подсказки. При смене текущего элемента управления (т. е. при смене текста в свойстве Hint) в объекте Application возникает событие
property OnHint: TNotifyEvent; 
Пример:
procedure TForml.AppHint(Sender: TObject);
 begin
Panell.Caption:= Application.Hint;:
 end;
procedure TForml.FormCreate(Sender: TObject);
 begin
Application.OnHint := AppHint; 
end;
В этом примере текст подсказки будет отображаться в строке состояния Panell независимо от значения showHint у любого объекта — лишь бы этот текст был в наличии. Для этого разделяйте подсказку у элементов управления вашего приложения на две части при помощи символа " |" — краткая информация появится рядом с элементом, а более полная — в строке состояния.
function GetLongHint(const Hint: string): string; function GetShortHint(const Hint: string): string;
У других компонентов свойство ShowHint интерпретируется системой так: когда курсор мыши останавливается над элементом управления или пунктом меню, и приложение не занято обработкой сообщения, происходит проверка, и если свойство showHint у элемента или у одного из его родительских элементов в иерархии равно True, то начинается ожидание.
Если в данный момент другие ярлычки не показываются, то интервал времени задается свойством HintPause:
property HintPause: Integer;
Интервал времени по умолчанию равен 500 мс. Если в данный момент уже виден ярлычок другого компонента, то интервал времени ожидания задается свойством:
property HintShortPause: Integer;
По истечении этого времени, если мышь осталась над тем же элементом управления, наступает момент инициализации окна подсказки. При этом программист может получить управление, предусмотрев обработчик события объекта Application:
property OnShowHint: TShowHintEvent;
TShowHintEvent = procedure (var HintStr: string; 
var CanShow: Boolean;
 var Hintlnfo: THintlnfo) of object;
Рассмотрим параметры обработчика события OnShowHint:

  •   Hintstr — отображаемый текст;
  •  CanShow — необходимость (возможность) появления подсказки. Если в переменной CanShow обработчик вернет значение False, то окно подсказки высвечиваться не будет;
  •  Hintinfo — структура, несущая всю информацию о том, какой элемент управления, где и как собирается показать подсказку. Ее описание:

THintlnfo = record
HintControl: TControl;
HintPos: TPoint;
HintMaxWidth: Integer;
HintColor: TColor;
CursorRect: TRect;
CursorPos: TPoint;
 end;
Для показа окна подсказки необходимо еще, чтобы у элемента управления или у его предков в цепочке строка Hint была непустой. Впрочем, это можно исправить в обработчике OnShowHint:
procedure TForml.AppShowHint(var HintStr: string; 
var CanShow: Boolean;var Hintlnfo: THintlnfo);
 begin if HintStr='' then
begin
HintStr := Hintlnfo.HintControl.Name; Hintlnfo.HintColor := clRed; CanShow := True; 
end;
 end;
Присвоив этот метод обработчику Application.OnShowHint, установив Form.showHint:=True и очистив все строки Hint, получим в качестве подсказки имя каждого элемента.
Длительность показа ярлычка задается свойством
property HintHidePause: Integer;
По умолчанию его значение равно 2500 мс.
Свойство
property HintShortCuts: Boolean;
отвечает за показ вместе с текстом ярлычка описания "горячих" клавиш данного элемента управления.
Наконец, можно вручную "зажечь" и "потушить" ярлычок. При помощи метода
procedure ActivateHint(CursorPos: TPoint);
ярлычок показывается в точке CursorPos (система координат — экранная). "Спрятать" окно подсказки можно с помощью метода:
procedure CancelHint;
Без повторного перемещения мыши на текущий элемент оно более не возникнет.
Резюме
Delphi предоставляет разработчику набор стандартных программных механизмов, позволяющих добавлять к приложениям функции пользовательского интерфейса Windows. Кроме представленных здесь, разработчик может использовать расширенный набор функций, содержащийся в библиотеке Shell API, которой посвящена гл. 31.

 

 
На главную | Содержание | < Назад....Вперёд >
С вопросами и предложениями можно обращаться по nicivas@bk.ru. 2013 г. Яндекс.Метрика