Главная » Delphi » Компоненты CLX для работы с базами данных

0

В четвертом варианте компонента Spinner реализовано взаимодействие с базами данных.   При   помощи  свойств   DataSource и  DataField компонент  типа TddgDBSpinner может  подключаться к  любому  целочисленному  полю  некоторого набора данных.  На  рис. 13.7  показан компонент типа  TddgDBSpinner, подключен ный к полю VenueNo набора данных Events.

Рис. 13.7.  Компонент  TddgDBSpin- ner используется для отображения и ре дактирования целочисленных полей на боров данных

Класс  TddgDBSpinner является производным от  класса  TddgImgListSpinner. Он реализован в модуле QddgDBSpin.pas, исходный код которого представлен в листинге 13.4.

Листинг 13.4. QddgDBSpin.pas — исходный  код компонента TddgDBSpinner

unit QddgDBSpin;

interface usesSysUtils, Classes, Qt, QddgILSpin, DB, QDBCtrls; (*

Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, ddgILSpin, DB, DBCtrls;

*)

type

TddgDBSpinner = class( TddgImgListSpinner )

private

FDataLink: TFieldDataLink;           // Обеспечивает доступ к данным

// Внутренние обработчики событий доступа к данным

procedure DataChange( Sender: TObject );

procedure UpdateData( Sender: TObject );

procedure ActiveChange( Sender: TObject );

(*

// VCL->CLX:         Методы обработки сообщений компонента,

// отсутствующие в CLX

procedure CMExit( var Msg: TCMExit ); message cm_Exit;

procedure CMDesignHitTest( var Msg: TCMDesignHitTest );

message cm_DesignHitTest;

*)

protected

procedure Notification( AComponent : TComponent;

Operation : TOperation ); override;

procedure CheckFieldType( const Value: string ); virtual;

Переопределенные методы обработки событий

procedure Change; override;

procedure KeyPress( var Key : Char ); override;

// VCL->CLX:         Процедура DoExit заменена на CMExit procedure DoExit; override;

// VCL->CLX:         Функция DesignEventQuery

// заменена на CMDesignHitTest

function DesignEventQuery( Sender: QObjectH;

Event: QEventH ): Boolean; override;

// Переопределенные вспомогательные методы procedure DecValue( Amount: Integer ); override; procedure IncValue( Amount: Integer ); override;

// Методы доступа к свойствам function GetField: TField; virtual; function GetDataField: string; virtual;

procedure SetDataField( const Value: string ); virtual;

function GetDataSource: TDataSource; virtual;

procedure SetDataSource( Value: TDataSource ); virtual;

function GetReadOnly: Boolean; virtual;

procedure SetReadOnly( Value: Boolean ); virtual;

// Доступ потомков к объектам Field и DataLinkproperty Field: TField read GetField;

property DataLink: TFieldDataLink read FDataLink;

public

constructor Create( AOwner: TComponent ); override;

destructor Destroy; override;

published

property DataField: string

read GetDataField

write SetDataField;

property DataSource: TDataSource read GetDataSource

write SetDataSource;

// Это свойство контролирует состояние (ReadOnly)

// объекта DataLink

property ReadOnly: Boolean

read GetReadOnly

write SetReadOnly

default False;

end;

type

EInvalidFieldType = class( Exception );

resourcestring

SInvalidFieldType = ‘DataField can only be connected to ‘ +

‘columns of type Integer, Smallint, Word, ‘ +

‘and Float';

implementation uses

Types;                           // VCL->CLX: Добавлено для CLX

{=================================}

{== Методы класса TddgDBSpinner ==}

{=================================}

constructor TddgDBSpinner.Create( AOwner: TComponent );

begin

inherited Create( AOwner );

FDataLink := TFieldDataLink.Create;

// Для поддержки метода TField.FocusControl, свойство

// FDataLink.Control должно указывать на Spinner.

// Свойство Control необходимо для всех компонентов

// класса TWinControl.

FDataLink.Control := Self;

// Назначение обработчиков событий FDataLink.OnDataChange := DataChange; FDataLink.OnUpdateData := UpdateData; FDataLink.OnActiveChange := ActiveChange;

// Примечание: пользователь компонента не имеет прямого доступа

// к объекту DataLink, поэтому не может назначать собственные

// обработчики событий.

end;

destructor TddgDBSpinner.Destroy;

begin

FDataLink.Free;

FDataLink := nil;

inherited Destroy;

end;

procedure TddgDBSpinner.Notification( AComponent: TComponent; Operation: TOperation );

begin

