Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Программирование в Delphi 5.doc
Скачиваний:
0
Добавлен:
01.03.2025
Размер:
410.11 Кб
Скачать

If Sender is tMenuItem then ShowMessage ('Выбран пункт меню');

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

const colors : array[0..7] of TColor = (clWhite,cIRed,clBlue,clYellow,clAqua,

cIGreen,cIMaroon,clBlack) ;

procedure TForml.CheckBoxClick(Sender: TObject);

begin

with (Sender as TCheckBox) do if Checked then Color := Colors[Tag] else Color := clBtnFace;

end;

Пусть на форме имеется несколько переключателей. Для того чтобы при нажатии каждый из них окрашивался в свой цвет, нужно в Инспекторе объектов для каждого присвоить свойству Tag значения от 0 до 7 и связать событие OnClick с методом checkBoxClick. Этот единственный способ справится с задачей для всех переключателей.

Обработка сообщений Windows

Можно сказать, что события и сообщения Windows находятся в знаменитой пропорции "80:20". В данном случае это означает, что доступные в компонентах Delphi события соответствуют 20% наиболее важных сообщений, которые покрывают 80% типовых потребностей программиста. Но есть еще и остальные 80% сообщений...

Методы, предназначенные специально для обработки сообщений Windows, составляют подмножество динамических методов и объявляются директивой message, за которой следует индекс — идентификатор сообщения. Динамические методы были введены в язык Object Pascal именно тогда, когда встала проблема создания объектов, соответствующих элементам интерфейса Windows. Когда каждый из большой иерархии компонентов содержит обработчики десятков разнообразных сообщений (посмотрите ради интереса на исходный код классов TControl и TWinControl в модуле classes . pas), разумное сочетание скорости и компактности обеспечивают только динамические методы.

Идентификатор, стоящий после директивы message, может быть числом в диапазоне от 1 до 49151 или константой вида wm_xxx, скажем, wm_mousemove.

Методы должны быть обязательно описаны как процедуры, имеющие один var-параметр. Этот параметр соответствует структуре, содержащей сообще-Windows. Однако он может быть описан произвольно, например:

type

TMyControl = class(TWinControl)

procedure WMSize(var Message: TWMSize); message WM_SIZE;

end;

TOtherMyControl = class(TMyControl)

procedure Resize(var Info); message WM SIZE;

end;

Нет необходимости изобретать собственные структуры, по-своему интерпретирующие содержание того или иного сообщения. Для большинства сообщений Windows структуры уже описаны в модуле messages. раs. Например, тип TMessage и приведенный в примере тип TWMSize выглядят так:

TMessage = record

Msg: Cardinals-case

Integer of 0: (

WParam: Longint;

LParam: Longint;

Result: Longint) ;

1: (

WParainLo Word;

WParamHi Word;

LParamLo Word;

LParamHi Word;

ResultLo Word;

ResultHi Word);

end;

TWMSize = record Msg: Cardinal;

SizeType: Longint;

( SIZE_MAXIMIZED, SIZE_MINIMIZED, SIZE_RESTORED,

SIZE_MAXHIDE, SIZE_MAXSHOW } Width: Word;

Height: Word;

Result: Longint;

end;

Для перекрытия методов-обработчиков сообщений директива override не используется, в этом случае нужно сохранить директиву message с индексом метода. Именно индекс является главным "якорем", привязывающим метод к определенному сообщению. В данном примере метод Resize перекрывает WMSize, хотя у них разные имена и даже разный формальный тип параметра. Важно только то, что они оба привязаны к сообщению wm_size.

В обработчиках сообщений можно вызвать метод-предок, просто указав ключевое слово inherited, без указания его имени и преобразования типа параметров: предок будет найден по индексу. Следует напомнить, что система обработки сообщений встроена в Object Pascal на уровне модели объектов, и самый общий обработчик — метод DefaultHandler — описан в классе TObject. Чтобы выполнить все необходимые действия при обработке сообщения, за редким исключением настоятельно рекомендуется вызвать предыдущий обработчик:

procedure MyMessageHandler(var Message);

message wm_command;

begin

MyProcessing;

inherited;

end;

