
- •9 Разработка компонентов в среде Delphi
- •9.1. Выбор класса-предка
- •9.1.1. Класс tControl
- •9.1.4. Класс tCustomControl
- •9.2. Создание модуля компонента и тестового приложения
- •Разработка тестового приложения
- •9.3.1. Простые свойства
- •9.3.6. Массив свойств
- •9.3.7. Перекрытие и переопределение свойств
- •9.3.8. Создание событий
- •9.3.9. Создание методов
- •Что такое событие? в чем отличие создания свойства от события?
9.3.1. Простые свойства
Простые свойства — это числовые, строковые и символьные свойства. Они могут непосредственно редактироваться в Инспекторе объектов и не требуют специальных методов доступа.
Рассмотрим создание простого свойства Color, описанного в классе TContol (модуль controls.pas):
Type
TControl = class (TComponent)
private
FColor: TColor;
function IsColorStored: Boolean;
procedure SetColor(Value: TColor);
protected
property Color: TColor read FColor write SetColor stored IsColorStored default clWindow;
end;
function TControl.IsColorStored: Boolean;
begin
Result := not ParentColor;
end;
procedure TControl.SetColor (Value: TColor);
begin
if FColor <> Value then
begin
FColor := Value;
FParentColor := False;
Perform(CM_COLORCHANGED, 0, 0) ;
{вызов Perform позволяет обойти очередь сообщений Windows и послать сообщение, в данном случае — изменить цвет, элементу управления}
end;
end;
9.3.2. Свойства перечислимого типа
Определенные пользователем перечислимые и логические свойства можно редактировать в окне инспектора объектов, выбирая подходящее значение свойства в раскрывающемся списке. Рассмотрим создание свойства перечислимого типа на примере компонента Shape (модуль extctrls.pas).
Туре
TShapeType = (stRectangle, stSquare, stRoundRect, stRoundSquare, stEllipse, stCircle);
{вначале необходимо определить новый тип — перечислить возможные значения}
Туре
TShape = class(TGraphicControl)
private
…
FShape: TShapeType;
procedure SetShape(Value: TShapeType);
published
…
property Shape: TShapeType read FShape write SetShape
default stRectangle;
end;
procedure TShape.SetShape{Value: TShapeType);
begin
if FShape <> Value then
begin
FShape := Value;
Inva 1 idate; {гарантирует перерисовку компонента}
end;
end;
9.3.3. Свойства типа множества
Свойство типа множества при редактировании в окне Инспектора объектов выглядит так же, как множество, определенное синтаксисом языка Pascal. Простейший способ его отредактировать — развернуть свойство в Инспекторе объектов, в результате каждый его элемент станет отдельным логическим значением.
При создании свойства типа множества нужно создать соответствующий тип, описать методы доступа, после чего описать само свойство. В модуле Controls.pas свойсво Align описано следующим образом:
Туре
TAlign = (alNone, alTop, alBottom, alLeft, alRight, alClient);
TAlignSet = set of TAlign; TControl = class(TComponent)
private
FAlign: TAlign;
procedure SetAlign(Value: TAlign);
public
property Align: TAlign read FAlign write SetAlign default alNone;
end;
procedure TControl.SetAlign(Value: TAlign);
var OldAlign: TAlign;
begin
if FAlign <> Value then
begin
OldAlign := FAlign;
FAlign := Value;
Anchors := AnchorAlign[Value];
if not (csLoading in ComponentState) and
(not (csDesigning in ComponentState) or (Parent <> NIL))
then
if ((OldAlign in [alTop, alBottom])=(Value in [alRight, alLeft])) and not (OldAlign in [alNone, alClient]) and not (Value in [alNone, alClient]) then SetBounds(Left, Top, Height, Width)
{изменение границ компонента}
else AdjustSize; {устанавливает заданные размеры компонента}
end;
Request Align; {нструктирует «родителя» переставить компонент
в соответствии со значением свойства Align }
end;
9.3.4. Свойство-объект
Свойства могут являться объектами или другими компонентами. Например, у компонента Shape есть свойства-объекты Brush и Реп. Когда свойство является объектом, то оно может быть развернуто в окне инспектора так, чтобы его собственные свойства также могли быть модифицированы. Свойства-объекты должны быть потомками класса TPersistent, чтобы их свойства, объявленные в разделе published, могли быть записаны в поток данных и отображены в инспекторе объектов.
Для определения объектного свойства компонента необходимо сначала определить объект, который будет использоваться в качестве типа свойства. В модуле graphics.pas описан класс TBrush:
TBrush = class(TGraphicsObject)
private
procedure GetData(var BrushData: TBrushData);
procedure SetData(const BrushData: TBrushData);
protected
function GetBitmap: TBitmap;
procedure SetBitmap(Value: TBitmap);
function GetColor: TColor;
procedure SetColor(Value: TColor);
function GetHandle: HBrush.;
procedure SetHandle(Value: HBrush);
function GetStyle: TBrushStyle;
procedure SetStyle(Value: TBrushStyle);
public
constructor Create; destructor Destroy; override;
procedure Assign(Source: TPersistent); override;
property Bitmap: TBitmap read GetBitmap write SetBitmap;
property Handle: HBrush read GetHandle write SetHandle;
published
property Color: TColor read GetColor write SetColor
default clWhite;
property Style: TBrushStyle read GetStyle write SetStyle
default bsSolid;
end;
Метод Assign предназначен для копирования значения свойств экземпляра TBrush:
procedure TBrush.Assign (Source: TPersistent);
begin
if Source is TBrush then
begin
Lock; {блокирует использование объекта}
try
TBrush(Source).Lock;
try
BrushManager.AssignResource(Self, TBrush(Source).FResource);
finally TBrush(Source).Unlock;
end;
finally Unlock; {завершает секцию кода, начатую методом Lock,
снимая блокировку объекта}
end;
exit:
end;
inherited Assign(Source);
end;
Чтобы определить свойство-объект, нужно определить внутреннее поле. Так как свойство представляет объект, его нужно создать, а по завершении — уничтожить, поэтому в код включены конструктор Create и деструктор Destroy. Кроме того, объявлен метод доступа SetBrush, предназначенный для записи свойства Brush.
TShape = class(TGraphicControl)
private
FBrush: TBrush;
procedure SetBrush(Value: TBrush);
public
constructor Create (AOwner: TComponent) ; overrider;
destructor Destroy; overrider;
published
property Brush: TBrush read FBrush write SetBrush;
end;
constructor TShape.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
FBrush := TBrush.Create;
FBrush.OnChange := StyleChanged;
end;
destructor TShape.Destroy;
begin
FBrush. Freer-inherited Destroy;
end;
procedure TShape.SetBrush (Value: TBrush);
begin
FBrush.Assign(Value);
end;
9.3.5. Свойство-массив
Примерами свойств-массивов могут служить такие свойства, как TMemo.Lines, TScreen.Fonts, TStringGrid.Cells.
Особенности свойства-массива заключаются в следующем:
свойства-массивы объявляются с помощью индексных параметров, цель которых — указать количество и тип индексов, которые будут использоваться свойством;
спецификации методов чтения и записи должны ссылаться на методы доступа. Методом для определителя read должна быть функция, список параметров которой совпадает со списком параметров, описывающих индекс свойства, и возвращающей значение того же типа, что и свойство. В свою очередь, методом в определителе write должна быть процедура, список параметров которой совпадает со списком параметров, описывающих индекс свойства. Список параметров такой процедуры может содержать и дополнительные свойства.
TCanvas = class (TPersistent)
private
FHandle: HDC; {ссылка на контекст устройства, используется для отображения графической информации}
function GetPixel (X, Y: Integer) : TColor; {метод чтения}
procedure SetPixel (X, Y: Integer; Value: TColor);
{метод записи}
protected
public
constructor Create;
destructor Destroy; override;
property Pixels[X, Y: Integer]: TColor read GetPixel write SetPixel;
end;
constructor TCanvas.Create;
begin
inherited Create;
CanvasList. Add (Self) ; {добавляет в список ссылки на объекты}
end;
destructor TCanvas.Destroy;
begin
CanvasList .Remove (Self) ; {удаляет из списка ссылки на объекты}
inherited Destroy; end;
function TCanvas.GetPixel (X, Y: Integer): TColor;
begin
RequiredState([csHandleValid]);
GetPixel := Windows.GetPixel(FHandle, X, Y) ; end;
procedure TCanvas.SetPixel(X, Y: Integer; Value: TColor);
begin
Changing;
RequiredState([csHandleValid, csPenValid]);
Windows.SetPixel(FHandle, X, Y, ColorToRGB(Value));
Changed;
end;
Доступ к такому свойству-массиву осуществляется следующим образом:
Canvas.Pixels [10, 20] :=clRed; что означает:
Canvas.SetPixel (10, 20, clRed);
За описанием свойства-массива может следовать директива default. В данном случае это будет означать, что это свойство становится свойством по умолчанию для данного класса. Например:
type
TStringArray = class public property Strings[Index: Integer]: string . . . ; default;
end;
Если у класса есть свойство по умолчанию, то доступ к этому свойству может быть осуществлен оператором
<имя компонента>[Index], который эквивалентен оператору
<имя компонента>.<имя свойства>[Index].
Класс может иметь только одно свойство по умолчанию. В связи с тем, что компилятор статически определяет свойство по умолчанию у объекта, то смена свойства по умолчанию или его скрытие в наследниках класса может привести к непредсказуемым последствиям.