inherited Notification( AComponent, Operation );

if ( Operation = opRemove ) and

( FDataLink <> nil ) and

( AComponent = FDataLink.DataSource ) then begin

DataSource := nil;                // Косвенный вызов SetDataSource

end;

end;

function TddgDBSpinner.GetField: TField;

begin

Result := FDataLink.Field;

end;

function TddgDBSpinner.GetDataField: string;

begin

Result := FDataLink.FieldName;

end;

procedure TddgDBSpinner.SetDataField( const Value: string );

begin

CheckFieldType( Value );

FDataLink.FieldName := Value;

end;

function TddgDBSpinner.GetDataSource: TDataSource;

begin

Result := FDataLink.DataSource;

end;

procedure TddgDBSpinner.SetDataSource( Value: TDataSource );

begin

if FDatalink.DataSource <> Value then beginFDataLink.DataSource := Value;

// Необходимо вызвать метод FreeNotification, так как источник

// данных может находиться в другой форме или в другом модуле.

if Value <> nil then

Value.FreeNotification( Self );

end;

end;

function TddgDBSpinner.GetReadOnly: Boolean;

begin

Result := FDataLink.ReadOnly;

end;

procedure TddgDBSpinner.SetReadOnly( Value: Boolean );

begin

FDataLink.ReadOnly := Value;

end;

procedure TddgDBSpinner.CheckFieldType( const Value: string );

var

FieldType: TFieldType;

begin

// Поле указанного в Value столбца должно иметь тип

// ftInteger, ftSmallInt, ftWord или ftFloat.

// Если это не так, то передается исключение EInvalidFieldType.

if ( Value <> ” ) and

( FDataLink <> nil ) and

( FDataLink.Dataset <> nil ) and

( FDataLink.Dataset.Active ) then begin

FieldType := FDataLink.Dataset.FieldByName( Value ).DataType;

if ( FieldType <> ftInteger ) and

( FieldType <> ftSmallInt ) and

( FieldType <> ftWord ) and

( FieldType <> ftFloat ) then begin

raise EInvalidFieldType.Create( SInvalidFieldType );

end;

end;

end;

procedure TddgDBSpinner.Change;

begin

// Для FDataLink устанавливается признак модификации

if FDataLink <> nil then

FDataLink.Modified;

inherited Change;            // Передается событие OnChange

end;

procedure TddgDBSpinner.KeyPress( var Key: Char );

begin

inherited KeyPress( Key );

FDataLink.Reset;

//

Нажата клавиша Esc

Key := #0;

//

Значение #0 используется для того, чтобы

nd;

//

при нажатии Esc окно не закрылось.

 

if Key = #27 then begine end;

procedure TddgDBSpinner.DecValue( Amount: Integer );

begin

if ReadOnly or not FDataLink.CanModify then begin

// Запрет изменений, если FDataLink в режиме ReadOnly

(*

// VCL->CLX:         MessageBeep – это функция API Windows

MessageBeep( 0 )

*)

Beep;

end else begin

// Попытка открыть набор данных для редактирования.

// Уменьшение значения – только в режиме редактирования

if FDataLink.Edit then

inherited DecValue( Amount );

end;

end;

procedure TddgDBSpinner.IncValue( Amount: Integer );

begin

if ReadOnly or not FDataLink.CanModify then begin

// Запрет изменений, если FDataLink в режиме ReadOnly

(*

// VCL->CLX:         MessageBeep – это функция API Windows

MessageBeep( 0 )

*)

Beep;

end else begin

// Попытка открыть набор данных для редактирования.

// Увеличение значения – только в режиме редактирования

if FDataLink.Edit then

inherited IncValue( Amount );

end;

end;

{================================================================= TddgDBSpinner.DataChange

Этот метод вызывается вследствие различных событий:

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

2. Переход соответствующего набора данных в режим редактирования.

3. Изменение в наборе данных, указанном в свойстве DataSource.

4. Переход курсора к новой записи таблицы.

5. Восстановление записи при вызова метода Cancel.6. Изменено свойство DataField при переходе на другой столбец.

=================================================================}

procedure TddgDBSpinner.DataChange( Sender: TObject );

begin

if FDataLink.Field <> nil then

Value := FDataLink.Field.AsInteger;

end;

{================================================================= TddgDBSpinner.UpdateData

Этот метод вызывается в том случае, если необходима синхронизация содержимого поля и значения в компоненте Spinner. Учтите, что данный метод вызывается только при изменении данных.

=================================================================}

procedure TddgDBSpinner.UpdateData( Sender: TObject );

begin