Delphi предоставляет доступ и к остальным составным частям системы обработки сообщений. В "традиционных" программах, использующих только Windows API, главной из них является функция окна WndProc, куда собственно и поступают все входящие сообщения (такие примеры поставлялись еще с Borland Pascal for Windows). Сейчас же последовательность обработки напоминает известную историю "бабка за дедку, дедка за репку...". Итак:

  1. Через функцию setWindowLong системе сообщается адрес функции окна. По умолчанию эта процедура MainWndProc. При желании можно сообщить и другой адрес, однако это требует определенной квалификации; во всяком случае, не следует этого делать раньше, чем вы подробно ознакомитесь с тем, как это сделано в Delphi.

  2. Сообщение поступает в метод MainWndProc. Он не виртуальный, и "вклиниться" здесь тяжело.

  3. Метод MainWndProc вызывает метод, адрес которого содержится в свойстве windowproc. По умолчанию здесь содержится адрес виртуального метода WndProc. Тут открываются две возможности: а) переопределить значение свойства windowproc, присвоив ему адрес собственной процедуры обработки и б) перекрыть виртуальный метод WndProc.

  4. Для разных компонентов внутри WndProc может быть предусмотрена обработка тех или иных сообщений. В противном случае вызывается виртуальный метод TObject. Dispatch; именно он реализует поиск необходимого динамического метода-обработчика по индексу сообщения. Здесь тоже открываются две возможности: перекрыть Dispatch и написать свой метод-обработчик сообщения. Первая возможность совсем гипотетическая; вторая же, наоборот, самая реальная.

  5. Если метод Dispatch не нашел обработчика для данного индекса сообщения, вызывается виртуальный метод Default Handler. Если и ему "нечего сказать" по поводу данного сообщения, происходит обращение к свойству DefWndProc и вызывается метод, на который оно указывает. Этот последний метод "предоставляет последнее слово" стандартному обработчику Windows.

В зависимости от обстоятельств, нужные вам действия можно предусмотреть на любом из уровней. В 90% случаев достаточно написать динамический метод-обработчик:

TForrol = class(TForm)

procedure FormDblClick(Sender: TObject);

private

procedure WMLButtonDblClick(var Msg:TWMLButtonDblClk);

message WM_LBUTTONDBLCLK ;

end;

procedure TForml.WMLButtonDblClickfvar Msg:TWMLButtonDblClk);

begin

ShowMessage(Format('Click at %d,%d',[Msg.XPos,Msg.YPos]));

end;

procedure TFormI.FormDblClick(Sender: TObj ect) ;

begin

ShowMessage('Dbl Click') ;

end;

В данном случае реакция на двойной щелчок левой кнопкой мыши будет перехвачена нашим методом; в ответ на него будет выведено сообщение с указанием координат случившегося события. Обратите внимание, что в обработчике отсутствует оператор inherited, который вызвал бы унаследованную обработку. Это означает, что имеющийся в нашей форме обработчик события Delphi OnDblcik получает управление только по двойному щелчку правой кнопкой мыши, но — не левой. Стоит добавить оператор inherited — и все встанет на свои места.

Теперь от этого примера плавно перейдем к более сложному — нашим картам. Игральная карта порождена от класса TShape, порожденному от TGraphicControi. У этих классов есть особенность — поступившие в них сообщения от мыши они отдают системным обработчикам перетаскивания (drag-and-drop), и те интерпретируют их по-своему. Поэтому если мы слепо повторим предыдущий опыт и перенесем в TCard метод WMLButtonDblclick, то он никогда не получит управления. Это как раз тот случай, когда нужно переопределять метод WndProc:

TCard = class(TShape) procedure WndProc(var Message: TMessage); override;

end;

procedure TCard.WndProc(var Message: TMessage);

begin

if Message.Msg = WM_LBUTTONDBLCLK then

Dispatch(Message) else

inherited;

end;

Этот короткий метод перехватывает только сообщение WM_LButtonDbiclick и отправляет его на обработку самому себе; все остальные сообщения идут по обычному пути.

В принципе, если хорошо знать VCL (Visual Component Library), а также, кто и как реагирует на сообщения Windows, то можно не вызывать методы компонентов, а посылать и принимать от них сообщения. Это, конечно, малоценный вариант с практической точки зрения — ведь все сообщения ставятся в системные очереди Windows и извлекаются оттуда. Это замедляет работу. Чтобы напрямую обратиться к обработчику сообщения, во всех элементах управления предусмотрен метод perform. Он напрямую передает сообщение в функцию окна данного компонента.

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

procedure TForml.ButtonlClick(Sender: TObject);

var CharPos : TPoint;

begin with CharPos do

begin

Y:=Meniol.Perform(EM_LINEFROMCHAR, Memol.SelStart, 0);

X:=Memol.Perform(EM_LINEINDEX, Y,0);

inc(Y);

X:=Memol.SelStart-X+1;

ShowMessage(Format('%d:%d',[x,y]));

end;

end;

Сообщения em_linefromchar и em_lineindex — специализированные, обрабатываемые только текстовыми элементами управления. Вообще, компонент TMemo — это только оболочка, методы которой представляют собой посылку и получение сообщений от соответствующего элемента управления, имеющегося в составе стандартных библиотек Windows. О таких компонентах рассказано в главе 11.