FDataLink.Field.AsInteger := Value;

end;

{================================================================= TddgDBSpinner.ActiveChange

Этот метод вызывается при изменении свойства Active подключенного набора данных.

Примечание: для "обновления" состояния набора данных

можно использовать свойство FDataLink.Active.

=================================================================}

procedure TddgDBSpinner.ActiveChange( Sender: TObject );

begin

// После активизации набора данных в нем проверяется

// корректность типа поля, указанного в свойстве DataField.

if ( FDataLink <> nil ) and FDataLink.Active then

CheckFieldType( DataField );

end;

(*

// VCL->CLX:     Вместо процедуры CMExit используется DoExit

procedure TddgDBSpinner.CMExit( var Msg: TCMExit );

begin

try       // Попытка обновления записи при потере фокуса

// компонентом Spinner

FDataLink.UpdateRecord;

except

SetFocus;

//

В случае неудачного обновления фокус остается

//

в элементе управления

raise;

//

Повторная передача исключения

end;

inherited;

end;

*)

procedure TddgDBSpinner.DoExit;

begin

try       // Попытка обновления записи при потере фокуса

// компонентом Spinner

FDataLink.UpdateRecord;

except

SetFocus;

//

В случае неудачного обновления фокус остается

//

в элементе управления

raise;

//

Повторная передача исключения

end;

inherited;

end;

(*

// VCL->CLX:     Процедура CMDesignHitTest заменена

//               на DesignEventQuery

procedure TddgDBSpinner.CMDesignHitTest(var Msg: TCMDesignHitTest);

begin

// В компоненте базового класса значение Value можно изменять во

// время разработки. Для компонентов, взаимодействующих с базами

// данных, это недопустимо, так как подключенный набор данных

// переходит в режим редактирования.

Msg.Result := 0;

end;

*)

function TddgDBSpinner.DesignEventQuery( Sender: QObjectH;

Event: QEventH ): Boolean;

begin

// В компоненте базового класса значение Value можно изменять во

// время разработки. Для компонентов, взаимодействующих с базами

// данных это не допустимо, так как подключенный набор данных

// переходит в режим редактирования.

Result := False;

end;

end.К счастью, структура  компонентов,  взаимодействующих с базами  данных, в CLX практически такая же, как и в VCL. Для подключения к данным  в компонент CLX про сто внедряется объект типа  TFieldDataLink и реализуется обработка событий Da- taChange и UpdateData. Само собой  разумеется, что таким  же образом реализуются и свойства DataSource, DataField и ReadOnly, но их реализация точно такая  же, как в компонентах VCL.НА ЗАМЕТКУ

Не забудьте указать в разделе uses модуль DBCtrls вместо модуля QDBCtrls. В отличие от модуля DB, модуль DBCtrls не является общим для VCL и CLX. Класс TFieldDataLink определен как в модуле DBCtrls, так и в модуле QDBCtrls, но в Delphi 6 у версии VCL это- го класса не реализована обработка ошибок. Фактически такой компонент может корректно работать даже в среде Windows, однако при его переносе в Kylix компилятор выдаст мно- жество сообщений о синтаксических ошибках.

Существует  одна ситуация, о которой следует  сказать  особо.  Многие  компоненты VCL, взаимодействующие с базами  данных, для того чтобы  вызвать метод UpdateRe- cord, обрабатывают сообщение компонента cm_Exit.

Однако сообщение cm_Exit в CLX  не  реализовано, потому  вместо  метода  Up-

dateRecord необходимо использовать метод обработки события DoExit().

Класс TddgDBSpinner — это прямой наследник класса TddgImgListSpinner, ко 

торый, в свою  очередь, является прямым наследником класса  TddgDesignSpinner.

Одна  из  особенностей  класса  TddgDesignSpinner —   это  возможность  изменения

значения компонента Spinner при  помощи мыши  прямо  во время  разработки. Для

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

отключена, так как приводит к переходу  подключенного набора данных  в режим  ре дактирования. К сожалению, во  время  разработки приложения вывести набор  дан ных из режима редактирования невозможно, поэтому  в классе TddgDBSpinner пере определяется метод  DesignEventQuery(). В результате  этот  метод  просто возвра щает значение False, чтобы  компонент не обрабатывал события мыши  во время разработки.

Источник: Тейксейра, Стив, Пачеко, Ксавье.   Borland Delphi 6. Руководство разработчика. : Пер.  с англ. — М. : Издательский дом “Вильямс”, 2002. —  1120 с. : ил. — Парал. тит. англ.

По теме:

  • Комментарии