Глава 2

http://sybex.com - Примерите в книгата http://marcocantu.com marco@marcocantu.com - Сайта на автора

Задаването на  размерите на редактора става с RegEdit. Отиваш на ключа HKEY_CURRENTUSER\Software\Borlad\Delphi\4.0\Editor и добави два нови DWORD елемента, наречени DafaultHeight и  DefaultWidht, задаващи размерите на редактора в пиксели.

Прозореца Code Explorer дава типовете на всички променливи, процедури и др. Настройването на този прозорец се извършва с диалоговия прозорец Environment Options.

Най-важните настройки са свързани с класовете. Дефинициите на даден клас могат да бъдат показани по три начина:

- В зависимост от категориите, към които принадлежат: private, public и published.

- Разделени на методи и полета

- Всички събрани в една група:

При натискане на CTRL и посочване с мишката и натискане на бутона върху дума редактора търси думата.

CTRL + Shift + C - Генерира код.

CTRL + J - Изкарва програмни шаблони.

CTRL + Shift + интервал - Дава помощ за функцията след която се натиска

CTRL + Shift + цифра - отбелязват ред

CTRL + E - Лексикографско търсене

CTRL + Shift + I - Отместват едновременно няколко реда. Броя на интервалите е указан в опцията Block Indent в страницата Editor на диалоговия прозорец Environment Options.

CTRL + Shift + U - Обратно на горното.

CTRL + O + U - Променя регистъра на буквите.

CTRL + K + E; CTRL + K + F - редовни; главни // ама не действа

CTRL + Scrift + стрелка - преместват маркиран обект.

Прозорец за писане на код към даден обект се извиква чрез два пъти клик върху обекта. Може да се избере събитието чрез два пъти клик върху името в прозореца Object Inspector.

Отбелязване на редове в редактора

Маркирането на ред става с контекстното меню команда Toggle Bookmarks  и след това се посочва съответния номер на отбелязване от второстепенното меню. Това може да стане и с натискане на клавишите Ctrl+Shift+цифра. Отбелязването става в лявата част. За връщане към този ред се използва командата Goto Bookmarks от контекстното меню или с натискане на Ctrl+цифра.

Представяне на низове в паметта стр. 77

Пример D0101

Постави компонентата ListBox и я разшири. В последствие щракни два пъти с мишката в нея. Препиши от текста долу онова, което липсва в редактора на Delphi.

function StringStatus (Str: String): String;

Begin      { Така се пише коментар }

  Result := ‘Address ‘ + IntToStr (Integer (Str))

          + ‘, Length ‘ + IntToStr (Length (Str))

          + ‘, References ‘ + IntToStr (PInteger (Integer (Str) - 8) ^);

end;

procedure TForm1.FormCreate(Sender: TObject);

  var Str1, Str2, Str3: String;

      Z: Integer;

begin

Str1 := ‘Hello’;

Str2 := Str1;

Str3 := ‘Koko’;

ListBox1.Items.Add (‘Мара ба’);

ListBox1.Items.Add (Str1);

For Z := 1 to LENGTH (STR1) DO BEGIN

   ListBox1.Items.Add (Str1[Z]);

END;

ListBox1.Items.ADD (Str2);

ListBox1.Items.Add (‘Str3 = ‘ + Str3);

ListBox1.Items.ADD (‘Str1 = ‘ + StringStatus

(Str1));

ListBox1.Items.ADD (‘Str2 = ‘ + StringStatus (Str2));

 ListBox1.Items.ADD (‘Str3 = ‘ + StringStatus (Str3));

end;

Дългите низове завършват с #0. Може да се използват като се преобразуват до PChar, когато трябва да се предадат към някоя API функция на Windows.

Пример D0102

Копиране заглавието на форма в низ. Постави един бутон във формата. Натисни два пъти с мишката бутона и добави липсващия код. След това стартирай програмата, натисни бутона и гледай надписа в бутона.

procedure TForm1.Button1Click(Sender: TObject);

  var Str1, Str2: String;

begin

SetLength (Str1, 100);

GetWindowTextA (Handle, PChar (Str1), Length (Str1));

if Button1.Caption = Signs Then begin

Button1.Caption := Str1;

Str2 := Str1;

end

else begin

Button1.Caption := Signs;

Str2 := Signs;

end;

Label1.Caption := Str2 + ‘Това не излиза винаги на екрана понеже PChar слага нула в края на низа’;

end;

procedure TForm1.FormCreate(Sender: TObject);

begin

Signs := ‘Името на прозореца е:’;

Button1.Caption := Signs;

end;

Функцията Format изисква като параметър низ с основния текст и някой форматиращи символи (обикновено обозначени със %) и масив от стойности за всеки от форматиращите символи. Например, за да форматиращ две числа в един низ:

Format (‘First %n, Second %d’, [n1, n2] );

където n1, n2 са целочислени променливи. По време на компилация Format неправи проверка за типа на параметрите.

Таблица 2.2: Форматиращи символи във функцията Format:

d (десетично число)

Отговаряща целочислена стойност се преобразува до низ съдържащ десетични цифри

х (шестнадесетично число)

Отговаряща целочислен стойност се преобразува до низ, съдържащ шестнадесетични цифри.

р (указател)

Отговарящият указател се конвертира до низ, съдържащ шестнадесетичното му представяне.

s (низ)

Съответния низ, символ или PChar стойността се копира в изходния низ.

f (число с плаваща запетая)

Съответната стойност с плаваща запетая се преобразува в низ, базиран на стандартно представяне.

g (общо)

Съответната стойност с плаваща запетая се преобразува в най-късия възможен десетичен низ, базиран на експоненциално или стандартно представяне на число.

n (число)

Съответната стойност с плаваща запетая се преобразува в низ с плаваща запетая, но се използва и разделител между хилядите.

m (пари)

Съответната стойност с плаваща запетая се преобразува в низ, представящ количеството пари. Преобразуването се базира на регионалните настройки (Currency and date/time variables)

Пример: D0103

Сложи две контроли Label. От Object Inspector -> Font избери размер 12 ако искаш де. Щракни два пъти с   мишката в прозореца и добави липсващия код от текста долу.

procedure TForm1.FormCreate(Sender: TObject);

  var N1, N2: Real;

begin

   N1 := 33.22;

   N2 := 3.14159;

   Label1.Caption := Format (‘N1 = %3n, N2 = %8n’, [N1, N2]);

   Label2.Caption := Format (‘N1 = %3n, N2 = %.8n’, [N1, N2]);

   ShowMessage (Format (‘ N1 = %3n, N2 = %8n’, [N1, N2]));

end;

В долния пример и двата случая дефинират константи, чиято стойност не може да се изменя. При употреба на resourcestring низа се съхранява в ресурсите на програмата в таблицата с низовете.

const koko = ‘koko’;

resourcestring kiki = ‘kiki’;

Масиви

При деклариране на масив трябва да се зададе броя на елементите. Освобождаването на масива става чрез присвояване на променливата му стойност nul.

За да разбереш текущото състояние на масива може да използваш функциите Length, Low и  High;

Функцията SetLength може да променя дължината на масива. Ако го увеличава стойностите не се променят.

Пример D0104

// Функция за предаване на масиви като параметри може да я препишеш последно

Function Sum (M:  Array of Integer): Integer;

  var I: Integer;

begin

Result := 0;

For I := Low (M) to High (M) do Result := Result + M[I];

end;

procedure TForm1.FormCreate(Sender: TObject);

  var A, B: Array of Integer;

      I: Integer;

begin

SetLength (A, 10);

For I := Low(A) to High (A) do A[I] := I;

For I := High(A) downto Low (A) do ListBox1.Items.Add(IntToStr (A[I]));

B := Copy (A);

For I := Low (B) to High (B) do ListBox2.Items.Add (IntToStr (B[I]));

ListBox2.Items.ADD (‘Общо’);

ListBox2.Items.Add(IntToStr(Sum (B)));

end;

Функцията Copy премества съдържанието на един масив в друг, като замества първия със съдържанието на нов масив.

Array1 := Copy (Array 2, 0, 10); // Това не става

Ако искаш да предадеш само част от масива тогава:

Х := Sum (Slice (Array, 5));

Предаване на нетипизирани отворени масиви като параметри стр. 89

Представляват неопределен брой стойности, което може да бъде маса удобно при предаване на параметри.

Технически, обявяването на параметъра като масив от тип const ти позволява да предадеш масив с неопределен брой елементи от различни типове. За пример функцията format.

Function Format (const Format: string; const Args; Array of const): string;

Втория параметър на функцията е масив, който съдържа неопределен брой елементи от различни типове. Може да се извика функцията по някой от следните начини:

N := 20;

S := ‘Total:’;

Label1.Caption := Format (‘Total: %d’, [N]);

Lavel2.Caption := Format (‘Int: %d, Float %f’, [N, 12.4]);

Label3.Caption := Format (‘%s %d’, [S, N * 2]);

 

Може да се предаде като параметър или като константа стойност, или като стойност на променлива, или като израз. Декларирането на функция от този тип е просто, но как да се напише? Как да разбере типа на параметрите? Стойността на параметър на нетипизиран масив е съвместима с елементи от типа TVarRec.

Type TVarRec = record

case Byte of

vtInteger  :(VInteger :Integer; VType: Byte);

vtBoolean  :(VBoolean :Boolean);

vtChar     :(VChar :Char);

vtExtended :(VExtended :PExtended);

vtString   :(VString: PShortString);

vtPointer  :(VPointer :Pointer);

vtPchar    :(VPcahr: Pchar);

vtObject   :(VObject:TObject);

vtClass    :(VClass: TClass);

vtPwideChar  :(VPwideChar: WideChar);

vtAnsiString :(VAnsiString: Pointer);

vtCurrency :(VCurrency: PCurrency);

vtVariant  :(VVariant: PVariant);

vtInterface  :(VInterface: Pointer);

end;

Всяка променлива от този тип има поле Vtype (въпреки че това не е лесно да се забележи, тъй като тя се активира само веднъж) успоредно с фактическите данни с размера на типа Integer (обикновено обръщение или указател).

Сега вече може да се напише функция която може да работи с различни типове данни:

Функцията SumAll преобразува низовете в числа и символите в съответните им стойности и добавяйки 1 за стойност True на булевите променливи. Кодът е базиран на конструкцията case.

Пример D0105

Function SumAll (const Args: Array of const): Extended;

var I: Integer;

begin

Result := 0;

For I := Low (Args) To High (Args) Do

Case Args[I].VType Of

vtInteger:

Result := Result + Args [I].VInteger;

vtBoolean:

If Args [I].VBoolean Then Result := Result + 1;

vtChar:

Result := Result + Ord (Args [I].VChar);

vtExtended:

Result := Result + Args [I].VExtended^;

vtString:

Result := Result + StrToIntDef ( (Args [I].VString^), 0);

vtAnsiString:

Result := Result + StrToIntDef ( string (Args [I].VAnsiString), 0);

vtWideChar:

Result := Result + Ord (Args [I].VWideChar);

vtCurrency:

Result := Result + Args [I].VCurrency^;

end; //case

end; // Function SumAll

Извикването на функцията става:

var E: Extended;

begin

E := 10;

E := SumAll ([E * E]);

ShowMessage ( Format (

‘SumAll ([E * e, ‘’k’’, True, 10.34, ‘’999’’]) =>%n’, [E] ));

end;

Типа Varian: стр. 92

Пример D0106

var V: Variant;

I: Integer;

begin

V := 10; Edit1.Text := V;

V := ‘Mara ba’; Edit2.Text := V;

V := 12.5; Edit3.Text := V;

I := Integer (V) * 2;

V := I; Edit4.Text := V

end;

Типа за структура с вариант с име TVarData, който има същото разположение на паметта като типа Variant. Може да се използва за получаване достъп до определен тип вариант. Пълно описание на типовете може да се намерят в помощната информация “Values in variables”.

Вариантите са бавни!

Пример D0107

За онагледяване ще използваме следния пример с VSpeed:

procedure TForm1.Button1Click(Sender: TObject);

var V1, V2, Total: Variant;

Time1, Time2: TDateTime;

begin

Time1 := Now;

V1 := 0;

V2 := 0;

ProgressBar1.Position := 0;

While V1 < 50000 Do Begin

V2 := V2 + V1;

Inc (V1);

If (V1 mod 500) Then Begin

ProgressBar1.Position := V1 div 500;

Application.ProcessMessages;

End; //If

End; //While

//Трябва да използваме резултата

Total := V2;

Time2 := Now;

Label1.Caption := FormatDateTime (‘n:ss’, Time2-Time1) + ‘ second’;

end;

function TDate.GetMonth: Integer;

var Y, M, D: Word;

Begin

DecodeDate (fDate, Y, M, D);

Result := M;

end;

procedure TDate.SetMonth (const Value: Integer);

Begin

if (Value < 0) or (Value > 12) Then

Raise EDateOutOfRange.Create (‘Invalid month’);

SetValue (Year, Value, Day);

end;

type EDateOutOfRange = class (Exception);

Преопределяне на функции стр. 96

Разгледай следните декларации на функции намиращи се в модула Math от библиотеката VCL.

function Min (A, B: Integer ): Integer; Overload;

function Min (A, B: Int64): Int64; Overload;

function Min (A, B: Single): Single; Overload;

function Min (A, B: Double): Double; Overload;

function Min (A, B: Extended): Extended; Overload;

Типът на връщания резултат е Integer. Има две основни правила:

- Всеки вариант на подпрограма трябва да се следва от ключовата дума OVERLOAD.

- Разликите трябва да са в броя или в типа на параметрите, или и в двете. Типът на връщаната стойност не може да се използва, за да се различат двете подпрограми.

Пример D0108

Ето три варианта на процедурата ShowMsg_n:

Procedure ShowMsg_1 (str: String); Overload;

Begin

MessageDlg(str, mtInformation, [mbOK], 0);

end;

Procedure ShowMsg_2 (FormatStr: String; Params: Array of const); Overload;

begin

  MessageDlg( Format (FormatStr, Params), mtInformation, [mbOK], 0 );

end;

Procedure ShowMsg_3 ( I: Integer; str: String ); Overload;

begin

  MessageDlg (IntToStr (I) + ‘ ‘ + str, mtInformation, [mbOK], 0)

end;

procedure TForm1.Button1Click(Sender: TObject);

begin

ShowMsg_1 (‘Първо повикване’);

end;

procedure TForm1.Button2Click(Sender: TObject);

begin

ShowMsg_2 (‘Total = %d’, [100]);

end;

//Извиване на функциите

procedure TForm1.Button3Click(Sender: TObject);

begin

ShowMsg_3 ( 10, ‘koko’);

end;

Всяка от версиите на преопределена подпрограма трябва да бъде маркирана по съответния начин, т.е. не може да преопределиш подпрограмата в същия модул, ако не използваш думата OVERLOAD. Без това се получава съобщение за грешка “Previus declaration of ‘<name>’ was not marked with the overload direkcite” - Предишната декларация <name> не е отбелязана с директивата ‘overload’ .

Можеш да добавиш модул съдържащ:

procedure MessageDlg (str: string); overload;

begin

Dialog.sDialogMsg (str, mtInformation, [mbOK], 0);

end;

Стойности по подразбиране стр. 99

Може да се създадат стойности по подразбиране и да се пропуснат при извикването на функцията.

Пример D0109

PROCEDURE MsgBox (MSG: String; Caption: String = ‘Warning’; Flags: LongInt = mb_OK or mb_IconHand);

begin

  Application.MessageBox(PChar(MSG), PChar (Caption), Flags);

end;

procedure TForm1.FormCreate(Sender: TObject);

begin

MsgBox(‘Something wrong here!’);

MsgBox(‘Something wron here!’, ‘Attenion’);

MsgBox(‘Hello’, ‘Message’, mb_OK);

Form1.Deactivate;

Form1.Close;

Form1.Release; //Само това едейства до някъде

end;

Могат да се пропускат само последните параметри!!!!!!

Windows манипулатори стр. 101

Вътрешен код, който може да се използва, за указване на специфичен елемент, управляван от системата, включително прозорци, икони, блокове в паметта, и др.

SetWindowText (Handle, ‘Hi’);

Caption := IntToStr (Handle);

Външни декларации стр. 103

Служат за свързване на кода на Delphi с външни функции, написани на асемблер за извикване на функции от DLL (dinamic link library - библиотеки за динамично свързване). Има няколко такива декларации в модула Windows:

// Предварителна декларация

funcion LineTo (DC: HDC; X, Y; Integer): bool; atdcall;

// Външна декларация (вместо истинска дефиниция)

function LineTo; external ‘gbi32.dll’ name ‘LineTo’;

Тази декларация означава, че кода на функцията LineTo се намира в GDI32.DLL.

Процедурни типове

Показват броя и типа на параметрите и незадължително типа на външната стойност в случай на функцията. Например може да декларираш типа “процедура с един целочислен параметър, предаван по псевдоним” по следния начин:

type

IntProc = procedure (var Num: Integer);

IntcProc2 = procedure;

Този процедурен тип е съвместим с всяка подпрограма, имаща същите параметри.

Процедурните типове могат да се използват за две различни цели:

- за деклариране на променливи от процедурния тип;

- предаване процедурен тип като параметър на друга под програма:

Пример D0110

IntProc =  procedure (Var Num: Integer);

IntProc2 = procedure;

var

  Form1: TForm1;

implementation

{$R *.dfm}

procedure DoubleTheValue (Var Value: Integer);

begin

Value := Value * 2;

MessageDlg (IntToStr (Value), mtInformation, [mbOK], 0);

end;

procedure kiki;

begin

  MessageDlg(‘Здрасти аз съм КИКИ’, mtInformation, [mbOk], 0);

end;

procedure koko;

begin

  MessageDlg(‘Мара ба ба! Аз пък съм КОКО’, mtInformation, [mbOK], 0);

end;

procedure TForm1.FormCreate(Sender: TObject);

var IP: IntProc;

IP2: IntProc2;

X: Integer;

begin

 IP := DoubleTheValue;

X := 5;

IP (X);

IP2 := KIKI;

IP2;

IP2 := KOKO;

IP2;

end;

CallBack функции

Някой API функции извършват някое действие върху даден брой вътрешни елементи за системата, като например над всички прозорци от определен тип. Такива функции също се наричат enumerated и изискват като параметър действието, което да извършат над всеки от елементите и което е предадено като функция или процедура, съвместима с определен процедурен тип. Сега ще разгледаме API функцията Enum Window.

type

EnumWindowsProc = function (Hwnd: THandle; Param: Pointer): Boolen; stdCall;

Първия параметър е манипулатор към текущият прозорец от последователността, докато втория е стойността, която предаваме при извикването на функцията EnumWindows. Типа TNFndEnumProc не е дефиниран подходящо - той е указател. Трябва да предоставим функцията със съответните параметри и след това да я използваме като указател, взимайки адреса на функцията, вместо да я извикваме. Компилатора не ни оказва съдействие при извикване на функция от грешен тип.

Това е дефиниция на съвместима функция, която чете заглавието на прозореца в символен низ и след това го добавя в един списък.

Пример D0111

EnumWindowsProc = function (Hwnd: THandle; Param: Pointer): Boolean; StdCall;

var

  Form1: TForm1;

implementation

{$R *.dfm}

Function GetTitle (Hwnd: THandle; Param: Pointer): Boolean; StdCall;

  var Text: String;

Begin

SetLength (Text, 100);

GetWindowText (Hwnd, PChar (Text), 100);

Form1.ListBox1.Items.Add( IntToStr (Hwnd) + ‘; ‘ + Text);

Result := True;

end;

procedure TForm1.FormCreate(Sender: TObject);

  var EWProc: EnumWindowsProc;

begin

EWProc := GetTitle;

EnumWindows(@EWProc, 0);

end;

Формата има списък върху почти цялата си област

Следващата програма получава параметър от командния ред (съхранен в системната променлива cmdLine) и след това може да се прочете с функциите ParamCount и ParamStr. Първата връща броя на аргументите, а втората съдържанието им.

!!! Това е файл DPR a не PAS !!!

Напиши го с бележника, запиши го и го отвори с Delphi

Пример StrParam.DPR

program StrParam;

Uses Windows;

Begin

  MessageBox (0, cmdLine, ‘StrParam Command Line’, mb_OK); //Показва първия параметър

  if (ParamCount > 0) THEN

    MessageBox (0, PChar (ParamStr (1)), ‘1st StrParam Parameter’, mb_OK)

  else

    MessageBox (0, PChar (‘No Params’), ‘1st StrParam Parameter’, mb_OK);

end.

За предаване на параметър може да се използва Run>Parameter от менюто на Delphi или чрез завличане на файл към програмата с мишката.

Модули и структура на програмата

При програмиране усилено се използват модули (unitis). При добавяне на форма към проект всъщност се добавя нов модул.

Въпреки, че всяка форма е дефинирана в модул, обратното нещо не е вярно. Модулите не е обхбодимо да дефинират форми; те могат да дефинират и представят набор от функции. Чрез избирането на командата File -> New -> Unit се добавя модул към текущия проект. Този празен модул съдържа следния код, разделящ частите на модула:

unit Unit1;

interface

implementation

end.

Модула има уникално име отговарящо на файловото му име - интерфейсна част (interface), в която се декларират видимите от другите модули елементи на модула, и част за реализацията (implementation), съдържаща истинския код и други скрити декларации. Накрая модула може да има незадължителна част за инициализация (initialization) и финална част (finalization) която да се изпълни при спиране на програмата.

Общата структура на модула е следната:

unit UnitName;

interface

//Други модули, към които ще се обръщаме

uses

  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs;

//Видими за другите модули дефиниции на типoве

type

  TForm1 = class(TForm)

//Видими за другите модули константи

const

zero = 0;

// Глобални променливи

var

  Form1: TForm1;

// Списък на видимите от другите модули и функции процедури

proceduure MyProc;

implementation

uses D, E;

//скрити глобални променливи

var P: Integer;

procedure MyProc;

begin

end;

initialization

//незадължителна инициализираща част

finalization

//незадължителна част за освобождаване на ресурси

end.

Клаузата Uses в началото на интерфейсната част показва кои други модули трябва да са достъпни в интерфейсната част на модула. това включва модулите, дефиниращи типовете от данни, които ще използваме в дефиницията на други типове данни, като например компонентите, които дефинираме в дадена форма.

Втората кауза uses в началото на частта за реализация включва още модули, които са необходими само в кода, реализиращ функциите и процедурите. Когато трябва да се обърнеш към други модули от кода на процедурите и функциите, трябва да се добавят имената на тези модули, които използваш. Всички модули трябва да са или в директорията на проекта или в директорията от пътя за търсене (Directoriesconditionals на прозореца Options).

Локални променливи: Областта на видимост е в тялото на процедурата включително и вложените процедури (освен ако във вложената процедура има променлива със същото име и те препокрива външния идентификатор). Паметта за тази променлива се заделя в стека на процедурата по време на изпълнението. След завършване стека се изчиства.

Скрити глобални променливи: Ако декларираш идентификатор в частта за реализация на модула, не можеш да го използваш извън модула, но може да го използваш във всеки блок и процедура, дефинирани в този модул. Паметта за тези променливи се заделя по време на стартиране и се освобождава при завършване на програмата. Може да използваш инициализиращата част на модула, за да зададеш първоначална стойност на променлива.

Глобални променливи: Ако декларираш променлива в интерфейсната част на модула, нейната област на видимост ще се разпростира върху всеки модул, който използва текущия. Тази променлива използва паметта и има жизнен цикъл като променливите от предишната група. Разликата е в областта на видимост.

Модули и конфликти с идентификатори

Клаузата uses е стандартния начин за получаване на достъп до друг модул. След тази клауза програмата има достъп до дефинициите в модула. Но може да се случи така, че два модула да имат подпрограми с едно и също име.

В този случай се използва името на модула като префикс към името на подпрограмата. Например коко.коко.

Структурата на програмен файл е по-проста от тази на модулите. Ето пример за програмен модул.

program Project1;

Uses Forms, Form1 in ‘Form1.PAS’{Form1DateTime};

Begin

Appliation.Initialize;

Application.CreateForm (TForm1.Form1);

Application.Run;

end.

Глава 3

Класове и обекти стр. 118

Класът е тип данни, дефиниран от потребителя, който има състояние (негово представяне) и няколко операции (негово представяне). Той има някой вътрешни данни и методи под формата на процедури и функции и обикновено описва родовите характеристики и поведението на даден брой сходни обекти.

Обектът е инстанция на даден клас или, с други думи, променлива от типа данни, който е дефиниран от класа. Обектите са действителни величини. Когато програмата се изпълнява, обектите заемат някаква памет за вътрешното си представяне. Взаимовръзката между обекти и клас е същата, както между променлива и тип.

За да дефинирате нов клас с няколко локални полета и методи, използва следния синтаксис:

Type

TDate = class

Month, Day, Year: Integer;

procedure SetValue (M, D, Y: Integer);

function LeapYear: Boolean;

end;

Функцията и процедурата трябва да бъдат дефинирани по-късно, като за показване на тяхната принадлежност към класа TData се използва точкова нотация:

Procedure TDate.SetValue (M, D, Y: Integer);

begin

Month := M;

Day := D;

Year := Y;

end;

function TDate.LeapYear:Boolean;

begin

if (Year mod 4 <> 0) then

LeapYear := False

else

if (Year mod 100 <> 0) then LeapYear := True

else

if (Year mod 400 <> 0) Then LeapYear := False

else

LeapYear := True;

end;

При декларация на променливи не се създава обект. Само се запазва място в паметта. Инстанциите на класовете трябва да се създават ръчно, за допълнително дефинираните класове.

За създаването на инстанция на даден клас, трябва да се извика метода Create, който се нарича конструктор. Конструктора не се прилага върху обекта. Този конструктор се наследява от всички класове. Обекта се унищожава чрез командата Free.

Видимост на променливите:

private - само във текущия файл;

publbic - за всяка друга част на програмата;

protected - в текущия клас и неговите наследници.

type

TDate = class

private

Month, Day, Year: Integer;

Public

procedure SetValue (m, d, y: Integer);

function LeapYear: Boolean;

function GetText: String;

procedure Increase;

end;//class

procedure TDate.SetValue (m, d, y: Integer);

begin

end;

function TDate.LeapYear: Boolean;

Begin

end;

function TDate.GetText: String;

begin

end;

procedure TDate.Increase;

begin

end;

В този пример полетата на класа са декларирани като private с няколко допълнителни метода. Първият, GetText, е функция, която връща символен низ, съдържащ датата

Вторият метод е процедурата Increase, която увеличава датата с един ден. Това е много полезно, ако трябва да се съобразявате с различния брой дни в месеците. Сега може да се промени класа така, че във вътрешната му реализация лесно да може да се използва стандартния клас за работа с дати. Дефиницията малко се променя:

type

TDate = class

private

fDate: TDateTime;

Public

procedure SetValue (m, d, y: Integer);

function LeapYear: Boolean;

function GetText: String;

procedure Increase;

end;//class

При промяна в private частта на класа, ако няма програми, които използват този клас, няма нужда да се променят всички. Това е предимството на капсулоането на данните

Капсуловане и форми

Идеята е да се нали броя на глобалните променливи. Глобалната променлива може да се промени от всяка част на програмата. От друга страна като се промени представянето на клас трябва да се променят само някой методи на класа. Скриването на информацията се дължи на капсуловани променливи. Ето и пример: Когато имаш програма с няколко форми, може да се направи някоя променлива достъпна за всяка форма, декларирайки я като глобална в интерфейсната част на една от формите:

Var

Form1: TForm1;

onclicks: Integer;

Ако искаш всяка форма от този тип да си има свое собствено копие на променливата:

type

Form1 = class (TForm)

public

nClick: Integer;

end;

Ако искаш стойността на променливата да е достъпна от останалите форми само за четене се декларира като private и се предоставя метод за получаване на нейната текуща стойност:

type

Form1 = class (TForm)

public

function GetClicks: Integer;

private

nClicks: Integer;

end; //class

function TForm1.GetClicks: Ingeter;

begin

  Result := nClick;e

end;

Едно по-добро решение е да добавиш променливата като характеристика на формата, но за това по-нататък.

Ключовата дума Self стр. 123

Методите имат подразбиращ се параметър който сочи към текущ обект. В даден метод може да се обърнеш към този обект (параметър) използвайки self. Това е необходимо, когато се дефинират няколко обекта от един и същи клас, тъй като пролагаш метода към един от всички тези обекти. Той ще работи с данните само на този обект и няма да окаже влияние върху другите обекти. Например в метода SetValue на класа TDate ще използваме полетата Month, Year и Day. За да се обърнем към полето на текущ обект се пише:

Self.Month := m;

Self.Day := d;

Динамично създаване на компоненти стр. 124

Когато трябва да се създаде компонента по време на изпълнение на програмата трябва да се подаде собственика (owner) на компонентата на конструктора Create или пък да зададеш същата стойност на характеристиката Parent. И в двата случая се предава текущата форма като параметър се използва self.

Пример D0301

procedure TForm1.FormMouseDown(Sender: TObject; Button: TMouseButton;

Shift: TShiftState; X, Y: Integer);

begin

WITH TButton.Create (self) do Begin

Parent := self;

Left := X;

Top := Y;

Width := Width + 50;

Caption := Format (‘Button at %d, %d’, [X, Y]);

end; //with

end;

procedure TForm1.FormCreate(Sender: TObject);

begin

  Form1.Caption := ‘Създава бутон при натискане на мишката’;

end;

Деклариране на конструктор стр. 126

За заделяне на памет за обект се извиква метода Create. Преди използването на обекта се налага инициализация. Например, трябва да извикаме метода SetValue на класа TDate, след като сме създали обекта. Алтернативата е да направим собствен конструктор, който да създаде обекта и да инициализира стойността на датата. Новия конструктор може да се нарече Create или с друго име. Просто се използува думата constructor вместо procedure в декларацията на метода. Конструктора е специализиран вид процедура, тъй като при прилагането му за даден клас автоматично се заделя памет за обект от този клас. Главната причина за създаване на собствен конструктор е инициализацията на полетата. Ако се създават обекти, без инициализация е възможно да се получи странен резултат или грешка.

Ако вместо Create използваш друго име за конструктор имай в пред вид, че конструкторът Create на базовия клас TObject все още е достъпен. Дефиниране на нов клас изтрива стария със същото име.

Класът може да има и деструктор. Това е процедура която освобождава ресурсите заети от конструктора преди той да бъде унищожен.

Предопределени методи стр. 127

Това са повтарящи се конструктори и процедури.

type

TDate = class

procedure SetValue (y, m, d: Integer); Overload;

procedure SetValue (NewDate: TDateTime); Overload;

end; //class

procedure TDate.SetValue (y, m, d: Integer);

Begin

  fDate := EncodeDate (y, m, d);

end; //

procedure TDate.SetValue (NewDate: TDate);

Begin

fDate := NewDate;

end;//

Към този клас са добавяне и два различни конструктора Create - един без параметри, който покрива подразбиращия се конструктор, и един с инициализиращи стойности. Конструктора без параметри използва текущата дата.

type

TDate = class

public

constructor Create; overload;

constructor Create (y, m, d: Integer);

constructor TData.Create (y, m, d: Integer);

begin

  fDate := EncodeDate (y, m, d);

end;

constructor TDate. Create;

begin

  fDate := Date;

end;

Извикват се по този начин:

Day1 := TDate.Create (1998, 12, 25);

Day2 := TDate.Create //днешна дата

Завършения клас TDate

type

TDate = class

private

fDate: TDateTime;

function GetYear: Integer;

public

constructor Create; overload;

constructor Create (Y, M, D: Integer); overload;

procedure SetValue (Y, M, D: Integer); overload;

procedure SetValue (NewDate: TDateTime); overload;

function LeapYear: Boolean;

procedure Increase (NumberOfDays: Integer = 1);

procedure Decrease (NumberOfDays: Integer = 1);

function GetText: String;

end;

Ако методите Incease и Decrease се извикат без параметър те променят текущата дата с един ден. Ако се използва параметъра NumberOfDays методите добавят или изведат това число.

procedure TDate.Incease (NumberOfDays: Integer = 1)

begin

fDate := fDate + NumberOfDays;

end;

Методът GetText връща символен низ, съдържащ форматираната дата, използвайки функцията DateToStr;

function TDate.GetText: String;

begin

GetText := DateToString (FDate);

end;

Това го пропускам щото е тъпо.

begin

TheDay := TDate.Create (1998, 12, 25);

Label1.Caption := TheDay.GetText;

TheDay.Increase;

Label2.Caption := TheDay.GetText;

TheDay.Increase (10);

Label3.Caption := TheDay.GetText;

end;

Друг начин да се напише последния метод е да се унищожи текущия обект и на негово място да се създаде нов.

procedure name

var NewDay:TDate;

begin

TheDay.Free;

NewDay := TDate.Create;

TheDay := NewDay;

Label1.Caption := TheDay.GetText;

end;

Създаването на нов обект и унищожаването на стария отнема време.

Наследяване на съществуващи типове стр. 132

За да се наследи съществуващ клас трябва да се спомене в началото на декларацията:

type

TForm1 = class (TForm)

end;

Това означава, че класът TForm1 наследява целият клас TForm.

Наследяване и съвместимост на типовете

Правилото е, че две стойности са със съвместими типове само когато са от един и същ тип данни или ако техните типове данни имат еднакви имена.

Има едно важно изключение от това правило с типове класове. Ако делракираш един клас, например TAnimal, и създадеш негов наследник, например TDog може да присвоиш обект от тип TDog на променлива от от тип TAnimal

type

TAnimal = class

public

constructor Create;

function GetKind: string;

private

King: STring;

end;

TDog = class (TAnimal)

public

constructor Create;

end;

В дата метода Create просто се задава стойността на полето King, която се връща от метода GetKing.

Късно свързване и полиморфизъм стр. 136

Обектно ориентираните езици за програмиране позволяват използването на друг вид, свързване известно като динамично свързване или късно свързване. В случая истинския адрес на извиквания модел се изчислява по време на изпълнение на програмата. Ползата е т.н. полиморфизъм. Основната идея е, че може да се извика метод чрез променлива.

Например: ако имаш един клас и негов наследник (TAnimal  и TDog) които дефинират един и същ метод и той подлежи на късно свързване. Сега можеш да приложиш този метод към променлива от тип TAnimal с име MyAnimal, която по време на изпълнение на програмата може да сочи и към обект от тип TAnimal и към обект TDog. Кой точно метод ще извика, трябва да се установи по време на изпълнение на програмата в зависимост от типа на текущо сочения обект.

В тази дефиниция класовете TAnimal и TDog имат един метод Voice. Този метод е дефиниран като виртуален в класа TAnimal и след това е препокрит от класа TDog с помощта на ключовите думи virtual и override.

type

TAnimal = class

public

function Voice: Strintg; virtual;

TDog = class (TAnimal)

public

function Voice: string; override;

Разбира се методите трябва и да се реализират:

uses

MMSystem;

function TAnimal.Voice String;

begin

Voice := ‘Voice of the animal’;

PlaySound (‘Anim.wav’, 0,snd_Async);

end;

function TDog.Voice: String;

begin

Voice := ‘Art Art’;

PlaySoud (‘Dog.wav’, 0,snd_Async);

end;

Ako променливата MyAnimal в момента сочи към обект от класа TAnimal, ще се извика методът TAnimal.Voice и обратно. Това е защото функцията е виртуална.

Препокриване на методи стр. 139

Осъществява се с ключовата дума override.

type

myClas = class

procedure Two: /статичен метод

procedure one; virtual;

end;

mySubclass = class (myClass)

procedure One: override;

procedure Two;

end;

Обработка на съобщения

Ключовата дума message дефинира методи за обработка на съобщения

Type

....

procedure M (var Message: TMessage); message USER;

Абстрактни методи

Ключовата дума abstract се използва за деклариране на методи, които ще се дефинират единствено в наследниците на текущия клас.

public

constructor Create;

function GetKing: string;

function Voice: string: virtual: abstract;

private

king: string;

Информация за типовете по време на изпълнение на програмата

Става чрез оператора is. Параметрите са обект и тип клас, а връщаща стойност е Boolean:

if Myanimal is Tdog then

MyDog := TDog (My Animal);

Text := MyDog.Eat;

Изразът връща True ако обекта MyAnimal в момента сочи към  обект от клас TDog или негов наследник. Същата операция може да се извърши и с оператора as.

(MyAnimal as TDog).Eat; // Ако искаш да извикаш функцията Eat.

За да се избегнат грешки може да се използва:

  if MyAanimal is TDog then TDog (MyAnimal).Eat;

procedure TForm1.Button1Click (Sender: TObject);

begin

if Sender is TButton then

.....

end;

Наследяване на форми стр. 147 Пример D302

Главната полза е, че може по-късно да се промени базовата форма и така да се актуализират всички наследници. Описано в в пример VF1. Създай нов проект и добави 4 бутона в главната му форма. В последствие от главното меню избери File->New и посочи страница с името на проекта от  диалоговия прозорец New Items може да посочиш формата която искаш да наследиш. Новата форма ще има също 4 бутона а първоначалното и описание ще бъде:

inherited Form2: TForm2

Left = 313;

Top = 200;

Caption = ‘mara ba’

end;

Ако промениш описанието на единия бутон то другия в другата форма ще си промени името.

Дефиниране на интерфейс стр. 155

Може да се напише чисто абстрактна декларация на клас, т.е. съдържащ само абстрактни виртуални методи. Това става с използване на ключовата дума interface. Поради това тези класове се наричат интерфейси. Те са въведени за поддръжка на СОМ програмирането. Имат и маса други предимства:

- Класът може да наследява само един клас, но може да реализира множество интерфейси;

- Интерфейсните обекти имат свой брояч и за това колко променливи ги използват и автоматично се унищожават, ако вече не се използват;

- VCL вече поддържа няколко базови класа за реализиране на функционалността. В програма която не експортира СОМ обекти, може да използваш класа TInterfacedObject.

Ето и синтаксисът за деклариране на интерфейс (чието име по конвенциите започва с I):

type

ICanFly = interface

[‘{10000000000-0000-0000-0000000000000000}’]

function Fly:String;

end;

След декларацията на интерфейса може да се дефинира клас, който да го реализира:

type

  TAirplane = class (TInterfacedObject,ICanFly);

function Fly: String; virtual;

  end;

Този клас може да произлиза от TInterfacedObject, за да наследи реализацията на методите на IUnknown. Въпреки, че не е задължително, това е единствения подход, ако искаш да модифицираш тези методи в бъдещи наследници.

След дефиниране и реализация на интерфейса може да запишем:

var Airplane1: TAirplane;

begin

Airplane1 := TAirplane.Create;

Airplane1.Fly;

Airplane1.Free;

end;

Но може да използваме и променливи от интерфейсния тип:

var Flyer1: ICanFly;

Begin

Flyer1 := TAirplane.Create;

Flyer1.Fly;

end;

В момента, в който свържеш обект с интерфейсна променлива, автоматично се проверява дали този обект реализира интерфейса, използвайки специалната версия на оператора as. Тази проверка може да се направи и ръчно:

Flyer1 := TAirplane.Create as ICanFly;

Горния фрагмент няма код за унищожаване.

Стандартен и интерфейсен полиморфизъм - стр. 160

Има два начина за използване на полиморфизъм при интерфейсите. Може да се декларира и инициализира масив от обекти и след това да използваш различните интерфейси в даден обект или директно да използваш масив от интерфейси. В примера се използват и двата метода.

private

animals: Array [1..5] of TAnimal;

AnimIntf: Array [1..5][ of IAnimal;

Извикването на методите е директно:

for I := 1 to 5 do

Memo1.Lines.Add (Animals [I].King);

или

Мемо1.Lines.Add (AnimIntf [I].King);

Глава 4

Клас съдържащ броя на инстанциите си - стр. 175

type

Tcount = class (TButton)

constructor (Aowner: TConpomnt); override;

destructor Destroy; override;

class function GetTotal: Ingeger;

end; //class

Всеки път, когато се създава инстанция, програмата увеличава брояча, преди да извика конструктор на базовия клас. Всеки път, когато се унищожава обект, броячът се намалява:

constructor TCountButton.Create (Aowner: TComponent);

begin

inherited;

Inc (TotBtns);

end;

destructor TCountButton.DEstroy;

begin

Dec (TotBtns);

ingerited Destroy;

end;

Брояча представлява променлива, декларирана в частта за реализация на модула и недостъпна извън него. Единствено статичния метод позволява да се получи неговата стойност.

implementation

var totBtns: Ingeger = 0; // само дето това се пише преди горното де ама както и да е

class function TCountButton.GetTotal: Ingeter;

begin

  Result := TotBtns;

end;

Сега може да създадеш обекти от този нов тип:

procedure TForm1.FormMouse Down ( .........

begin

with TCountButton.Create (self) do begin

Parent := self;

left := X

top := Y;

Width := Width = 60;

Caption := Format (‘%d Button at %d, %d’, [GetTotal, X, Y]);

end; //with

end;

Указатели към методи - стр. 179

Същия като процедурен тип но сочи към метод.

type

IntProceduralType = procedure (Num: Ingerer);

IntMethodPointerType = procedure (Num: Ingeter) of object;

Като декларираш указател към метод като горния, можеш да имаш поле на клас от такъв тип, както при следващия клас:

type

MyClass = class

Value: Integer;

Operation: IntMethodPointerType;

end;

По-късно можеш да зададеш като стойност на това поле всеки метод, съвместим с декларирания тип. Можеш да декларираш друг клас имащ метод със същия целочислен параметър;

type

AnotherClass = class

X: Ingeter;

procedure Add (N: Ingeter);

end;

Ako декларираш един обект от два класа:

var

MyObject: MyClass;

AnotherObject: AnotherClass;

можеш да направиш присвояването:

MyObject.Operaation := AnotherObject.Add;

В този момент ако извикаш метода:

MyObject.Operation;

ще извикаш метода ADD на обекта от тип AanotherObject.

Дефиниране на характеристики - стр. 189

Характеристиките са атрибути, които определят състоянието и поведението на обекта. Характеристиката най-общо представлява име, за което са дефинирани методи за четене (read) и писане (write) или чрез което се осъществява директен достъп до данни. Ето дефиницията на характеристиката на обекта, представляващ датата:

property Month: Integer

  read FMonth write SetMonth;

За да получиш стойността на характеристиката Month, този код ще прочете стойността на private полет FMonth, докато при смяна на стойността ще извика методът SetMonthy. Възможни са различни комбинации но употребата на метод е най-често използвания вариант:

property Months: Integer

  read GetMonth write GetMonth;

property month: Integer

  read FMonth write FMonth;

Ako характеристиката е само за четене директивата write може да се пропусне. Може да се пропусне и read. Характеристиките предназначени за достъп по време на създаване на формата, са декларирани в частта published на декларацията на класа. Всички read-only характеристики трябва да се дефинирани в частта public. За да провериш стойността published на характеристика използвай Object Inspector. Той показва само desing-time характеристиките, а read-only  не се показват.

Добавяне на характеристика към формата - стр. 191

В декларацията на класа напиши името и типа на характеристиката:

public

property Clicks: Integer;

След това натисни Ctrl+Shift+С за да активираш дописването на кода (Code Competion). Според книгата ще скиваш следната промяна на кода във формата:

type

TForm1 = class(TForm)

private

FClicks: Integer;

procedure SetClicks(const Value: Integer);

{ Private declarations }

  public

{ Public declarations }

property Clicks: Integer read FClicks write SetClicks;

  end;

var

  Form1: TForm1;

implementation

{$R *.dfm}

{ TForm1 }

procedure TForm1.SetClicks(const Value: Integer);

begin

  FClicks := Value;

end;

end.

Това икономисвало маса писане например:

  Form1.StatusBar.SimpleText := ‘New text’;

Може да се използва алтернативата

property StatusText: String;

read GetText write SetText;

и натисни Ctrl+Shift+C за да се дефинират методите на характеристиката:

Добавяне на характеристики към класа TDate - стр. 193

  public

  ....

property Day: Integer read GetDay write SetDay;

property Month: Ingeter read GetMonth write SetMonth;

property Year: Integer read GetYear wrtite SetYear;

property Text: String read GetText;

 

function TForm1.GetText: String;

begin

end;

Function TForm1.SetText (Const Value: String);

begin

end;

Ето и двата метода свързани със характеристиката Monht:

function TDate.GetMonth: Ingeter;

var Y, M, D: Word;

begin

DecodeDate (fDate, Y, M, D);

Result = M;

end;

procedure TDate.SetMonth (const Value: Integer);

begin

  if (Value < 0) or (Value > 12) then

raise EDateOutOfRange.Create (‘Invalid month’);

  SetValue (Year, Value, Day);

end;

Събития - стр. 196

Събитията са характеристики.

Application.ProcessMessages; //like Microsoft's DoEvents ;-)

Да добавим събитие към класа TDate.

type

TDate = class

private

FOnChange: TNotifyEvent;

...

  protected

procedure DoChange: Virtual;

...

  public

property onCnage: TNotifyEvent

read FOnChange write FOnChange;

...

  end;

Създаване на компонентата TDate - стр. 199

Първо трябва да наследим нашия клас от класа TComponent, а не от класа по подразбиране TObject:

type

  TDate = class (TComponent)

  ...

  public

constructor Create (AOwner: TComponent); overload; override;

constructor Create (Y, M, D: Ingeter); reintroduce; override;

Втората стъпка е добавянето на нов конструктор на класа, препокриващ подразбиращия се конструктор на компонентите, за да зададем подходяща начална стойност. Новия конструктор задава стойност на датата и извиква конструктора на родителския клас.

constructor TDate.Create (AOwner: TComponent);

var Y, D, M: Word;

begin

inherited Create (AOwner);

fDate := Date;

end;

След това трябва да се прибави към модула дефиниращ нашия клас (файла DATES.PAS в DATECOMP директорията), процедурата Registe за да се добави компонентата към палитрата с компоненти на Delphi. Декларирай в интерфейсната част на модула коя не изкисва параметри, и в частта за реализация напиши:

procedure Register;

begin

  RegisterComponents (‘Md4’, [TDate]);

end;

Този код добава новата компонента в страницата Md4 на палитрата с компоненти и ако е необходимо, страницата бива създадена.

Последната стъпка е да се инсталира компонентата. Избери страницата Into exicting package (тя е подразбиращата се), избери DCLUSR40.PAK и след това въведи името на файла на компонентата DATE.PAS. Сега вече в палитрата се намира нова страница Md4 съдържаща новата компонента.

Класът TObject - стр. 201

Всеки клас в системата е наследник на класа TObject, така цялата йерархия има един корен. Например, обработчиците на събитията обикновено имат параметър Sender от тип TObject. Недостатък е, че трябва да се знае типа на обекта. Ако тази променлива или параметър сочи към обект от тип TButton, не може да се използва характеристиката му Caption директно. Решение за случая дават RTTI операторите is и as за да извършват проверка за типа на обекта и съответното преобразуване на типовете. Например ако параметъра Sender, който е от тип TOject, сочи към обекта от тип TButton, може да напишеш следния ред:

(Sender as TButton).Caption

Предимството на този подход е, че ако преобразуването не може да се осъществи delphi генерира изключение. Някой от класовете връщат стринг и името на класа.

Метода InstanceSize връща размера на обекта по време на работа на програмата. Може да се използва и глобалната функция SizeOf. Функцията връща размера на променливата сочеща към обекта.

Показване на информацията за клас - стр. 202

Постави бутон в ново приложение. Добави модула дефиниращ компонентата TDate, използвай конструктора uses за да се обърнеш към нейния код в главната програма.

Пример D0401.pas

procedure ShowInfo (Obj: TObject);

begin

with Form1 do begin

//добавяне името на класа

ListBox1.Items.Add (‘Class Name: ‘ + Obj.ClassName);

//добавяне името на родителския клас ако има такъв

if Obj.ClassParent <> Nil Then Begin

ListBox1.Items.ADD (‘Parent Class Name: ‘ + Obj.ClassParent.ClassName);

// добавяне името на на класа-прародител, ако има такъв

if Obj.ClassParent.ClassParent <> Nil Then

ListBox1.Items.ADD (‘Grandparent Class Name:’ + Obj.ClassParent.ClassParent.ClassName);

end;

//Добавяне размера на обекта и псевдоним

ListBox1.Items.ADD (‘Object Size: ‘ + IntToStr (Obj.InstanceSize));

ListBox1.Items.ADD (‘Reference Size’ + IntToStr (SizeOf (Obj)));

//показване дали е компонента или не

If Obj.InheritsFrom (TComponent) Then

ListBox1.Items.ADD (‘This is a component’)

else

ListBox1.Items.ADD (‘This is NOT a Component’);

end; //with

end;

procedure TForm1.Button1Click(Sender: TObject);

begin

  ShowInfo (Sender)

end;

Общи характеристики във VCL

Action

Идентифицира обекта Action за контролата

Align

Определя подравняването на контролата в областта на родителската контрола

Anchors

Показва страната на формата, с която е свързана компонентата

AutoSize

Автоматична смяна на размера

BiDiMode

Предлага под ръка на езици, я които се пише от дясно на ляво

BorderWidht

Прозоречни контроли. Ширина на рамката

BboundRect

Дефинира обхващащия правоъгълник на контролата

Caption

Заглавие на контролата

ComponentCount

Броя на компонентите, притежавани от текущата компонента (run-time only и only)

ComponentIndex

Позицията на компонента в списъка с компоненти на притежателя

Compoents

Списък от компонентите, притежавани от текущата компонента

Constraints

Определя и максималния и минималния размер на контролата по време на промяна на размера

ControlCount

Броя на контролите, чиито родител е текущата

Controls

Масив с контролите, чийто родител е текущата контрола

Color

Цвят на фона

Ctrl3D

Триизмерен поглед

Cursor

Определя вида на показалеца на мишката

DockSite

Задава дали прозоречната контрола може да се използва за скачване на прозорци.

DragCursor

Показалеца, използван да покаже, че контролата приема техника на влачене

DragKind

Позволява избор между влачене и скачване, ако режима на влачене е автоматичен

DragMode

Определя дали drag-and-drop(влачене и пускане), или скачване в зависимост от стойността на DragKIng) ще се активира автоматично.

Enable

Активна или неактивна

Font

Определя шрифта

Handle

Манипулатор на системния прозорец, използва от контролата (run-time only и read-only)

Height

Вертикален размер

HelpContenst

Задава контекстно число, което ще се използва за автоматично извикване на контекстната помощна информация

Hint

Символния низ използван като подсказка за контролата

Left

Хоризонтална координата на горния ляв ъгъл

Name

Уникално име

Owner

Задава компонента, притежаваща текущата компонента (run-time only и read-only)

Parent

Задава родителска контрола на текущата

ParentColor

Дава дали компонентата ще използва съответната характеристика color от родителската

ParentCtrl3D

Ще се използва ли Ctrl3D характеристиката

ParentFont

Ще се използва ли Font

ParentShowHint

Ще използва ли ShowHint от родителската

PopupMenu

Задава конкретно меню на контролата

ShowHint

Показване на подсказката

TabOrder

Номера на контролата при обхождане

TabStop

Дали да се премине към контролата при натискане на TAB.

Tag

4 байтово число за съхраняване на допълнителна информация

Top

Вертикална координата на горния ляв ъгъл

UndockHeight

Височина на контролата, когато не е скачена за друг прозорец

Visible

Показване или не на контролата

Widht

Хоризонтален размер.

Показване имената на всички компоненти: - стр. 213

Използване на характеристиките Components и Controls:

Пример D0402

Освен споменатите компоненти сложи още малко по твой избор.

procedure TForm1.Button1Click(Sender: TObject);

  var I: Integer;

begin

For I := 0 to ComponentCount - 1 do

    ListBox1.Items.Add (Components [I].Name);

  ListBox1.Items.Add (‘------------‘);

end;

procedure TForm1.FormCreate(Sender: TObject);

begin

Button1.Caption := ‘Добавянее’;

Button2.Caption := ‘Изтриване’;

end;

procedure TForm1.Button2Click(Sender: TObject);

begin

ListBox1.Items.Clear;

end;

Достъп до компонента може да се получи и с FindComponent на формата

Характеристиката Tag няма видим ефект. Тя е един допълнителен адрес в паметта представен във всеки клас на компонентата. Оказва се полезно да имаш допълнителна памет, за да свържеш информацията с компонентата. Tag съдържа едно 4 байтово число.

Общи методи във VCL - стр. 218

Методите на компонентите са като всички останали. Те са процедури и функции, които може да се извикат, за да се извърши действие. Често се използват методи за постигане на ефекта който се постига чрез характеристики. Обаче, не всички методи имат съответни характеристики. Повечето от тях са процедури, които изпълняват някакво действие, вместо да четат и записват стойност. Отново: има методи, които са общи за всички компоненти, други методи, които са общи само за контролите и т.н.

BeginDrag

Стартира операция ръчно влачене

BringToFont

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

CanFocus

Определя дали контролата може да получи фокуса

ClientToScreen

Преобразува клиентските координати в екрани

ContainsControl

Определя дали дадена контрола се съдържа в текущата

Create

Създава нова инстанция конструктор)

Destory

Унищожава инстанция (деструктор). По добре е да се използва Free.

DragGing

Показва дали контролата се влачи в момента

EndDrag

Ръчно прекратяване на влаченето

ExecuteAction

Активира действието, свързано с компонентата

FindComponent

Връща компонента от масива Components, отговарящ на зададеното име.

FilpChildren

Премества контролите, чийто родител е в текущата контрола, от лявата в дясната страна на контролата и обратно. Използва се за поддръжка на езици, а които се пише от дясно намалява, заедно с IsRightToLeft.

Focused

Определя дали контролата притежава фокуса

Free

Изтрива област от паметта (за форми се използва метода Release

GetTеxtBuf

Връща текста (или заглавието) на контролата

GetTextLen

Връща дължината на текста (или заглавието) на контролата

HandleAlocated

Връща стойност True, ако е заделен системен манипулатор за тази контрола

HideNeeded

Заделя съответния системен манипулатор, ако все още не е създаден

Hide

Прави контролата невидима

InsertComponent

Добавя нов елемент към масива с притежаваните компоненти

InsertControl

Добавя нов елемент към масива с контролите, чийто родител е текущата контрола

Invalidate

Пречертаване на контролата

ManualDock

Ръчно активиране на скачването

NamualFloat

Разскачване на контрола

RemoveComponent

Премахва компонент от списъка Components

ScaleBy

Мащаб на контролата със зададен процент

ScreenToClient

Преобразува координатите на екрана в клиентски

SendToBack

Поставя контролата зад всички останали

SetBounds

Променя позицията и размера на контролите (по-бърз е, отколкото да се задават стойности на отделните характеристики)

SetFocus

Предава фокуса на текущата контрола

Show

Показва контролата

Update

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

Събития - стр. 221

OnCanResize

При промяна на размера. Позволява прекратяването на операцията.

ОnChange

При промяна на данните или обекта

OnClick

При щракане с левия бутон на мишката

OnDblClic

Двукратно щракане с левия бутон на мишката  

OnDockDrop

Когато операцията по скачване приключи в границите на текущата контрола

OnDockOver             

При преместване на показалеца на мишката по време на операция за скачване  

OnDragDrop

При завършване на операция влачене, получава се при компонентата която е получила операцията.

onDragOver              

При влачене на мишката над контролата  

onEndDock

При завършване на операция скачване

onEndDrag                

При завършване на операция влачене. Предизвиква се от компонентата която е стартирала операцията.  

onExit

При загубване на фокуса

onGetSiteInfo            

Връща информация за скачването на компонентата

onKeyDown

При натискане на клавиш от клавиатурата

onKeyPress

При натискане и пускане на клавиш

onKeyUp

При пускане на клавиш

OnMouseDown

При натискане на някой от бутоните на мишката       

OnMouseMove

При преместване на мишката

onMouseUp

При отпускане бутон на мишката       

onMouseWheel

При въртене на колелцето на мишката

onMouseWhelDown

 

onMouseWheelUp

 

onResize

При завършване на операция промяна на размера

OnStrtDock

При започване на операция влачене

OnUnDock

При разскачване на контрола от текущата

Използване на списъци стр. 223

Класът TList дефинира списъци от указатели, които могат да се използват за съхраняване на обект от произволен тип. Може динамично да увеличава размера си по време на изпълнение на програмата. Списъците съхраняват указатели към обекти се налага да се правят рисковани преобразувания по време на изпълнение на програмата.

Класът TString е абстрактен. Представлява списък на всички форми на списъци с низове, без да се грижи за представянето им в паметта. Дефинира абстрактен списък от низове. Обектите от типа TString се използват само като характеристики на компоненти, които могат да съхраняват низове в себе си, като контролата, представляваща списък ListBox.

Класът TStringList е наследени на TString и дефинира списък на низове със собствена памет. Може да се използва за дефиниране на собствени списъци от низове.

Класът TCollection дефинира хомогенен списък от обекти, които са притежание на класа на колекцията. Обектите в колекцията трябва да са наследници на класа TCollectionItem. Ако трябва колекцията да съхранява дадени обекти, трябва да се създаде наследник на класовете TCollection и TCollectionItem.

В тези списъци има методи и характеристики. Може да се работи със списъците, като се използва нотация за масиви “[]”. Други характеристики включват характеристиката Count, както и типичните методи за достъп като Add, Insert, Delete, Remove  и методи за търсене IndexOf.

Обектите от типовете TStringList и TString имат като списъци от низове, така и списъци от обекти, асоциирани с низове. Това ги прави удобни за много различни цели. Може да ги използват примерно за речници на асоциираните обекти или за да съхраните bitmap избрания, които да бъдат използва в компонентите, представляващи списъци.

Двата класа за списъци имат и методи за записване и зареждане на съдържанието им в текстови файлове SaveToFile и LoadFromFile. За да се обходят елементите в списъка може да се използва индексиране, тъй като списъците са масиви.

Използване на списъци от обекти и данни - стр. 224

Употребата на TList.

Пример D0403

var

Form1: TForm1;

ListNum, ListDate: TList;

implementation

{$R *.dfm}

Function CreateDate: String;

  var Y, M, D: String;

begin

Y := IntToStr (1900 + Random(200));

M := IntToStr (1 + Random (12));

D := IntToStr (1 + Random (30));

if Length (D) = 1 then

D := ‘0’ + D;

  if Length (M) = 1 Then

M := ‘0’ + M;

Result := Y + ‘/’ + M + ‘/’ + D + ‘    ‘;

Form1.Caption := Result;

end;

procedure TForm1.FormCreate(Sender: TObject);

begin

Randomize;

ListNum := TList.Create;

ListDate := TList.Create;

ListDate.Clear;

Button1.Caption := ‘AddNumber’;

Button2.Caption := ‘AddDate’;

Button3.Caption := ‘ViewNumber’;

Button4.Caption := ‘ViewData’;

end;

procedure TForm1.Button1Click(Sender: TObject);

  var I: Integer;

begin

I := Random (10000);

ListNum.Add(Pointer (I));

Form1.Caption := IntToStr (I);

end;

procedure TForm1.Button2Click(Sender: TObject);

  var Str: String;

begin

Str := CreateDate;

ListBox2.Items.Add(Str);

ListDate.Add(Pointer (Str));

//ListDate.ADD ( TDate.Create (1900 + Random (200), 1 + Random (12), 1 + Random (31)) );

end;

procedure TForm1.Button3Click(Sender: TObject);

  Var Z: Integer;

begin

ListBox1.Clear;

For Z := 0 to ListNum.Count - 1 do

ListBox1.Items.Add(IntToStr (Integer (ListNum [Z])));

end;

procedure TForm1.Button4Click(Sender: TObject);

  var Z: Integer;

begin

ListBox1.Clear;

For Z := 0 to ListDate.Count - 1 do

ListBox1.Items.Add(String (ListDate[Z]));

//ListBox1.Items.ADD ((TObject (ListDate [Z] as TDate).GetText);

end;

Глава 5

Компонента Edit - стр. 223 Пример D501.pas

Позволява въвеждането на един ред текст. (Текста може да се покаже с компонентите Label и Static Text но тези компоненти се използват най-вече само за статичен текст или текст генериран от програмата). Използва се характеристиката Text за да покаже текста. Ако трябва да се следят въвежданите символи се използва събитието OnKeyPress. Ето пример който проверява дали натиснатия клавиш е цифра или BackSpace (код 8). Ако не е така можем да променим стойността на клавиша на #0, като по този начин той няма да се обработи и ще се изведе сигнал.

Пример D0501

procedure TForm1.Edit1KeyPress(Sender: TObject; var Key: Char);

begin

  if not (key in [’0’..’9’, #8]) Then Begin

Beep;

Key := #0;

  end;

end;

Компонента MaskEdit 234

Специфицира по-точно входа от клавиатурата при използване на характеристиката EditMask. Редактора позволява въвеждането на маска, кой символ да се използва за шаблон при въвеждане, дали да запишеш литералите, представени в маската, заедно с крайния низ. Например може да има скоби около кода на телефонния номер само като подсказка по време на въвеждането или скобите са се запазят и във въведения номер.

(Примера не е даден.)

Е да ама аз си направих свой. Маската я задаваш в Properties -> EditMask

Пример D0502

Компонентите Memo и RichEdit

Мемо може да въвежда няколко реда от 32 КВ. Може да се работи с текста ред по ред (използвайки масива Lines) или с целия текст (характеристиката Text).

Ако трябва да се работи с по-голям обем текст или да се сменят шрифтовете и подравняванията на параграфите се използва RichEdit - Win32 контрола.

Компонентата RichEdit има характеристиката DefAttributes, чрез която се задават стиловете по подразбиране, и характеристиката SelAttributes, показваща стила на текущо избрания текст. Тези характеристики не са от типа TFont, но са съвместими с шрифтовете, така че можем да използваме метода Assign, за да копираме стиловете им, както е показано в примера. Има и Pargraph.

RichEdit1.SelStart – връща позицията на курсора.

Пример D0503

Та значи сложи и компонентата TFontDialog

procedure TForm1.Button1Click(Sender: TObject);

begin

  IF RichEdit1.SelLength > 0 Then Begin

    FontDialog1.Font.Assign(RichEdit1.DefAttributes);

    if FontDialog1.Execute then

      RichEdit1.SelAttributes.Assign(FontDialog1.Font);

  end

  else begin

    beep;

    ShowMessage('Маркирай текста бре');

  end;

end;

 

procedure TForm1.Button2Click(Sender: TObject);

  var Z: Integer;

begin

  Caption := 'Брой редове ' + IntToStr (Memo1.Lines.Count);

  Z := RichEdit1.FindText(Edit1.Text, 0, RichEdit1.GetTextLen,[]);

  IF Z > 0 THEN BEGIN

    RichEdit1.SelStart := Z;

    RichEdit1.SelLength := Length (Edit1.Text);

    RichEdit1.SetFocus;

  END; //IF Z

end;

 

procedure TForm1.FormCreate(Sender: TObject);

begin

  //Button2.Click;

  Button3.Caption := 'Добавяне на ред';

  Memo1.ScrollBars := ssBoth;

  RichEdit1.ScrollBars := ssVertical;

  // За показане на маркиран текст

  RichEdit1.HideSelection := False;

end;

 

procedure TForm1.Button3Click(Sender: TObject);

begin

  Memo1.Lines.Add('Добавяне на ред');

  RichEdit1.Lines.Add('Добавяне на ред');

end;

CheckBox; RadioButton - стр. 235

CheckBox - поле за отметка. Съответната възможност може да бъде избрана без значение от състоянието на другите полета за отметки.

RadioBbutton - Само една възможност за избор от много други.

GroupBox - Група от радиобутони - стр. 236

Поставя се във формата и след това се поставят радио бутоните. Те могат да се управляват и чрез масива контроли, които притежават групата. Този пример показва избрания бутон в групата:

Пример D0504

procedure TForm1.Button1Click(Sender: TObject);

  var I: Integer;

      Text: String;

begin

For I := 0 to GroupBox1.ControlCount - 1 do

If (GroupBox1.Controls[I] as TRadioButton).Checked Then

      Button1.Caption := (GroupBox1.Controls[I] as TRadioButton).Caption;

end;

Компонента ListBox - стр. 237

При избирането на елемент от компонентата ListBox се използват характеристиките Items и ItemIndex. Ако се налага работа с текста на избрания елемент ето пример:

Пример D0505

function SelText (List: TListBox): String;

  var nItem: Integer;

begin

nItem := List.ItemIndex;

if nItem >= 0 Then

Result := List.Items [nItem];

end; //function

procedure TForm1.FormCreate(Sender: TObject);

  var I: Integer;

begin

For I := 1 to 20 do

ListBox1.Items.Add(‘Mara ba ‘ + IntToStr (I));

  ShowMessage(‘Избери елемент и натисни бутона’);

end;

procedure TForm1.Button1Click(Sender: TObject);

begin

  Button1.Caption := SelText(ListBox1)

end;

Mоже да се избира една или няколко възможности. Задава се чрез характеристиката Multiple. Има два вида множествен избор в списъците на Windows и Delphi. В първия случай потребителя избира множеството елементи като щраква с мишката върху тях, докато във втория може да се използват клавишите Ctrl Shift. Това се задава от характеристиката ExtendedSelection.

При списъци с повече от един избор, програмата може да получи броя на избраните елементи от характеристика SelCount, и може да определи избраните елементи чрез масива Selected. Това е масив от тип Boolean и има същи брой елементи като списъка. Например, за да конкатенираш в един низ всички избрани елементи, може да претърсиш масива Selected по следния начин:

Пример: D0506

procedure TForm1.FormCreate(Sender: TObject);

  var I: Integer;

begin

For I := 1 to 20 do

ListBox1.Items.Add(‘Mara ba ‘ + IntToStr(I));

ListBox1.MultiSelect := True;

Label1.Caption := ‘Маркирай няколко реда и натисни бутона’;

end;

procedure TForm1.Button1Click(Sender: TObject);

  var SellItem: String;

      nItem: Integer;

begin

SellItem := “;

For nItem := 0 to ListBox1.Items.Count -1 do

if ListBox1.Selected [nItem] Then

      SellItem := SellItem + ListBox1.Items [nItem] + Chr (13) + Chr (10);

      Memo1.Text := SellItem;

end;

Заемат маса място на екрана и предлагат само избор от предварително зададен списък.

Компонента ComboBox - 238 стр.

Поведението му се изменя от стойността на характеристиката Style. csDropDown задава типичен комбиниран списък, който позволява директното редактиране и при заявка показва пaдащ списък; csDropDownList задава комбиниран списък, който не позволява редактиране, а csSimple задава комбиниран списък, който винаги показва падащия списък. Достъпа до избрания елемент се осъществява с характеристиката Text.

Следващия пример показва добавяне на елемент при натискане на Enter.

Пример: D0507

procedure TForm1.ComboBox1KeyPress(Sender: TObject; var Key: Char);

begin

IF Key = Char (13) Then

   //and (Items.IndexOf (Text) < 0) Then

With ComboBox1 do

      if Text <> “ Then Begin

        Items.Add(Text);

         Label3.Caption := IntToStr (Items.IndexOf(Text));

         Label1.Caption := Text;

      end; //if

end;

procedure TForm1.FormCreate(Sender: TObject);

begin

ComboBox1.Text := “;

ComboBox1.Sorted := True;

end;

 

procedure TForm1.ComboBox1Select(Sender: TObject);

begin

 Label2.Caption := ComboBox1.Text;

end;

CheckListBox - стр. 238

Може да се избира един елемент, но може да избира и поле за отметка за промяна на състоянието им.

За проверка на текущото състояние на елементите може да се използват масивите-характеристики Checked и State (използвай втората ако си позволил на някой от полетата да са неактивни)

Пример: D0508

procedure TForm1.FormCreate(Sender: TObject);

  var Z: Integer;

begin

For Z := 1 to 10 do

CheckListBox1.Items.Add(‘Mara ba ‘ + IntToStr (Z));

end;

Компонентите ListView и TreeView стр. 239

????

Няколко компоненти които може да се използват за избор на стойност в даден интервал:

Компонента ScrollBar (позиционна линия) - стр. 240

Може да се разглежда като част от друга компонента. Управляват се, чрез използване характеристиките на формата.

TrackBar и ProgressBar

Извеждат стойност в даден интервал

Пример: D0511

procedure TForm1.Button1Click(Sender: TObject);

  Var I: Integer;

begin

  ProgressBar1.Position := ProgressBar1.Position + 5;

  Button1.Caption := ‘ProgressBar.Position := ‘ + IntToStr (ProgressBar1.Position);

end;

procedure TForm1.TrackBar1Change(Sender: TObject);

begin

  Label1.Caption := ‘TrackBar.Position := ‘ + IntToStr (TrackBar1.Position);

end;

UpDown

Свързва се с текстово поле. Позволява на потребителя да намалява или увеличава стойността на число. Връзката се задава чрез характеристиката Associate.

Пример: D0510

procedure TForm1.FormCreate(Sender: TObject);

begin

UpDown1.Associate := Edit1;

UpDown1.Position := 5;

end;

PageScroller

Превъртане на вътрешни контроли. Например, ако се постави лента с инструменти в контролата PageScrollera и лентата е по-голяма от видимата област ще се покажат две малки стрелки в страницата. При натискане страницата се превърта.

Влачене от една компонента в друга (dragging) 241

Започваме с пример DragList. Може да се влачат компоненти от една контрола в друга. Използвани са компоненти ListBox, ChechListBox и Edit. Новите елементи се въвеждат в Edit и се преместват до съответния списък. Елементите в списъците не може да се повтарят. Двата списъка използват стойност dmAutomatic за характеристика си DragMode (стойността на характеристиката DragKing си запазва стойността по подразбиране, която е dkDrag). За текстовото поле използваме ръчно влачене, за да позволим на контролата да се държи както обикновено, когато потребителя щракне с мишката върху нея. Поради тази причина, когато потребителя щракне с бутона на мишката върху текстовото поле, ние трябва да започнем операцията по влачене с известно закъснение, което се показва от първия параметър на метода BeginDrag;

Пример: D0512

function AddNotDup (List: TCustomListBox; Text: String): Boolean;

Begin

//Добави ако елемента не е в списъка

Result := List.Items.IndexOf(Text) < 0;

if Result Then List.Items.Add(Text);

end; //function

procedure TForm1.FormCreate(Sender: TObject);

begin

CheckListBox1.DragMode := dmAutomatic;

ListBox1.DragMode := dmAutomatic;

end;

procedure TForm1.Edit1MouseDown(Sender: TObject; Button: TMouseButton;

  Shift: TShiftState; X, Y: Integer);

begin

  Edit1.BeginDrag(False, 10);

end;

procedure TForm1.ListBox1DragOver(Sender, Source: TObject; X, Y: Integer;

  State: TDragState; var Accept: Boolean);

begin

  //Двата списъка си поделят един и същ обработчик на събитието OnDragOver, който се използва, за да определи дали контролата ще приеме влачения обект от източника.

Accept := True;

// Ако източника е текстово поле и елемента е вече е в списъка, отказва операцията

IF (Source = Edit1) and (( Sender as TCustomListBox).Items.IndexOf(Edit1.Text) > -1) Then

Accept := False;

end;

procedure TForm1.CheckListBox1DragDrop(Sender, Source: TObject; X,

Y: Integer);

var nItem: Integer;

begin

//копира съдържанието на текстовото поле

If Source = Edit1 Then // Копиране от текстовото поле

CheckListBox1.Items.Add(Edit1.Text);

Edit1.Text := “;

  //Копира избрания елемент от списъка и го премахва от източника.

  if Source = ListBox1 Then Begin

    nItem := ListBox1.ItemIndex;    //CheckListBox1.Items.Add(ListBox1.Items[nItem]);

//Местене на елемента ако не се повтаря

If AddNotDup (CheckListBox1, ListBox1.Items [nItem]) Then

        ListBox1.Items.Delete (nItem);

  end; //if

end;

procedure TForm1.ListBox1DragDrop(Sender, Source: TObject; X, Y: Integer);

  var I: Integer;

begin

if Source = Edit1 Then begin

//Копиране на текста от текстовото поле

ListBox1.Items.Add(Edit1.Text);

Edit1.Text := “;

end;

if Source = CheckListBox1 Then Begin

//Проверка за избрани елементи. Заради изтриването се прави в обратен ред

For I := CheckListBox1.Items.Count - 1 downto 0 do

      if CheckListBox1.Checked [I] then Begin

        //ListBox1.Items.Add(CheckListBox1.Items [I]);

        //Местене на всички избрани елементи ако не се повтарят

        if AddNotDup(ListBox1, CheckListBox1.Items[I]) Then

        CheckListBox1.Items.Delete(I);

      end; //if

  end;

end;

Управление на фокуса - стр. 244

Чрез характеристиките TabStop и TabOrder, поддържани от повечето контроли, може да се зададе реда, по който контролите ще получават фокуса при натискане на клавиша Tab. Вместо да задаваш по отделно за всяка компонента стойността на характеристиката TabOrder можеш да използваш контекстното меню на формата, за да активираш диалоговия прозорец Edit Tab Order.

Когато компонента получи или загуби фокуса приема събитията onEnter и onExit. Някой от тези техники са демонстрирани в примера InFocus.

За да се изведе информацията за състоянието се използва компонентата StatusBar с една област на извеждане (зададена чрез стойността True на характеристиката SimplePanel. Символа & в етикетите, задаващ клавишната комбинация за бърз достъп и връзката между тези етикети и съответните им текстови полета (характеристиката FocusContor):

Пример: D0513

Програмата показвала в лентата за състояние кое текстово поле е получило фокуса, чрез събитието onEnter. Може да се използва общ обработчик за да не се повтаря писането. Проверява се всяко поле от контролите във формата, за да се установи кой етикет е свързан с текущото текстово поле (показано от параметъра Sender):

Втория обработчик на събитие във формата е свързан със събитието OnExit на първото текстово поле. Ako контролата е празна, тя отказва да загуби фокуса и се връща обратно и извежда съобщение. Ако е въведена стойност и е натиснат Enter се преминава към второто поле. После към третото.

procedure TForm1.FormCreate(Sender: TObject);

begin

Label1.Caption := ‘&First name’;

Label2.Caption := ‘&Last name’;

Label3.Caption := ‘&Password’;

Edit1.Text := “;

Edit2.Text := “;

Edit3.Text := “;

Label1.FocusControl := Edit1;

Label2.FocusControl := Edit2;

Label3.FocusControl := Edit3;

Edit3.PasswordChar := ‘*’;

StatusBar1.SimplePanel := True;

ActiveControl := Edit1; //Активиране на контрола

end;

procedure TForm1.Edit1Exit(Sender: TObject);

begin

//Ако полето е празно

if Edit1.Text = “ Then Begin

Edit1.SetFocus;

MessageDlg(‘First name is required’, mtError, [mbOK], 0);

  end; //if

end;

procedure TForm1.Edit1KeyPress(Sender: TObject; var Key: Char);

begin

IF Key = #13 Then

      Edit2.SetFocus;

end;

procedure TForm1.Edit2KeyPress(Sender: TObject; var Key: Char);

begin

  IF Key = #13 Then Edit3.SetFocus;

end;

procedure TForm1.Edit1Enter(Sender: TObject);

  var I: Integer;

begin

For I := 0 to ControlCount -1 do

if (Controls[I] is TLabel) //Ако контролата е етикет

        and (TLabel (Controls[I]).FocusControl = Sender) Then Begin

      StatusBar1.SimpleText := ‘Enter ‘ + Copy (TLabel (Controls[I]).Caption, 2, 100);

End; //if

end;

Управление на менюта - стр. 246

Задаващите състояние елементи се използват за включване или изключване на дадена опция, за да се промени състоянието на даден елемент. Тези команди имат отметка вляво от текста си, за да покажат, че за включени;

Радио елементите имат кръгла отметка и са групирани, за да представляват различни алтернативни възможности,като бутоните на радио. За да създадеш такива елементи, просто задайте стойност True  на характеристиката RadioItem и задай една и съща стойност на характеристиката GroupIndex за всички алтернативни елементи на менюто.

Диалоговите елементи на менюто предизвикват показването на диалогово прозорец и обикновено се маркират с многоточие (три точки) след името.

Контекстни менюта PopUp menu стр. 247

Пример: D0514

Извиква се с десен бутон на мишката. Освен да се свърже със съответната характеристика на компонентата може да се извика метода му Popup, който изисква координатите на появяването на менюто. Стойностите на координатите могат да се получат с ClientToScreen на текущия компонент (в следващия програмен фрагмент тя е етикет):

procedure TForm1.FormMouseDown(Sender: TObject; Button: TMouseButton;

Shift: TShiftState; X, Y: Integer);

var ScreenPoint: TPoint;

begin

//ако е изпълнени някакво условие

if Button = mbRight then begin

ScreenPoint := Label1.ClientToScreen (Point (X, Y));

PopupMenu1.Popup (ScreenPoint.X, ScreenPoint.Y);

end;

end;

Динамично свързване - стр. 249

Освен да дефинираш структурата на менюто чрез Menu Dersigner и да променяш състоянието на елементите чрез характеристиките Checked, Visible и Caption може да си създадеш цяло меню. Всеки обект от клас TMenuItem съдържа списък на елементите от менюто. Всеки елемент има същата структура така, че се получава рекурсия. Ново падащо меню трябва да се вмъкне в характеристиката Items на компонента MainMenu1. Позицията му може да се изчисли като се поиска от главното меню номера на предишното падащо меню:

И това не върви !!

procedure TFormColorText.FormCreate(Sender: TObject);

var PullDown, Item: TmenuItem;

I: Integer;

begin

//създаване на ново падащо меню

PullDown := TMenuItem.Create (self);

PullDown.Caption := ‘&Size’;

PullDown.OnClick := SizeClick;

//изчислява позицията и добавяне към менюто

Position := MainMenu1.Items.IndexOf (Options1);

MainMenu1.Items.Insert (Position + 1, PullDown);

//създаване елемент на менюто за различни размери

I := 8;

while I <= 48 do begin

//създаване на нов елемент

Item := TMenuItem.Create(self);

Item.Caption := IntToStr (I);;

//преобразуване на новия елемент в радио елемент

Item.GroupIndex := 1;

Item.RadioItem := True;

//обработване на избирането и вмъкването на елемента

Item.OnClick := SizeItemClick;

PullDown.Insert(PullDown.Count, Item);

I := I + 4;

end; // while

//добавяне на допълнителен елемент в края на менюто

Item := TMenuItem.Create (self);

Item.Caption := ‘More...’;

//преобразуване на новия елемент в радио елемент

Item.RAdioItem := True;

//Обработка - показва се диалоговия прозорец за избор на шрифт

Item.OnClick := FontClick;

PullDown.Insert (PullDown.Count, Item);

end;

Обработчикът на събитието onClick на тези динамично създадени елементи на менюто използва заглавието на параметъра си Sender, за да зададе размера на шрифта:

procedure TFormColorText.SizeItemClick (Sender: TObject);

begin

with Sender as TMenuItem do Label1.Form.Size := StrToInt (Caption);

end;

Този код задава подходяща отметка до избрания елемент, защото потребителя може да промени размера чрез смяна размера на шрифта. Отметката до съответния елемент се поставя в обработчика на събитието onClic на цялото падащо меню, зададен веднага след създаването на менюто и извикван, преди да се покажат елементите му. Кодът проверява дали заглавията на елементите съответстват на текущия размер на шрифта. Ако не се открие съответствие програмата маркира последния елемент от менюто, за да покаже, че шрифтът е с различен размер:

procedure TFormColorText (Sender: TObject);

var I: Integer; Found: boolean;

begin

Found := False;

with Sender as TMenuItem do begin

          if StrToInt (Item [I].Caption = Laabel1.Font.Size then begin

Items [I].Checked := True;

Found := True;

//пропускане останалата част от цикъла

System.Break;

end;

if not Found then Items (Count - 1).Checked := True;

end;

end;

Поставяне на изображения в менюта - стр. 251

Добавя се списък с картини във формата, след това добавяш картините в този списък. Свързването става с характеристика на менюто Images и задаваш правилна стойност на характеристиката ImageIndex на елементите от менюто. Ама я по-добре сложи bitmap изображение директно с елемента от менюто използвайки характеристиката Bitmap.

Добавяне на елемент към системното меню стр. 256

procedure TForm1.Button1Click(Sender: TObject);

const idSysAbout = 100;

begin

  AppendMenu (GetSystemMenu (Handle, FALSE), MF_SEPARATOR, 0, ‘’);

  AppendMenu (GetSystemMenu (Handle, FALSE), MF_STRING, idSysAbout, ‘&About...’);

end;

API функцията GetSystemMenu  като параметър изисква манипулатор на системното меню. При добавяне на елемент трябва да му се зададе заглавието и цифров идентификатор.

Избирането на нормално меню генерира съобщението wm_Command на Windows. То се обработва вътрешно в Delphi, след което се изпраща събитието onClick на елемент от менюто.

При избор от системно меню се генерира команда wm_SysCcommand съобщението, което се предава от Delphi на подразбиращия се обработчик. Обикновено Windows трябва да направи нещо в отговор на тази команда.

Може да се прехване тази команда и да се провери дали идентификатора на командата (предаван в полето CmdType на параметъра TWinSysCommand) на елемента от менюто е нашия idSysAbout. Тъй като за него няма дефинирано събитие в Delphi, трябва да се дефинира обработчик на събитието в класа на формата:

public

  procedure WMSysCommand (var Msg: TMessage); Message wm_SysCommand;

procedure WMSysCommand (var Msg: TMessage);

begin

if MSG.CmdType = idSysAabout then

ShowMessage (‘Mastering Delphi: SysMenu example’);

inharied;

edn;

Добавяне на елементи към системното меню (само дето малко не ще ама както и да е)

procedure TForm1.Button2Click(Sender: TObject);

VAR I: Integer;

begin

//razdelitel

  AppendMenu (GetSystemMenu (Handle, FALSE), MF_SEPARATOR, 0, ‘’);

  // EDOBAWQNE NA GLAWNOTO MEN| KYM SISTEMNOTO MEN|

with MainMenu1 do

for I := 0 to Items.Count - 1 DO

AppendMenu (GetSystemMenu (self.Handle,FALSE),

mf_Popup, Items [I].Handle, PChar (Items [I].Caption));

Button2.Enabled := False;

end;

Флагът mf_Popup, показва, че се добавя падащо меню. Четвъртия параметър се интерпретира като манипулатор на менюто, което добавяме. Претърсване на менюто става с метода FindItem.

Когато (и ако ) намериш елемент на главното меню, отговарящ на избрания елемент от системното меню, може да се извика метода му Click (onClick):

var Items: TMenuItem;

begin

....

Item := MainMenu1.FindItem (msg.CmdType, fkCommand);

if Item <> nul then Item.Click;

Action обекти

Ако имаш елемент от менюто и бутон, които включват една и съща опция, всеки път, когато опцията е включена, трябва да добавиш отметка в менюто и да промениш състоянието на бутона, така че да се показват натиснати.

За да решаването на такива кахъри е въведена архитектурата action. Едно действие или команда може едновременно да показва операцията, която да се извърши, когато менюто или бутонът са избрани, както и да определя състоянието на всички елементи, свързани с действието.

Всеки Action обект е свързан с един или повече клиентски обекти чрез ActionLink обект. Те осъществяват двупосочна връзка. Операцията върху обект предизвиква събитието onExecute.

Action обектите се съхраняват в компонентата ActionList. В редактора на компонентата може да се избере някое действие или да се добави ново действие. Компонентата има свой редактор, който може да се употребява за създаване на множество действия.

Действия на практика - стр. 262

object ActionList1: TActionList

Images = ImageLIst1

object Copy1: TEditCopy

Category = ‘Edit’

Caption = ‘&Copy’

Hint = ‘Copy’

ImageIndex = 1

ShortCut = <Ctrl+C>

end

Object Cut2: TEditCut

Category = ‘Edit’

Caption = ‘Cu&t’

Hint = ‘Cut’

ImageIndex = 0

ShortCut = <Ctrl+X)

end

Object Paste1: TEditPaste

Category = ‘Edit’

Caption = ‘&Paste’

Hint = ‘Paste’

ImageIndex = 2

ShorCut = <Ctrl+V>

end

Object ACtionBold: TAction

Category = ‘Edit’

Category = ‘Bold’

ImageIndex = 4

ShorCut = <Ctrl+B>

OnExecute = ActionBoldExecute

end

Object ActionNew: TAction

Category = ‘File’

Caption = ‘New’

ImageIndex = 3

ShorCut = <F2>

OnExecute = ActionNewExecute

Object ACtionExit: TAction

Category = ‘File’

Caption = ‘Exit’

ImageIndex = 5

ShorCut = <Alt-F4>

OnExecute = AACtionExitExecute

end

Object NoAction: TAction

Category = ‘TAction’

Caption = ‘No Action’

end

Object ACtionCount: TAction

Category = ‘Text’

Caption = ‘CountChars’

OnExecute = ActionCountExecute

OnUpdate = ActionCountUpdate

end

Object ACtionEnable: TAction

Category = ‘Text’

Caption = ‘Enable NoAction’

OnExecute = ActionEnableExecute

end

end

Всички тези действия са свързани с елементите на компонентата MaianMenu и с някой контроли на SpeedButton. Изображенията избрани в контролата ActionList се отразяват само в редактора.

Свързване на елемент от менюто с обекта NoAction:

Procedure ACtionEnableExecute (Sender: TObject);

begin

NoAction.Enabled := True;

NoACtion.DisableIfeNoHandler := False;

ActionEnabled := False;

end;

Ето кода за забрана на действието bold:

procedure TForm1.ActionBoldExecute (Sender: TObject);

begin

with Memo1.Font do

if fsBold in Style then Style := Style - (rsBold);

//промяна на състоянието

ACtionBold.Checked := not ActionBold.Checked

end;

Обектът ActionCount използва обработчикът onUpdate:

procedure TForm1.ActionCountExecute (Sender: TObject);

begin

  ShowMessage (‘Characters: ‘ + IntToStr (Lenght (Memo1.Text)));

end;

procedure TForm1.ActionCountUpdate (Sender: TObject);

begin

ActionCount.Enabled := Memo1.Text<> ‘’;

end;

Проверка на обекта Sender:

procedure TForm1.ActionSenderExecute (Sender: TObject);

begin

Memo1.Lines.Add (‘Sender class: ‘ + Sender.ClassName);;

Memo1.Lines Add (‘Sender name: ‘ + (Sender as TComponent).Name);

Memo1.Lines Add (‘Category: ‘ + (Sender as TAction).Category);

Memo1.Lines Add (‘Action list name: ‘ + (Sender as TAction).ActionList.Name);

end;

Owner-Draw контроли стр. 267 Пример D503.PAS

С употребата на Window API може директно да се поставя голямо изображение върху целия елемент на менюто, като се премахне текста върху него.

Атрибута TextHeight на формата задава броя пиксели, необходими за извеждане на текста.

Този пример съдържа Shape компонента в средата на формата и менютата за даване на вида и цвета на графиката. За запазване графиката голяма колкото формата:

procedure TForm1.FormResize(Sender: TObject);

begin

Shape1.SetBounds (ClientWidth div 6,

ClientHeight div 6,

2 * ClientWidth div 3,

2 * ClientHeight div 3);

end;

Целта на програмата е да превърне елементите на менюто в bitmap изображения: Предварително се прави един BMP файл за всяка от трите графики и по един за всеки от трите основни цвята. Тези изображения могат да имат произволна големина, но за да изглеждат добре, те трябва да са ниски и широки (като обикновения вид на елементите от менюто).

Имайки шестте BMP файла, всеки си име на съответния елемент на менюто (плюс разширение BMP) ето и обработчик на събитието onCreate на формата където се заменя текста на елементите от менюто с bitmap изображенията.

function Modifymenu (hMnu:HMENU;

uPosition, uFlags, uIDNewItem: UINT;

lpNewItem:PChar): BOOL; stdcall;

Първият параметър е манипулатор на менюто, с което работим; втория е позицията на елемента, който искаме да модифицираме; третия флагове, имащи ефекти върху останалите параметри; четвъртия идентификатор на елемента на менюто (съхраняван от характеристиката Command); петият новия текст за елемента на менюто.

Кодът се базира на два for цикъла

for I := 0 to Color1.Count - 1 do begin

Bmp := TBitmap.Create;

Bmp.LoadFromFile (Color1.Items [I].Caption + ‘.bmp’);

ModifyjMenu (Color1.Handle,

Color1.Items [I].MenuIndex,

mf_ByPosition or mf_Bitmaap,

Color1.Items [I].Command,

Pointer (Bmp.Handle));

Color1.Items [I].Tag := Integer (BMP);

end;//next

За да се определи размерът на елементите от менюто, се генерира събитието onMeasureItem за всеки елемент на менюто при показване. Обработчикът има два var  параметъра на които се задава:

procedure ColorMeaasureItem (Sender: TObject; ACanvas; TCanvas;

var Width, Height: Ingeter;)

OnDragItem се генерира когато елемента трябва да се прерисува. Това се случва при първо показване на елемента.

Blue1.Tag := clBlue;

Red1.Tag := clRed;

Green1.Tag := clGreen;

Това променя цвета на Shape1 при събитието onClick на елементите от меню Color

procedure TForm1.ColourClick(Sender: TObject);

begin

Shape1.Brush.Color := (Sender as TComponent).Tag;

end;

Обработчикът на събитието OnMeasureItem не зависи от конкретния елемент, но използва фиксирана стойност.

procedure TForm1.OnMeasureItem(Sender: TObject; ACanvas: TCanvas;

var Width, Height: Integer);

begin

Width := 80;

height := 30;

end;

Това било най-важното ама кво прави ???? За стойност на цвете се използвала характеристиката Tag. Преди това трябвало да се изчертае фона на елементите от менюто (представляващ правоъгълната област) със стандартния цвят за менюто (clMenu) или с цвета на избраните елементи на менюто (clHighLight); Само дето нищо не прави де ама както и да е

procedure TForm1.OnDrawItem(Sender: TObject; ACanvas: TCanvas;

  ARect: TRect; Selected: Boolean);

begin

  if Selected then ACanvas.Brush.Color := ClHighlight

else ACanvas.Brush.Color := clMenu;

  ACanvas.FillRect (ARect);

//показване на цвета

ACanvas.Brush.Color := (Sender as TComponent).Tag;

InflateRect (ARect, -5, -5);

// Изчертаване на елипса

Acanvas.Ellipse (Arect.Left, ARect.Top, ARect.Right, ARect.Bottom);

// Изчертаване на правоъгълник

//Acanvas.Rectangle (Arect.Left, ARect.Top, ARect.Right, ARect.Bottom);

end;

Списък от цветове - стр. 273

Сложи компонента Color Dialog. Стойността на атрибута TextHeight на формата задава броя пиксели, необходим за извеждане на текста. Тази стойност трябва да използваме за характеристиката ItemHeight  на списъка. Понеже нямаме достъп до TextHeight щото не била характеристика копиране нейния код в FormCreate.

ListBox1.ItemHeight := Canvas.TExtHeight (‘0’);

След това се добавят елементи в списъка.

procedure AddColors ( Colors: array of TColor );

var I: Integer;

begin

for I := Low (Colors) to High (Colors) do

Form1.ListBox1.Items.AddObject ( ColorToString (Colors [I]), TObject (Colors[I]));

end;

Извиква се с:

AddColors ([clRed, clBlue, clYellow, RGB (255, 0, 0), RGB (10, 0, 0)]);

Добавянето на цвят може да стане с:

procedure TForm1.ListBox1DblClick(Sender: TObject);

begin

  if ColorDialog1.Execute then

AddColors ([ColorDialog1.Color]);

end;

Компонентите ListView и TreeView - стр. 275

Съхраняват се в библиотеката ComCtl32.DLL на Windows.

Списък сочещ към графични изображения с ListView стр. 276

Пример D505.PAS

Може да се зададат множество от bitmap изображения, показващи състоянието на елементите и описващи съдържанието им по графичен път.

За да може да се свържат изображенията със списък или дърво трябва да се обърнеш към компонентата ImageList. ListVeiw може да има повече от един списък с изображения, един за големи икони (характеристиката LargeImages) и за малки (SmallImages).

За да се дефинират изображенията първо се създават с ImageEditor и се добавят се във файла с ресурсите.

Създават се две компоненти ImageList по време на изпълнение на програмата в първата част на събитието onCreate за формата:

procedure TForm1.FormCreate(Sender: TObject);

VAR ImageList1, ImageList2: TImageList;

begin

ImageList1 := TImageList.Create (self);

ImageList1.Height := 32;

ImageList1.Width := 32;

ImageList1.ResourceLoad (rtBitmap, ‘LargeImage’, clWhite);

ListView1.LargeImages := ImageList1;

// зареждане на малки изображения

ImageList2 := TImageList.Create (self);

ImageList2.ResourceLoad (rtBitmap, ‘SmallImages’, clWhite);

ListView1.SmallImages := ImageList2

end;

Всеки от елементите в компонентата ImageList има характеристика ImageIndex, която сочи изображение в списък. Двата списъка трябва да са подредени еднакво. Когато има даден списък може да се добавят елементи в него, като използваш редактора ListView Item Editor, който е свързан с характеристиката Items. В него могат да се създават елементи и поделементи. Поделементите се показват само при детайлен изглед (когато си задал стойност vsReport на характеристиката ViewStyle) и са свързани със заглавията, зададени в характеристиката Columns.

В примера RefList елементите се съхраняват във файл и потребителя може да редактира съдържанието на списъка, като промените автоматично се записват при затваряне на програмата. TListItems не предлага автоматично записване на данните. Данните се копират в низове. Списъкът от низове може да се запише и зареди във файл с една команда. За всеки елемент от списъка програмата записва заглавието му на един ред, индексът на изображението му на друг ред (с префикс),  а поделементите на следващите редове, отместени навътре с една табулация:

procedure TForm1.FormDestroy(Sender: TObject);

VAR I, J: Integer; List: TStringList;

// Тука нищо не прави затова го сложих на бутон

begin

//запазване на елементите

List := TStringList.Create;

try

For I := 0 to ListView1.Items.Count - 1 do Begin

//записване на заглавието

List.ADD (‘@’ + IntToStr (ListView1.Items[I].ImageIndex));

//Записване на поделементите (с отместване)

for J := 00 to ListView1.Items [I].SubItems.Count - 1 do

List.ADD (#9 + ListView1.Items[I].SubItems [J]);

end; //for

List.SaveToFile (ExtractFilePath (Application.ExeName) + ‘Items.txt’);

Finally

List.Free;

end; //try

ListView1.Items.Clear;

List := TStringList.Create;

end;

Елементите се зареждат от файла

procedure TForm1.Button1Click(Sender: TObject);

VAR List: TStringList; NewItem: TListItem; I: Integer;

begin

//зареждане на елементите

ListView1.Items.Clear;

List := TStringList.Create;

try

List.LoadFromFile (ExtractFilePath (Application.ExeName) + ‘Items.txt’);

For I := 0 to List.Count -1 do

if List [I][1] = #9 then

NewItem.SubItems.Add (Trim (List [I]))

else if List [I][1] = ‘@’ then

NewItem.ImageIndex := StrToIntDef (List [I][2], 0)

else begin

NewItem := ListView1.Items.ADD;

NewItem.Caption := List [I];

end; //нов елемент

finally

List.Free;

end;

end;

За сортиране на елементите в една колона са необходими три операции:

Задава се стойност StBoth или stData на характеристиката SortType на компонентата ListView. Сортирането става чрез извикване на събитието onCompare за всеки два елемента които трябва да се сортират. Ако ще се сортира всяка от колоните се използва събитието onColumnClick което се генерира ако е зададена стойност True на ShowColumnHeaders. При щракване върху заглавието на колоната, се записва номера на колоната, която трябва да се сортира, в private полето на класа на формата.

procedure TForm1.ListView1ColumnClick(Sender: TObject;

  Column: TListColumn);

begin

nSortCol := Column.Index;

ListView1.AlphaSort;

end;

Следва и сортиращия код

procedure TForm1.ListView1Compare(Sender: TObject; Item1, Item2: TListItem;

  Data: Integer; var Compare: Integer);

begin

  if nSortCol = 0 then

Compare := CompareStr (Item1.Caption, Item2.Caption)

  else

Compare := CompareStr (Item1.SubItems [nSortCol - 1], Item2.SubItems [nSortcol - 1]);

end;

Показване описанието на елемент при ляв бутон на мишката и редакция при натискане с десния бутон на мишката

Менюто позволява да се избират различните изгледи поддържани от контролата ListView. Може да се добавят полета за отметки към елементите на списъка.

За да може да се свърже изображението със списък или дърво трябва да вземеш компонентата ImageList. ListView може да има повече от един списък с изображения.

Създаване на съдържание - стр. 282

Контролата TreView поддържа редактира и влачене на елементите. Това е интерфейс за Windows Explorer. Има множество характеристики и начини за промяна на изображението на всеки ред или на всеки тип редове. За дефиниране на елементи може да се използва Tree View Editor. В случая ще ги заредим по следния начин:

procedure TForm1.BitBtn1Click(Sender: TObject);

VAR Node: TTreeNode;

begin

Node := TreeView1.Items.ADD (nil, ‘First Level’);

TreeView1.Items.AddChild (Node, ‘Secopnd’);

end;

Зареждане на информация в дървото. Абе нещо не е така както се пише.

procedure TForm1.BitBtn2Click(Sender: TObject);

begin

TreeView1.LoadFromFile (ExtractFilePath (Application.ExeName) + ‘Items.txt’);

end;

Освен зареждането на данните, програмата ги записва при прекратяване на работата си. Има няколко елемента за промяна на шрифта на TreeView. За поддръжка на влаченето на характеристиката DragMode се задава dmAutomatic и се обработват събитията onDragDrop  и OnDragOver.

Само дето не ще да го влачи де ама айде сега подробности.

procedure TForm1.TreeView1DragOver(Sender, Source: TObject; X, Y: Integer;

State: TDragState; var Accept: Boolean);

VAR TargetNode, SourceNode: TTreeNode;

begin

TargetNode := TreeView1.GetNodeAt (X, Y);

// позволява влачене от себе си

if ( Source = Sender ) and ( TargetNode <> nil ) Then Begin

Accept := True;

//Определя източника и целта

while (TargetNode <> SourceNode) do

TargetNode := TargetNode.Parent;

// ако източника е намерен

if TargetNode = SourceNode Then

//не позволява влачене върху дъщерната контрола

Accept := False;

end

else

Accept := False;

end;

Може да се преместват елементи като се създаде под елемент на елемента в който ще се извърши преместването, да копираме изходния елемент в него и да изтрием оригинала. Преди да се унищожи трябва да се копират елементите му. Това може да стане рекурсивно като се извика същата функция за всеки от поделементите му.

procedure CopyNodeUnder (TreeView: TTreeView; SourceNode, TArgetNode: TTreeNode);

VAR NewNode: TTreeNode;

  I: Integer;

Begin

//добавяне на нов възел

NewNode := TreeView.Items.AddChildFirst (TargetNode, ‘’);

//копиране на източника

NewNode.Assign (SourceNode);

//рекурсивно копиране на поделементите

for I := SourceNode.Count - 1 downto 0 do

CopyNodeUnder (TreeView, SourceNode.Item[I], NewNode);

//изтриване на източника след местене

TreeView.Items.Delete (SourceNode);

end;

Кода на обработчика OnDragDrop

procedure TForm1.TreeView1DragDrop(Sender, Source: TObject; X, Y: Integer);

VAR TargetNode, SourceNode: TTreeNode;

begin

if TargetNode <> nil Then Begin

SourceNode := TreeView1.Selected;

TreeView1.Items.BeginUpdate;

Try

CopyNodeUnder (TreeView1, SourceNode, TargetNode);

TargetNode.Expand(False);

Finally

TreeView1.Items.EndUpdate;

end;

end;

end;

Глава 6

В API функциите се използва терминът Parent при посочване на родителя и притежателя. Дори и GetParent може да върне и двата вида прозорци. В системата тези два манипулатора се съхраняват отделно.

Характеристиката Parent задава кой е отговорен за нейното показване. При поставяне на компонента във формата, формата ще стане неин родител и притежател, но трябва да се зададе стойност на Parent защото контролата ще остане невидима.

Приложението е прозорец - стр. 294

Прозореца на приложението е скрит но се появява върху лентата със задачите. Прозореца е свързан с обекта Application и служи за обединител на всички прозорци в програмата. По време на изпълнение на програмата може да се промени заглавието на приложението чрез страницата Application на диалоговия прозорец:

Application.Title := Form1.Caption;

За обработката на събитията OnActivate, OnDeactivate, OnMinimize и OnRestore трябва да се напише самостоятелен код.

Показване прозорец на приложението - стр. 295

Пример: D0601

procedure TForm1.FormCreate(Sender: TObject);

begin

Button1.Caption := ‘Show Application’;

Button2.Caption := ‘Add SysMenu’;

// обработване на съобщенията на приложението

//Application.OnMessage := AppMessage;

end;

procedure TForm1.Button1Click(Sender: TObject);

  var OLdStyle: Integer;

begin

  //добавяне рамка и заглавие в прозореца на приложението

  OLdStyle := GetWindowLong(Application.Handle, GWL_STYLE);

  SetWindowLong(Application.Handle, OLdStyle, WS_THICKFRAME or WS_CAPTION);

   //Запазване на размера на прозореца на приложението

   SetWindowPos(Application.Handle, 0, 0, 0, 200, 100, SWP_NOMOVE or SWP_NOZORDER);

   Button1.Visible := False;

end;

procedure TForm1.Button2Click(Sender: TObject);

begin

  //Добавяне на разделител и елемент в системното меню

  AppendMenu(GetSystemMenu(Handle, False), MF_SEPARATOR, 0, “);

  AppendMenu(GetSystemMenu(Handle, False), MF_STRING, 0, ‘@About....’);

  //AppendMenu (GetSystemMenu (Handle, FALSE), MF_STRING, idSysAbout, ‘@About...’)

  // добавяне на същите елементи в системното меню на приложението

  AppendMenu(GetSystemMenu(Application.Handle, False), MF_SEPARATOR, 0, “);

  AppendMenu(GetSystemMenu(Application.Handle, False), MF_STRING, 1, ‘@About...’);

end;

Двете Api функции GetWindowLong и SetWindowLong дават системна информация за прозореца. В случая се използва параметър dwl_Style, за прочитане или записване стиловете на прозореца, който включва рамка, заглавие, системно меню, икони и т.н. Програмата по-горе получава текущия стил и добавя (с помощта на оператора OR) стандартна рамка и заглавие на прозореца. Това се използва рядко.

За да обработиш събитието OnMessage добави нов метод в главната форма и го инсталирай по време на изпълнението на програмата и след това напиши кода на метода му

Задаване стилове на формата - стр. 300

Възможни стойности на характеристиката FormStyle са:

fsNormal: Формата представлява нормален SDI или диалогов прозорец;

fsMDIChild: Дъщерен прозорец в MDI приложение;

fsStayOnTop: SDI прозорец винаги върху всички други.

Стил на рамката - стр. 301

Характеристиката BorderStyle

bsSizable - плътна рамка;

bsDialog - формата се държи като рамка на диалогов прозорец. Задава се и при графични форми.

bsSingle - главен прозорец на приложението който не може да променя размера си;

bsSizeToolWindow / bsToolWindow - превръща прозореца в плаваща кутия с инструменти. Не трябва да се използва в главната форма.

Икони в рамката – стр. 303

Характеристиката BorderIcons. По подразбиране прозореца има малка икона, свързана със системното меню и намиращите се в най-дясната му част бутони за минимизиране, максимизиране и затваряне.

Задаване на допълнителни стилове - стр. 305

Стилът на рамките и иконите в нея се задават чрез две различни характеристики. Те са свързани с рамката и характеристиката FormStyle. Тези два параметъра представляват дават от параметрите на API функцията CreateWindowEx, която създава формите.

Пример: D0602

public

{ Public declarations }

procedure CreateParams (Var Params: TCreateParams);

procedure HitTest (Var Msg: TWMNCHitTest);

procedure TForm1.CreateParams(var Params: TCreateParams);

begin

  //премахва старото заглавие:

//Ingerited CreateParams(Params);

Params.Style := (Params.Style or WS_POPUP) and not WS_CAPTION;

end;

procedure TForm1.HitTest(var Msg: TWMNCHitTest);

begin

inherited;

if (Msg.Result = htClient) then //and Msg.YPos < Label1.Height + Top + GetSystemMetrics (SM_CYFRAME) )  then

Msg.Result := HTCAPTION;

end;

procedure TForm1.FormResize(Sender: TObject);

begin

  //Записване местоположението на бутон дори и ако прозореца промени размера си:

  Button1.Left := ClientWidth - Button1.Width - 2;

  Button1.Top := ClientHeight - Button1.Height -2;

end;

procedure TForm1.FormCreate(Sender: TObject);

begin

  Form1.Caption := ‘Бутона стои в долния десен ъгъл при Resize’;

end;

За да се промени заглавието трябва да се промени прозореца от overlapped на pop-up. В противен случай заглавието просто ще изчезне. За да направиш фалшиво заглавие, трябва да зададеш на системата че в тази област операциите с мишката ще отговарят на операции със заглавието. Това може да стане чрез обработката на съобщението wm_NCHitTest което се изпраща от Windows за установяване позицията на мишката. Когато позицията е в клиентската област и в етикета, можем да отговорим на Windows, че тя се намира върху заглавието на прозореца, като върнем съответната стойност:

Мащабиране на форми - стр. 307

Става чрез метода ScaleBy. Характеристиките PidxelsPerInch и Scaled позволяват автоматична промяна на размера. Ако желаеш това може да стане и ръчно.

Ръчно

ScaleBy (3, 4);

Размера на текущата форма се дели на 4 и се умножава по 3. По-лесно се работи с проценти;

ScaleBy (75, 100);

Добре е да се следи мащабирането.

Взема се контролата UpDwon. Свързва се с Edit1 чрез характеристиката Associate. Написването на погрешни стойности се забранява чрез характеристиките Min и Max.

Пример: D0603

procedure TForm1.FormCreate(Sender: TObject);

begin

Form1.AutoScroll := False;

Button1.Caption := ‘% Scale’;

Button2.Caption := ‘ScaleBy (3, 4)’;

Button3.Caption := ‘ScaleBy (75, 100)’;

UpDown1.Min := 50;

UpDown1.Max := 150;

end;

procedure TForm1.Button1Click(Sender: TObject);

  VAR Scaled: Integer;

begin

Scaled := UpDown1.Position;

ScaleBy (Scaled, 100);

UpDown1.Height := Edit1.Height;

end;

procedure TForm1.Button2Click(Sender: TObject);

begin

  ScaleBy( 3, 4);

end;

procedure TForm1.Button3Click(Sender: TObject);

begin

  ScaleBy (75, 100)

end;

procedure TForm1.Button4Click(Sender: TObject);

begin

  Form1.Close;

end;

procedure TForm1.FormKeyPress(Sender: TObject; var Key: Char);

begin

  if Key = #27 Then Form1.Close;

end;

Автоматично мащабиране на форма - стр. 310

Задава се True на характеристиката Scaled и False на характеристиката AutoScroll

Задаване позицията и размера - стр. 311

Характеристиката Position. Стойността по подразбиране PoDesigned показва, че формата ще се появи на мястото където е била създадена. Стойността PoScreenCenter казва на формата да се появи в центъра на екрана със проектираните размери.

Характеристиката WindowState показва прозореца дали да се появи нормален, минимизиран или максимизиран

Добавяне на звук към приложение - стр. 312

Използва се функцията PlaySoud на модула Mmsystem, предавайки като параметър името на системния звук.

Пример: D0604

uses MMSystem

private

{ Private declarations }

procedure AppMini (Sender: TObject);

procedure AppRestore (Sender: TObject);

procedure TForm1.AppMini(Sender: TObject);

begin

  PlaySound(‘Minimize’, 0, SND_ASYNC);

end;

procedure TForm1.AppRestore(Sender: TObject);

begin

  PlaySound(‘RestoreUP’, 0, SND_ASYNC);

end;

procedure TForm1.FormCreate(Sender: TObject);

begin

Application.OnMinimize := AppMini;

Application.OnRestore := AppRestore;

end;

Размер и клиентска област на формата - стр. 313

Характеристиките Widht и Height  се отнасят за размера на формата с нейните граници, характеристиките ClientWidth  и ClientHeight са размера на вътрешната част. Когато се сменя стойността на характеристиката ClientHeight се сменя и стойността на ClientWidth.

Съобщението wm_Size което се генерира от събитието OnResize информира, че е сменен размера на формата.

Чрез задаване на стойности на подхарактеристиките на характеристиката Constraints формата може да променя размера си само в тези граници.

Пример: D0605

procedure TForm1.FormCreate(Sender: TObject);

begin

  Form1.Caption := ‘Задаване максимален и минимален размер на формата’;

Constraints.MaxHeight := 300;

Constraints.MaxWidth := 500;

Constraints.MinHeight := 150;

Constraints.MinWidth := 150;

end;

Затваряне на форма - стр. 323

Осъществява се с метода Close

Управление входа от клавиатурата - стр. 325

Пример: D0606

procedure TForm1.FormCreate(Sender: TObject);

begin

RadioGroup1.Items.Clear;

with RadioGroup1.Items do begin

Add(‘None’);

Add(‘Enter = Tab’);

Add(‘Form1.Caption’);

Add(‘Skip’);

end;

RadioGroup1.ItemIndex := 1;

end;

procedure TForm1.Button1Click(Sender: TObject);

begin

  Form1.Close;

end;

procedure TForm1.FormKeyPress(Sender: TObject; var Key: Char);

begin

if Key = #27 then Form1.Close;

case RadioGroup1.ItemIndex of

1: //Enter = Tab

      if Key = #13 then Begin

Key := #0;

Perform (CM_DIALOGKEY, VK_TAB, 0);

      end;

2: //Form1.Caption

      IF Key = #13 Then //Enter

RadioGroup1.ItemIndex := 0 else

IF Key = #8 Then //BackSpace

Caption := Copy (Caption, 1, Length (Caption) -1) else

Caption := Caption + Key;

3:// Skip

IF Key in [’a’, ‘e’, ‘i’, ‘o’, ‘u’] Then

        Key := #0;

  end; //Case

Получаване входа на мишката - стр. 327

OnMouseDwon - натиснат бутон на мишката

OnMouseUp - отпуснат бутон на мишката

procedure FormMouseDown ( Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer );

Button - кой бутон е натиснат. Възможни са три стойности mbRight, mbLeft и mbCenter

Shift - кой от свързаните с мишката клавиши са били натиснати Alt, Ctrl и Shift, плюс бутоните на мишката. Стойностите се проверяват с конструкцията in, а не чрез равенство (if ssShift in Shift)

Y, X - координатите на мишката

Чертане на кръг при натиснат ляв бутон:

Пример: D0607

procedure TForm1.FormMouseDown(Sender: TObject; Button: TMouseButton;

  Shift: TShiftState; X, Y: Integer);

begin

IF Button = mbLeft Then

//Чертае кръг

Canvas.Ellipse(X - 10, Y - 10, X + 10, Y + 10);

end;

procedure TForm1.FormMouseMove(Sender: TObject; Shift: TShiftState; X,

  Y: Integer);

begin

//Показва координатите на мишката

   Caption := Format (‘Mouse in X = %d, Y = %d’, [X, Y]);

//Ако е натиснат Shift черта синя точка

if ssShift in Shift then

Canvas.Pixels[X, Y] := clBlue;

end;

Колелото на мишката се следи от OnMouseWhell, OnMouseWhellDown, OnMOuseWhellUp

Влачене и рисуване чрез мишката - стр. 330

Функцията ClipCursor забранява на мишката да напуска определена област от екрана. Друго решение на това е съобщенията на мишката. Този подход е даден в примера.

Натискането на левия бутон задава стойност True на полето Dragging на формата. Методът използва променливата от тип TRect, за да запази началната и текущата позиция на влачене. Ето и неговия код:

procedure TForm1.FormMouseDown(Sender: TObject; Button: TMouseButton;

  Shift: TShiftState; X, Y: Integer);

begin

if Button = mbLeft then begin

fDragging := True;

SetCaptureControl (Handle);

fRect.Left := X;

fRect.Top := Y;

Frect.BottomRight := fRect.TopLeft;

Canvas.DrawFocusRect (fRect);

end; //if

end;

Когато стойността на FDragging e True и се мести мишката програмата чертае правоъгълник с метода DrawFocuRect

procedure TForm1.FormMouseMove(Sender: TObject; Shift: TShiftState; X,

Y: Integer;

begin

Caption := Format (‘Mouse in X = %d, Y = %d’, [X, Y]);

if fDragging then Begin

//премахване и пречертаване на правоъгълника

Canvas.DrawFocusRect (fRect);

fRect.Right := X;

fRect.Bottom := Y;

Canvas.DrawFocusRect (fRect);

end

else

if ssShift in Shift then

Canvas.Pixels [X, Y] := clYellow;

end;

end;

Когато се отпусне бутона програмата завършва операцията по влаченето

procedure TForm1.FormMouseUp(Sender: TObject; Button: TMouseButton;

Shift: TShiftState; X, Y: Integer);

begin

if Dragging then begin

ReleaseCapture;

fRagging := False;

Invalidate;

end; //if

end;

Последното извикване (Invalidate) предизвиква пречертаване на формата:

procedure TForm1.FormPaint(Sender: TObject);

begin

  Canvas.Restangle (fRect.Left, fRect.Top, fRect.Right, fRect.Bottom);

end;

Глава 7

Рисуване във форма - стр. 338

Осъществява се чрез характеристиката:

  Canvas.Elipse (X, Y, X, Y)

При чертане във формата и скриването и чертежите изчезват. Съдържанието и може да се съхрани чрез bitmat изображение. В Windows се изисква писане на код, докато в Delphi може да се използва компонентата Image.

Чертане (drawing) и рисуване painting в Windows - стр. 339

За да се запазят чертежите при препокриване или скриване на формата трябва да се обнови изображението.

При paiting се използват специалните графично ориентирани програми, които съхраняват цялото изображение в bitmat формат. Този подход има се нарича с описателното име store-and-paint (съхрани и нарисувай). Този подход се извършва на две части. Трябва да се накара системата да прересува прозореца с някой от методите:

Invalidate - прересува цялата повърхност;

Update - незабавно обновяване съдържанието на формата. Тази операция се извършва само ако има невалидна област;

Repaint и Refresh - извиква последователно Invalidate и Update. Генерира събитието OnPaint. При компонентите двата метода имат малко различия.

Средства за рисуване - стр. 341

Всички изходни операции в Windows се извършват чрез обекти от тип TCanvas. Средствата за рисуване се наричат GDI (Graphics Device Interface) обекти.

Характеристики: Brush (четка) - запълва затворени фигури;, Pen (писалка) - определя цвета и размера на линиите и границите на геометричните обекти;

Пример D0701.PAS

TBaseShape = class

private

PBrushColor: TColor;

FPenColor: TColor;

FPenSize: Integer;

procedure SetBrushColor (const Value: TColor);

procedure SetPenColor (const Value: TColor);

Procedure SetPenSize (const Value: Integer);

procedure SetBottom (const Value: Integer);

procedure SetLeft (const Value: Integer);

procedure SetRight (const Value: Integer);

procedure SetTop (const Value: Integer);

protected

FRect: TRect;

  public

procedure Paint (Canvas: TCanvas); virtual;

  published

property BrushColor: TColor read fBrushColor write SetBrushColor;

property PenSize: Integer read fPenSize write SetPenSize;

property PenColor: TColor read fPenColor write SetPenColor;

property Left: Integer write SetLeft;

property Right: Integer write SetRight;

property Top: Integer write SetTop;

property Bottom: Integer write setBottom;

property Rect: TRect read FRect;

end;

 

  TEllShape = class (TBaseShape)

procedure Paint (Canvas: TCanvas); override;

end;

//Единствения важен код в трите процедури

procedure TBaseShape.Paint (Canvas: TCanvas);

begin

//задаване на атрибутите

Canvas.Pen.Color := fPencolor;

Canvas.Pen.Width := FPenSize;

Canvas.Brush.Color := fBrushColor;

end;

procedure TEllShape.Paint (Canvas: TCanvas);

begin

inherited PaintRgn (Canvas);

Canvas.Rectangle (fRect.Left, fRect.Top, fRect.Right, fRect.Bottom);

end;

procedure TRectShape.Pain (Canvas);

begin

inherited Paint (Canvas);

Canvas.Rectangle (fRect.Left, fRect.Top, fRect.fRight, fRect.Bottom);

end;

{Този код се съхранявал в модула ShapesH (Shapes Hierachy)._За да съхранява списъка с графичните обекти, формата има поле TList с име ShareList, което се инициализира от OnCreate и унищожава в края на програмата заедно с всички обекти от деструктора в обратен ред.}

procedure TTShapesForm.FormCreate(Sender: TObject);

begin

ShapeList := TList.Create;

end;

procedure TTShapesForm.FormDestroy(Sender: TObject);

VAR I: Integer;

begin

// изтриване на всеки обект

for I := ShapesList.Count - 1 downto 0 do

TBaseShape (ShapesList [I]).Free;

end;

{Добавяне на нов обект при операция влачене. Тъй като обекта не е напълно дефиниран се пази в полето CurrChare. Типът на обектите зависи от състоянието на натиснатите клавиши}

procedure TTShapesForm.FormMouseDown(Sender: TObject; Button: TMouseButton;

  Shift: TShiftState; X, Y: Integer);

begin

if Button = mbLeft then Begin

fDragging := True;

SetCapture (Handle);

// създаване на правилния обект

if ssShift in Shift then

CurrShape := TellShape.Create

else

CurrShape := TRectShape.Create;

//задаване на стил и цвят

CurrShape.Left := X;

CurrShape.Top := Y;

CurrShape.Right := X;

CurrShape.Tottom := y;

Canvas.DrawDocusRect (CurrShape.Res);

//Добавяне на списъка

ShapeList.Add (CurrShape);

end;

end;

{По време на влачене се чертае контура на графичния обект.}

procedure TTShapesForm.FormMouseMove(Sender: TObject; Shift: TShiftState;

X, Y: Integer);

VAR ARect: TRect;

begin

// копира координатите на мишката

Caption := Format (‘Shapes (X = %d, Y = %d, [X, Y]);

// код за влаченето

if fDragging then Begin

//изтриване и пречертаване на правоъгълник

ARect := NormalizeRect (CurrShape.Rect);

Canvas.DrawFocusRect (ARect);

CurrShape.Right := X;

CurrShape.Bottom := Y;

ARect := NormalizeRect (CurrShape.Rect);

Canvas.DrawFocusRect (ARect);

end; //if

end;

//Коригиране координатите на правоъгълника за да го направи подходящ за извикване на функцията DrawFocusRect

function NormalizeRect (ARect: TRect): TRect;

VAR TMP: Integer;

begin

if ARect.Bottom < ARect.Top Then begin

TMP := ARect.Bottom;

ARect.Bottom := ARect.Top;

ARect.Top := TMP;

end;

if ARect.Right < ARect.Left Then Begin

TMP := ARect.Right;

ARect.Right := ARect.Left;

ARegct.Left := TMP;

end;

Result := ARect;

end;

{Кода са рисуване влияе върху характеристиките на Canvas, трябва да се съхранят и в края на метода да се възстановят}

procedure TTShapesForm.FormPaint(Sender: TObject);

VAR I, OldPenW: Integer; AShape: TBaseShape;

OldPenCol, OldBrushCol: TColor;

begin

  //запазване на текущите атрибути на характеристиката Canvas

OldPenCol := Canvas.Pen.Color;

OldPenW := Canvas.Pen.Width;

OldBrushcol := Canvas.Brush.Color;

//подчертаване на всеки елемент от списъка

for I := 0 to ShapesList.Count - 1 Do Begin

AShape := ShapeList.Items [I];

Ashape.Paint (Canvas);

end;

//възстановяване на атрибутите на характеристиката Canvas

Canvas.Pen.Color := OldPenCol;

Canvas.Pen.Width := OldPenW;

Canvas.Brush.Color := OldBrushCol;

end;

{Крайния размер на графичния обект.}

procedure TTShapesForm.FormMouseUp(Sender: TObject; Button: TMouseButton;

Shift: TShiftState; X, Y: Integer);

VAR ARect: TRect;

begin

  if fDragging Then Begin //задаване на окончателния размер

ReleaseCapture;

fDragging := False;

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

ARect := NormalizeRec (CurrShape.Rect);

Canvas.DrawFocusRect(ARect);

CurrShape.Right := X;

CurrShape.Bottom := Y;

//оптимизация на кода правещ невалиден правоъгълник

ARectt := NormalizeRect (CurrShape.Rect);

InvalidateRect (Handle, @ARect, False);

end;

{Три от командите на менюто позволяват на потребителя да смени цвета на фона, на очертанието на обектите (писалката) и на вътрешната област (четката). Използват компонентата colorDialog}

procedure TShapeForm.PenColor1Click (Sender: TObject);

begin

//избор на нов цвят за писалка

ColorDialog1.Color := Canvas.Pen.Color;

if ColorDialog1.Execute then

Canvas.PenColor := ColorDialog1.Color;

end;

{Същото се прилага и за проверка ширината на линията, ако е така се забранява елемента от менюто}

procedure TShapesForm.DecreasePenSize1Click (Sende: TObject);

begin

  //намаляване размера на писалката, проверявайки дали е по-малък от нула

Canvas.Pen.Width := Canvas.Pen.Width - 2;

if Canvas.PenWidth < 3 Then

DecreasePenSize1.Enabled := False;

end;

{Промяна на цветовете чрез използване на стандартния диалогов прозорец Color}

procedure TShapeForm.PenColor1Click (Sender: TObject);

begin

//избиране на нов цвят за писалката

ColorDialog1.Color := Canvas.Pen.Color;

if ColorDalog1.Execute then

Canvas.Pen.Color := ColorDialog1.Color;

end;

{Програмата иска потвърждение за премахването на обектите от списъка чрез команда от менюто File > New}

procedure TTShapesForm.New1Click(Sender: TObject);

VAR I: Integer

begin

  //пречертаване на образа след премахване на елементите от списъка,

  //ако има създаден елемент и потребителят потвърди заявката си

  if (ShapesList.Count > 0) and (MessageDlg (‘Аre you sure you want to delete all the shapes?’, mtConfirmation, [mbYes, mbNo], 0 ) then Begin

for I := ChapeList.Count - 1 downto 0 do

TBaseShape (ShareList[I].Free; //изтриване на всички обекти

ShapeList.Clear;

Refresh;

end; //if

end;

Рисуване в Bitmap изображение - стр. 351

Обектите от типа TBitMap имат свое поле за рисуване. Това е компонентата Image. В примера се използват компонентите OpenDialog и SaveDialog. Преди да се използват задължително трябва да се провери дали съществуват за да не скиваш шоу. Променя се характеристиката за фона на формата. При извършване на първата операция върху изображение то става bitmap формат.

Пример D702.pas

procedure TForm1.Image1MouseMove(Sender: TObject; Shift: TShiftState; X,

Y: Integer);

var ARect: TRect;

begin

  //показване на позицията на мишката в заглавието на формата.

  Caption := Format (‘Shape BMP (X = %d, Y = %d)’, [X, Y]);

//Премахване и пречертаване на правоъгълника

{if fDragging Then Begin

ARect := NormalizeRect(fRect);

fRect.Right := X;

fRect.Left := Y;

ARect := NormalizeRect(fRect);

Canvas.DrawFocusRect(ARect);

end

  else  }

if ssShift in Shift Then

//маркирай точката като червена

      Image1.Canvas.Pixels [X, Y] := clRed;

end;

procedure TForm1.Image1MouseUp(Sender: TObject; Button: TMouseButton;

  Shift: TShiftState; X, Y: Integer);

begin

  //В края на операцията влачене се рисува правоъгълник:

//if fDragging then Begin

ReleaseCapture;

//fDragging := False;

//Dragging := False;

Image1.Canvas.Rectangle(fRect.left, fRect.Top, fRect.Right, fRect.Bottom);

end;

procedure TForm1.Exti1Click(Sender: TObject);

begin

  Close;

end;

procedure TForm1.FormMouseMove(Sender: TObject; Shift: TShiftState; X,

  Y: Integer);

var ARect: TRect;

begin

  Caption := Format (‘Shape BMP X = %d, Y = %d’, [X, Y]);

  {

  if fDragging Then Begin //Това не става fDragging липсва

    ARect := NormalizeRect (fRect); // трябва да се сложи protected fRect: TRect;

ARecg := NormalizeRec (fRect);

Canvas.DrawFocusRect (ARect);

fRect.Right := X;

fRect.Bottom := Y;

ARect := NormalizeRect (fRect);

Canvas.DrawFocusRect (ARect)

end

else}

if ssShift in Shift Then

//маркиране точка като червена

Image1.Canvas.Pixels [X, Y] := clRed;

end;

procedure TForm1.FormMouseUp(Sender: TObject; Button: TMouseButton;

  Shift: TShiftState; X, Y: Integer);

begin

  //В края на операцията влачене се рисува правоъгълник:

  if fDragging Then Begin

ReleaseCapture;

fDragging := False;

Image1.Canvas.Rectangle (fRect.Left, fRect.Top, fRect.Right, fRect.Bottom);

  end;

end;

procedure TForm1.New1Click(Sender: TObject);

  VAR Area: TRect; OldColor: TColor;

begin

  //Метода FillArea изчертава eдин голям правоъгълник върху цялото bitmap изображение.

  If fChanged or (MessageDlg(‘Are you sure you want to delete current Image?’, mtConfirmation ,[mbYes, mbNo], 0) = idYes) Then Begin

    //Пречертаване на изображението, покривайки цялата област, и възстановяване на старата четка

    Area := Rect(0, 0, Image1.Picture.Width, Image1.Picture.Height);

OldColor := Image1.Canvas.Brush.Color;

Image1.Canvas.Brush.Color := clWhite;

Image1.Canvas.FillRect(Area);

Image1.Canvas.Brush.Color := OldColor;

  end;

end;

procedure TForm1.Load1Click(Sender: TObject);

  VAR PenCol, BrushCol: TColor; PenSize: Integer;

begin

  if not fChanged or (MessageDlg (‘Are you sure want to delete the current Image?’, mtConfirmation, [mbYes, mbNo], 0) = idYes) Then Begin

    if OpenDialog1.Execute Then Begin //Тази проверка винаги е задължителна ако зареждаш файл с OpenDialog

      with Image1.Canvas do begin

BrushCol := Brush.Color;

PenCol := Pen.Color;

PenSize := pen.Width;

      end;   Image1.Picture.LoadFromFile(OpenDialog1.FileName);

      with Image1.Canvas do begin

Pen.Color := PenCol;

Pen.Width := PenSize;

Brush.Color := BrushCol;

end;

fChanged := False;

end;

  end;

end;

procedure TForm1.SaveAs1Click(Sender: TObject);

begin

   IF SaveDialog1.Execute Then Begin Image1.Picture.SaveToFile(SaveDialog1.FileName);

    fChanged := False;

   end;

end;

Пример: D7021

Uses ..., ClipBrd;

private

{ Private declarations }

B: TBitmap;

PROCEDURE ViewImage;

procedure TForm1.FormCreate(Sender: TObject);

begin

  B := GetFormImage;

  Clipboard.Assign(B);

  ViewImage;

end;

 

procedure TForm1.ViewImage;

begin

  Image1.Picture.Bitmap := B;

end;

 

procedure TForm1.Button1Click(Sender: TObject);

begin

  B := GetFormImage;

  ViewImage;

end;

Пример: D703

procedure TForm1.Exit1Click(Sender: TObject);

begin

  Close;

end;

procedure TForm1.Load1Click(Sender: TObject);

begin

  If OpenDialog1.Execute Then Begin

    Image1.Picture.LoadFromFile(OpenDialog1.FileName);

    Caption := ‘Image Viewer - ‘ + OpenDialog1.FileName;

  end;

end;

procedure TForm1.Stretch1Click(Sender: TObject);

begin

Image1.Stretch := not Image1.Stretch;

Strech1.Checked := Image1.Stretch;

end;

procedure TForm1.Center1Click(Sender: TObject);

begin

Image1.Center := not Image1.Center;

Center1.Checked := Image1.Center;

end;

procedure TForm1.PixelFormat1Click(Sender: TObject);

  VAR StrDebth: String;

begin

//Показване формата на изображението

case Image1.Picture.Bitmap.PixelFormat of

pfDevice: StrDebth := ‘Device’;

pf1bit: StrDebth := ‘1 bit’;

pf4bit: StrDebth := ‘4 bit’;

pf8bit: StrDebth := ‘8 bit’;

pf15bit: StrDebth := ‘15 bit’;

pf16bit: StrDebth := ‘16 bit’;

pf24bit: StrDebth := ‘24 bit’;

pf32bit: StrDebth := ‘32 bit’;

pfCustom: StrDebth := ‘Custom’;

end; //case

MessageDlg (‘Bitmap color debth: ‘ + StrDebth, mtInformation, [mbOK], 0);

end;

procedure TForm1.ScanSlow1Click(Sender: TObject);

  VAR Bmp: TBitmap; I, J, T: Integer;

begin

{ Сканиране на изображението в двете посоки, съхраняването му в променливата Bmp и задаване цвета на всеки пиксел. }

Bmp := Image1.Picture.Bitmap;

Bmp.PixelFormat := pf24bit;

Bmp.Width := 256;

Bmp.Height := 256;

T := GetTickCount;

//Промяна на всеки пиксел

For I := 0 to Bmp.Height - 1 do

  For J := 0 to Bmp.Width -1 do

    Bmp.Canvas.Pixels[I, J] := RGB (I * J mod 255, I, J);

    Caption := ‘Image Viewer - Memory Image (MSecs: ‘ + IntToStr (GetTickCount - T) + ‘)’;

end;

procedure TForm1.ScanFast1Click(Sender: TObject);

  VAR Bmp: TBitmap; I, J, T: Integer; Line: PByteArray;

begin

  { Може да се ускори процеса на сканиране като се използва характеристиката ScanLine }

//промяна на изображението

Bmp := Image1.Picture.Bitmap;

Bmp.PixelFormat :=  pf24bit;

Bmp.Width := 256;

Bmp.Height := 256;

T := GetTickCount;

// промяна на всеки пиксел ред по ред

For I := 0 to Bmp.Height -1 do Begin

Line := PByteArray (Bmp.ScanLine[I]);

For J := 0 to Bmp.Height -1 do Begin

Line [J * 3] := J;

Line [J * 3 + 1] := I * J mod 256;

Line [J * 3 * 3] :=  I;

end//

end;//

//обновяване на екрана

Image1.Invalidate;

Caption := ‘Image Viewer - Memory Image (MSecs: ‘+ IntToStr (GetTickCount - T) + ‘)’;

{това се използва за превъртане на картинка  }

end;

procedure TForm1.Cancel1Click(Sender: TObject);

begin

{ Бутона Cancel променя флага fCancel, който се проверява на всяка интеграция на външния цикъл }

  //fCancel := True;

end;

procedure TForm1.Go1Click(Sender: TObject);

  VAR W, J, H, I, LineBytes: Integer; Line: PByteArray;

      Bmp: TBitmap; R: TRect;

begin

//Настройка на потребителския интерфейс

//fCancel := False;

Go1.Enabled := False;

Cancel1.Enabled := True;

//Промяна размера на изображението

Bmp := Image1.Picture.Bitmap;

W := Bmp.Width;

H := Bmp.Height;

//Заделяне на необходимата памет

   Line := AllocMem(LineBytes);

//Превъртания според броя на линиите

For I := 0 to H - 1 do Begin

//копиране на първия ред

//Move ((Bmp.ScanLine[J]^), (Bmp.ScanLine[J - 1]^), LineBytes);

//На всеки nLines ред се обновява изображението

{    if (J mod nLines = 0) Then Begin

      R := Rect (0, PanelScrol1.Height + J - nLines, W, PanelScrol1.Height + J);

      InvalidateRect(Handle, @R, False);

      UpdateWindow(Handle);

    end; //if}

end; //next

//поставяне на първия ред в края

//Move (Line^, (BMP, ScanLine [BMP.Height - 1])^, LineBytes);

//R := Rect (0, PanelScrol1.Height + H - nLines, W. PanelScrol1.Height + H);

//InvalidateRec (Handle, &R, False);

//UpdateWindow (Handle);

//предоставяне на възможността на програмата да обработи другите съобщения

Application.ProcessMessages;

Go1.Enabled := True;

Cancel1.Enabled := False;

end;

Контролата Animate стр. 347

Базирана е на AVI (Audio Video Interleaved) файлове - последователност от Bitmap изображения. Могат да бъдат компресирани и некомпресирани. Ако във файла има звуци, те се игнорират.

Контролата Animate може да има два възможни източника за анимация. За да се използва трябва да се зададе стойност на характеристиката FileName и стойност True на характеристиката Active. По подразбиране анимацията се изпълнява последователно, започвайки пак отначало при достигане на края. Този ефект се се контролира, чрез характеристиката Repetitions. При стойност (-1) повтарянето е безкрайно. Друга стойност би довела до зададените повторения. Може да се зададе началния и крайния кадър с характеристиките StartFrame и StopFrame. Тези три характеристики (начална позиция, крайна позиция и брой повторения) отговарят на параметрите на метода Play.

По време на изпълнението на програмата може да се получи достъп до общия брой кадри, чрез характеристиката FrameCount.

На контролата Animete1 се задава:

AutoSize = False;

Align := alCenter;

CommonAVI = aviFindFolder;

OnOpen = Animate1.Open;

Пример: D0704

procedure TForm1.FormCreate(Sender: TObject);

begin

  with Animate1 do begin

    FileName := ‘C:\Program\Borland\Delphi6\Demos\CoolStuf\cool.avi’;

Active := True;

AutoSize := False;

Align := alClient;

OnOpen := Animate1.OnOpen; //Това нищо не върши

  end;

end;

procedure TForm1.Exit1Click(Sender: TObject);

begin

  Close;

end;

procedure TForm1.ListBox1Click(Sender: TObject);

begin

  {Благодарение на тази структура, когато потребителят избере елемент от списъка чрез преобразуване на номера ще получим подходяща стойност за характеристиката CommonAVI}

  //Animate1.CommonAVI := TCommmonAVI := (ListBox1.ItemIndex);

  IF (ListBox1.ItemIndex = 0) and OpenDialog1.Execute Then

Animate1.FileName := OpenDialog1.FileName

end;

procedure TForm1.Animate1Open(Sender: TObject);

begin

  {При отваряне на файл се извежда броя на кадрите}

  Label1.Caption := ‘Frames’ + IntToStr (Animate1.FrameCount);

end;

procedure TForm1.PlayOne1Click(Sender: TObject);

begin

  Animate1.Play(0, Animate1.FrameCount, 1);

end;

procedure TForm1.PlayTree1Click(Sender: TObject);

begin

  Animate1.Play(0, Animate1.FrameCount, 3);

end;

procedure TForm1.Fragment1Click(Sender: TObject);

begin

  Animate1.Play (SpinEdit1.Value, SpinEdit2.Value, -1);

end;

procedure TForm1.GotoFrame1Click(Sender: TObject);

begin

  Animate1.Seek(SpinEdit3.Value);

end;

procedure TForm1.Reverse1Click(Sender: TObject);

  VAR I: Integer; Init: TDateTime;

begin

  For I := Animate1.FrameCount downto 0 Do BEgin

Animate1.Seek(I);

//изчаква 30 милисекунди;

Init := Now;

While Now < Init + EncodeTime(0, 0, 0, 30) do

Application.ProcessMessages;

end;//next

end;

Поставяне на контролата Animate в бутон - стр. 377

Пример: D0705

procedure TForm1.FormCreate(Sender: TObject);

  VAR hDiff: Integer;

begin

  Animate1.FileName := ‘C:\Program\Borland\Delphi6\Demos\CoolStuf\cool.avi’;

Animate1.Parent := Button1;

Button1.Width := Animate1.Width - 4;

hDiff := Button1.Height - Animate1.Height;

Animate1.SetBounds(hDiff div 2, hDiff div 2, Animate1.Width, Animate1.Height);

Animate1.Active := True;

end;

Графични таблици - стр. 378

Компонентите DrawGrid  и StringGrid са близко свързани. Класът TStringGrid е наследник на TDrawGrid. Таблицата показва начина по който информацията да се показва на екрана.

Събитието OnDrawCell изчертава конкретната клетка от колоната. Таблиците с низове могат да показват съдържанието на клетките си. Метода CellRect, връща правоъгълника отговарящ на зададена клетка, или MouseToCell.

Таблица на шрифтовете - стр. 380.

Пример D0706

Задаване стойности на характеристиките на компонентата StringGrid

Align = alClient

DefaultColWidth = 200

DefaultDrawing = False

Options = [do FixedVertLine, goFixedHorzLine, goVertLine, goHorizLine, goDrawFocusSelected, goColZizing, goColMoving, goEditing]

Оказа се, че може и без опциите

procedure TForm1.FormCreate(Sender: TObject);

  VAR Y, X: Integer;

begin

  {with TStringGrid do Begin

    Align := alClient;

    DefaultColWidth := 200;

    DefaultDrawing := False;

    Options := [goFixedVertLine, goFixedHorzLine, goVertLine, goHorizLine, goDrawFocusSelected, goColZizing, goColMoving, goEditing]

  end;//with}

  {Броя на колоните е равен на броя на шрифтовете + 1 за първата заглавна колона, която има размер 50}

  StringGrid1.ColCount := Screen.Fonts.Count +1;

  StringGrid1.ColWidths[0] := 50;

  For Y := 1 to Screen.Fonts.Count do Begin

    //Записване името на шрифта в първия ред на колоната;

    StringGrid1.Cells [Y, 0] := Screen.Fonts.Strings [Y-1];

    {Изчисляване на максималния необходим размер за колона, вземайки ширината на текста в колоната при най-голям размер на шрифта}

    StringGrid1.Canvas.Font.Size := 32;

    //Съхранява размера на низа 'AaBbCcZz' във всички клетки

    StringGrid1.ColWidths [Y] := StringGrid1.Canvas.TextWidth('AaBbCcZz');

  end; //next

  {Редовете са винаги 26 и са с увеличаваща се височина, изчисляваща се по: 15 + I * 2 }

  StringGrid1.RowCount := 26; //броя на редовете

  For Y := 1 to 25 do begin

    StringGrid1.Cells [0, Y] := IntToStr (Y + 7);

    StringGrid1.RowHeights [Y] := (15 + Y) * 2; //височина

    For X := 1 to StringGrid1.ColCount do

      //Вмъкване на текст по подразбиране

      StringGrid1.Cells [X, Y] := 'AaBbCcZz';

  end; //next

  StringGrid1.RowHeights[0] := 25

end;

{Параметрите Col и Row показват коя клетка се чертае във момента.

Rect е областта в която клетка ще се изчертае.

State показва състоянието на тази клетка, множество от три флага, които могат да са зададени по едно и също време.

  gdSelect - избрана клетка

  gdFocused - клетката е във фокус

  gdFixed - клетката се намира във фокусираната област

Метода DrawCell рисува текста на съответния елемент в таблицата, като използва шрифта за колоната и размера за реда:}

procedure TForm1.StringGrid1DrawCell(Sender: TObject; ACol, ARow: Integer;

  Rect: TRect; State: TGridDrawState);

begin

  //Избиране на шрифта в зависимост от колоната

  IF (ACol = 0) or (ARow = 0) Then

    StringGrid1.Canvas.Font.Name := 'Arial'

  else

    StringGrid1.Canvas.Font.Name := StringGrid1.Cells [ACol, 0];

  //избиране размера на шрифта в зависимост от номера на реда

  If ARow = 0 Then

    StringGrid1.Canvas.Font.Size := 14

  else

    StringGrid1.Canvas.Font.Size := ARow + 7;

  //избиране цвета на фона

  if gdSelected in State Then

    StringGrid1.Canvas.Brush.Color := clHighlight

  else if gdFixed in State then

    StringGrid1.Canvas.Brush.Color := clBtnFace

  else

    StringGrid1.Canvas.Brush.Color := clWindow;

  //извеждане на текста

  StringGrid1.Canvas.TextRect(Rect, Rect.Left, Rect.Top, StringGrid1.Cells [ACol, ARow]);

  //извертаване правоъгълника, задаващ фокуса

  if gdFocused in State Then

    StringGrid1.Canvas.DrawFocusRect(Rect);

end;

Пример D07061

procedure TForm1.FormCreate(Sender: TObject);

  VAR Z: INTEGER;

      TSL: TStringList;

      A: ARRAY [0..30, 0..30] OF TStrings;

begin

  TSL := TStringList.Create;

//Броя на колони и редове

  StringGrid1.RowCount := 20;

  StringGrid1.ColCount := 20;

//Дължината на първата колона

  StringGrid1.ColWidths[0] := 20;

 //Писане на номерата на първите колона и ред

  FOR Z := 1 TO 4 DO StringGrid1.Cells[Z, 0] := IntToStr(Z);

  FOR Z := 1 TO 10 DO StringGrid1.Cells[0, Z] := IntToStr(Z);

//Писане в определена клетка

  StringGrid1.Cells[1, 1] := 'MARA BA ' + #13 + #10 + 'KOKO';

//Записване в цяла колона

  FOR Z := 0 TO 30 DO TSL.Add(IntToStr((Z)));

  StringGrid1.Cols[2] := TSL;

  A[0, 0] := TSL;

  StringGrid1.Cols[3] := A[0, 0];

end;

Мините в таблица - стр. 384

Компонентата StringGrid използва масив Cells, за да съхранява стойностите на елементите си, и също има характеристика Objects, за да съхранява потребителските данни за всяка клетка. Компонентата DrawGrid няма допълнителна памет. Поради тази причина в следващия пример ще се дефинира двумерен масив.

При щракване върху клетка с левия бутон на мишката ако няма мина, на екрана се показва в клетка броя на мините, намиращи се в съседните клетки. Като намериш мина щракни с десния бутон на мишката за да поставиш флагче.

Таблицата съдържа квадратни клетки с размер 30 x 30 пиксела. Първия масив Display е от тип Boolean и показва дали елемента да се рисува или не. Броя на редовете и колоните на този масив е NItems.

Display: Array [0 .. NItems - 1, 0..NItems-1] of Boolean;

Map: Array [0..NItems - 1, 0..NItems - 1] of Char

Може свободно да се променя тази константа но трябва да се променя и размера на таблицата. Втория масив Map -съдържа позициите на мините, флаговете и броя на съседните мини. Ето и списък на възможните стойности:

M - на тази позиция има неоткрита мина;

К - позната мина;

W - грешна мина;

0 - 8 - брой на мините.

Поставянето на мините става с:

Radomize;

MinesToPlace := NMines; //броя на мините

while MinesToPlace > 0 Do Begin

X := Random (NItems);

Y := Random (NItems);

If map [X, Y] <> ‘M’ Then Begin

Map [X, Y] = ‘M’;

Dec (MinesToPlace);

end;

end;

Ресурсен файл се включва със: {R NameFile.RES}. Това правело програмата по-бърза. Изображението се зареждало с:

Bmp.LoadFromResourceName (HInstance, ‘M’ + Code);

DrawGrid1.Canvas.Draw (Rect.Left, Rect.Top, Bmp);

Това нямало да се случи ако масива Dsplay имал стойност True.

Използване на компонентата TeeChart - стр. 389 само дето не я намирам.

VCL базирана компонента за създаване на диаграми, създадена от David Berneda и лицензирана на Borland за включване в Developer и Client/Server версиите на Delphi. Разпространявала се в три версии: самостоятелна компонента (в страницата ADDitional  на палитрата с компоненти), свързана с бази данни (в страницата Data Controls) и свързана с генерирането на отчети (в страницата Quiick Report). В Delphi Client/Server е включена и контролата DesicionChar в страницата Decisions Cube на палитрата.

Същинските диаграми са от обекти от типа TChartSeries или от някой наследник. След поставянето и може да се създадат една или повече серии. Това се прави с Chart Component Editor.

Пример: D0707

procedure TForm1.StringGrid1GetEditMask(Sender: TObject; ACol,

  ARow: Integer; var Value: String);

begin

//редактиране маската за въвеждане в клетка

Value := ‘09;0’;

end;

procedure TForm1.FormCreate(Sender: TObject);

  VAR X, Y: Integer;

begin

//Въвеждане на данни в таблицата

with StringGrid1 do Begin

For X := 1 to 5 do

      Cells [X, 0] := Format (‘Group %d’, [X]);

For Y := 1 to 4 do

      Cells [0, Y] := Format (‘Group %d’, [Y]);

    //запълване на таблицата със случайни стойности

Randomize;

For X := 1 to 5 do

      For Y := 1 to 4 do

        Cells [X, Y] := IntToStr (Random(100));

//обновяване на диаграмата

Update;

  end;//with

end;

{ Достъп до сериите се получава като се използва името на компонентата (Series1) или като се използва характеристиката Series на диаграмата представляваща масив. Например Chart1.Series [J-1]}

procedure TForm1.Button1Click(Sender: TObject);

  VAR X, Y: Integer;

begin

For Y := 1 to 4 Do Begin

//Chart1.Series[Y-1].Clear;

For X := 1 to 5 do

      //Chart1.Series[Y-1].Add(StrToInt (StringGrid1.Cells[X, Y]), ‘[]’, Chart1.Series[Y-1].SeriesColor);

      //Chart1.Series[Y-1].Add(StrToInt (StringGrid1.Cells[X, Y]), ‘[]’, clWhite);

  end; //next Y

end;

procedure TForm1.CheckBox1Click(Sender: TObject);

  VAR X: Integer;

begin

For X := 1 to 4 do

//Chart1.Series[X-1].Marks.Visible := CheckBox1.Checked;

end;

Използване на метафайлове - стр. 396

Единствения векторно графичен формат поддържан от Windows е Windows Metafile Format (WMF). Този формат е разширен в Win32 до Enhanced Metafile Format (EMF), който съхранява допълнителна информация за режима на чертане и координатната система.

Метафайловете представляват последователности от извикване на GDI функции. След като са запазени последователностите от извикванията, може да се повторят чрез класовете TMetafile и TMetaFileCanvas.

TМetaFile предлага характеристики за зареждане и записване на файл, както и характеристики определящи ключовите особености на файла. Една от тях е Enhanced, която определя типа на метафайла. При четене стойността на Enhanced се установява в зависимост от разширението WMF за Windows 3.1 и EMF за Win32.

За генериране на метафайл може де се използва обект от тип TMetaFileCanvas, свързан към файла чрез конструктора си:

WMF := TMetaFile.Create;

WmfCanvas := TMetaFileCanvas.CReateWithComment (Wmf, 0, ‘Marcro’, ‘Demo’);

След създаването на тези два обекта обекта може да се рисува в обекта от тип TMetaFileCanvas чрез стандартните изисквания на функции и накрая да съхраниш във файл на диска.

След създаването на метафайл може да се зареди в компонентата Image или може да се извикат методите Draw или StretchDraw на произволен обект от тип TCanvas.

Пример: D0708

procedure TForm1.FormCreate(Sender: TObject);

  VAR WmfCanvas: TMetafileCanvas; X, Y: Integer;

begin

Wmf := TMetafile.Create;

Wmf.Enhanced := True;

Randomize;

//създаване на виртуална област за рисуване

WmfCanvas :=TMetafileCanvas.CreateWithComment(Wmf, 0, ‘Macro’, ‘Demo metafile’);

try

//Изчистване на фона

WmfCanvas.Brush.Color := clWhite;

WmfCanvas.FillRect(WmfCanvas.ClipRect);

//Чертане на 100 линии

For X := 1 to 10 do

      For Y := 1 to 10 do Begin

        WmfCanvas.MoveTo(4 * ( X + Random(3)), 4 * (Y + Random (3)));

        WmfCAnvas.LineTo(12 * X, 12 * Y);

      end; //next

Finally

//край на рисуването

WmfCanvas.Free;

end;

//показване на изображението

PaintBox1.Canvas.Draw(0, 0, Wmf);

end;

procedure TForm1.Load1Click(Sender: TObject);

begin Wmf.LoadFromFile(ExtractFilePath(Application.ExeName) + ‘Text.EMF’);

end;

procedure TForm1.Save1Click(Sender: TObject);

begin  Wmf.SaveToFile(ExtractFilePath(Application.ExeName) + ‘Text.EMF’);

end;

procedure TForm1.Create2Click(Sender: TObject);

VAR WmfCanvas: TMetafileCanvas; X, Y: Integer;

begin

//създаване на виртуална област за рисуване

  WmfCanvas :=TMetafileCanvas.CreateWithComment(Wmf, 0, ‘Macro’, ‘Demo metafile’);

  try

//Изчистване на фона

WmfCanvas.Brush.Color := clWhite;

WmfCanvas.FillRect(WmfCanvas.ClipRect);

//Чертане на 100 линии

For X := 1 to 10 do

      For Y := 1 to 10 do Begin

        WmfCanvas.MoveTo(4 * ( X + Random(3)), 4 * (Y + Random (3)));

        WmfCAnvas.LineTo(12 * X, 12 * Y);

      end; //next

Finally

//край на рисуването

WmfCanvas.Free;

end;

//показване на изображението

PaintBox1.Canvas.Draw(0, 0, Wmf);

end;

Глава 8

Създаване на лента с инструменти с помощта на компонентата Panel - стр. 404

Постави един панел в горната част на клиентската област на формата и в него сложи няколко компоненти SpeedButton. Tози чишитин може да има заглавие (Caption) и картина (Glyph). Главната разлика е в поведението.

Ако избереш компонентата SpeedButton и я поставиш в панел - лентата с инструменти вече си задал графичен бутон. Бутоните са малки 20х20 пиксела. Не може да се пише текст.

За да накараш група бутони да действат като радио бутони, постави няколко бутона в панела, избери всички и им задай една стойност на характеристиката GroupIndex различна от 0. Всички бутони вече са взаимно изключващи се. Един трябва да е включен така че задай на характеристиката му Down стойност True.

Като алтернатива може да има група от взаимно изключващи се бутони без да е натиснат нито един. Потребителя трябва да включи и изключи бутона за да може да се включи друг бутон. Това поведение се избира със стойност True  на характеристиката AllowAllUp на всички бутони в групата.

Като специален случай може да се накарат бутоните бутони да се държат като полета за отметки, като дефинираш група (чрез GrouIndex) само от един бутон и позволиш на всички бутони в групата да не са натиснати (характеристиката Allow AllUp).

За да се покаже подсказка към бутона при посочването му с мишката трябва да се напише текст в характеристиката Hint и да се зададе стойност True на характеристиката ShowHint. Може да се променя цвета на текста (HitnColor), паузата (HintHIdePause) и др.

procedure TTollbarForm.HintColor1Click (Sender: TObject);

begin

ColorDialog1.Color := Application.HintColor;

if CalorDialog1.Execute Then

Application.HintColor := ColorDilaog1.Color;

Промяна на подсказките. Дефиниране на метод - стр. 411

TShowHIntEvent = procedure (

var HintStr: String;

var CanShow: Boolean;

var HintInfo: THInginto) of object;

Всеки параметър се предава по име, така че може да се промени.

THintInfo = record

HintControl: TControl;

HintPos: TPoing;

HintMaxWidth: Integer;

HintColor: TColor;

CursorRect: TRect;

CursorPos: TPoint;

end;

Добавяне на нов метод

public

procedure ShowHint (var HintStr: String; var CanShow: Boolean; var HintInfo: THintInfo);

Свързване със събитие на метода

procedure TForm1Create (Sender: TObject);

begin

...

Application.onShowHint := ShowHint;

end;

Ето и кода на новия метод:

procedure TForm1.ShowHint (var HintStr: String; var CanShow: Boolean; var HintInfo: THintInfo);

Begin

with HintInfo Do

if HintControl = Label1 Then

HintPos := HintControl1.ClientToScreen (Point (

HintControl.Width div 2, HintControl.Height div 2 ));

end;

Кодът възстановява центъра на контролата (HitnInfo.HintControl) и след това преобразува координатите в екранни координати, прибягвайки до метода ClientToScreen.

Контролата ToolBar - стр. 414

Намира се в страницата Win32 на палитрата с компоненти. Командите се избират от меню което се извиква с десен бутон на мишката. ToolBar съдържа обекти от клас TToolButton. Това са вътрешни обекти и имат една основна характеристика Style, което определя тяхното поведение:

- tbsButton - стандартен бутон;

- tbsCheck - бутон с поведение на отметка или на радио бутон, ако се намира в група с други бутони в блока (блоковете се разделят с разделител).

- tbsDropDown - бутон отварящ меню. Падащата част може да се реализира чрез свързване на PopUpMenu контрола към характеристиката DropDownMenu на контролата.

- tbsSeparator - разделител.

Контролата CoolBar - стр. 418

Намира се в библиотеката COMCTRLS.DLL. Представлява набор от обекти тип TCollBand. Появява се в Object Inspector при избиране на редактора на характеристиката Bands. Създават се ленти и след това се задават техните атрибути. Лентите могат да бъдат поставени на повече от един ред.

Компонентата ControlBar - стр. 420

Представлява контейнер за контроли.

Компонентата StatusBar - стр. 425

Може да бъде използвана като панел когато се зададе стойност True  на характеристиката SimplePanel. Може да се дефинират подпанели, чрез характеристиката Panels. Съобщения се извеждат чрез характеристиката SimpleText. Най-често StatusBar се използва за показване състоянието на програмата, избрана команда, позицията на мишката и др.

Ако се зададе на характеристиката Hint следния низ: ‘мара | ба коко’; първата част ще излезе като подсказка обаче втората след разрелителя | ще се появи в компонентата StatusBar ако е сложен подходящ код.

Пример: D0801

procedure TForm1.FormCreate(Sender: TObject);

  VAR I: Integer; NameFile: String;

begin

SpeedButton1.Down := True;

SpeedButton9.Hint := ‘Exit’;

SpeedButton2.Hint := ‘Забранява / Разрешава бутон 1’;

SpeedButton3.Hint := ‘Копиране на шрифтовете’;

Label1.Font.Size := 12;

Path := ‘C:\Delphi\Exampel\’;

//Path := ExtractFilePath ( Application.Create);

//Path := GetCurrentDir;

//ShowMessage(Path);

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

For I := 1 to 9 Do Begin

NameFile := IntToStr (I) + ‘.bmp’;

Case I of

      1: SpeedButton1.Glyph.LoadFromFile (Path + NameFile);

      2: SpeedButton2.Glyph.LoadFromFile (Path + NameFile);

      3: SpeedButton3.Glyph.LoadFromFile (Path + NameFile);

      4: SpeedButton4.Glyph.LoadFromFile (Path + NameFile);

      5: SpeedButton5.Glyph.LoadFromFile (Path + NameFile);

      6: SpeedButton6.Glyph.LoadFromFile (Path + NameFile);

      7: SpeedButton7.Glyph.LoadFromFile (Path + NameFile);

      8: SpeedButton8.Glyph.LoadFromFile (Path + NameFile);

      9: SpeedButton9.Glyph.LoadFromFile (Path + NameFile);

    end;

  end; //nezt

end;

procedure TForm1.SpeedButton9Click(Sender: TObject);

begin

  Close;

end;

procedure TForm1.SpeedButton8Click(Sender: TObject);

begin

  SpeedButton8.Glyph.LoadFromFile(Path + ‘18.bmp’);

end;

procedure TForm1.SpeedButton2Click(Sender: TObject);

begin

  SpeedButton2.Glyph.LoadFromFile(Path + ‘12.bmp’);

  SpeedButton8.Glyph.LoadFromFile(Path + ‘8.bmp’);

  SpeedButton1.Enabled := not SpeedButton1.Enabled;

end;

procedure TForm1.SpeedButton3Click(Sender: TObject);

begin

//Копиране на шрифтовете

ComboBox1.Items := Screen.Fonts;

//Избиране на текущия шрифт

ComboBox1.ItemIndex := ComboBox1.Items.IndexOf(Label1.Font.Name);

end;

procedure TForm1.ComboBox1Change(Sender: TObject);

begin

  Label1.Font.Name := ComboBox1.Items[ComboBox1.ItemIndex];

end;

Компонентата Action List свързвалa елементите на главното меню с контекстното

Пример: D0802

procedure TForm1.CheckCapsLock;

begin

while StatusBar1.Panels.Count < 2 do

begin

StatusBar1.Panels.Add;

end;

If Odd (GetKeyState(VK_CAPITAL)) Then

StatusBar1.Panels[1].Text := ‘Caps’

  else

StatusBar1.Panels[1].Text := “;

end;

procedure TForm1.FormCreate(Sender: TObject);

begin

CheckCapsLock;

StatusBar1.Panels[0].Text := ‘Мара ба koko’;

StatusBar1.SimpleText := ‘Ехо! Как си бре?’;

//ToolBar1.ShowCaptions := True;

ToolBar1.Images := ImageList1;

ToolBar2.Images := ImageList1;

ToolBar2.Flat := True;

ToolBar1.ShowCaptions := True;

with ToolButton1 do begin

Hint := ‘Първи’;

ShowHint := True;

Down := True;

Style := tbsButton;

ImageIndex := 0;

end;

with ToolButton3 do begin

Hint := ‘Втори’;

ShowHint := True;

Down := False;

Style := tbsButton;

end;

with ToolButton5 do begin

Hint := ‘Трети’;

ShowHint := True;

Down := False;

Style := tbsButton;

end;

with ToolButton7 do begin

Hint := ‘Четвърти’;

ShowHint := True;

Down := False;

Style := tbsCheck;

end;

with ToolButton9 do begin

Hint := ‘Пети’;

ShowHint := True;

Down := False;

Style := tbsCheck;

end;

with ToolButton11 do begin

Hint := ‘Шести’;

ShowHint := True;

Down := False;

Style := tbsCheck;

  end;

end;

procedure TForm1.Button1Click(Sender: TObject);

begin

ColorDialog1.Color := Application.HintColor;

Application.HintColor := ColorDialog1.Color;

end;

procedure TForm1.Button2Click(Sender: TObject);

  VAR I: Integer;

begin

For I := 0 to ToolBar1.ControlCount -1 Do

(ToolBar1.Controls[I] as TToolButton).Caption := ‘ Mara ba: ‘ + IntToStr (I);

end;

procedure TForm1.FormKeyPress(Sender: TObject; var Key: Char);

begin

  if Key = #27 Then

Close;

end;

Превъртане съдържането на формата - стр. 429 Пример D803.

Зависи от стойността на характеристиката AutoScroll. По подрзбиране е True. Други характеристики са HorzScrollBar, VertScrollBar, Visible (задава дали позиционнаа линия ще бъде видима), Increment (с каква стойност да се намали размера при натискане на стрелките) и Range. Нпример:

object Form11.TFrom1

Width = 458

Height = 368

HorzScrollBar.Range = 1000;

VertScrollBar.Range = 3305

Autoscroll = False

Превъртане и координати на формата - стр. 435

Пример D0803

procedure TForm1.FormCreate(Sender: TObject);

  VAR X, Y: Integer;

begin

//Нищо не чертае

OpenDialog1.Filter := ‘BitMap|*.bmp’;

If OpenDialog1.Execute Then

    Image1.Picture.LoadFromFile(OpenDialog1.FileName);

HorzScrollBar.Range := 2000;

VertScrollBar.Range := 2000;

//Чертане на жълта линия

X := HorzScrollBar.Position;

Y := VertScrollBar.Position;

Canvas.Pen.Width := 30;

Canvas.Pen.Color := clBlue;

//Canvas.MoveTo(30 - X, 30 - Y);

Canvas.MoveTo(1970 - X, 1970 - Y);

//чертане на правоъгълник

  Canvas.Rectangle (500 - X, 500 - Y, 1500 - X, 1500 - Y);

end;

Хоризонтално разделяне - стр. 240

Пример: D0804

Използва се компонентата Spliter. При задаване стоййост alTop на характеристиката Align разделянето ставало и вертикално. Язък за заглавието. Може да се промени характеристиката Hight.

Разделяне чрез компоннетта HeaderControl

Частите се дефинират чрез редактора Scrtion. Може да се зададат характеристики на текста.

Характеристиката Anchors

Прави позицията на бутона Anchors относителна спрямо долния десен ъгъл. Постави бутона в долния десен ъгъл, задай стойност akRight, akBottom на характеристиката Aanchors и когато се промени размера на формата, разстоянието на бутона до фиксираните страна и ще се запази.

Глава 9

Диалогови прозорци - стр. 452

Модален прозорец, който получава фокуса, и трябва да бъде затворен, преди потребителя да продължи работа с програмата.

Използват се две функции Show и ShowModal

Вторична форма се добавя с File -> New от менюто. Избира се Forms или Dialogs и след това се избира един от предоставените шаблони. Може да се избере и коя форма да е главна и коя форма ще се създава автоматично при стартиране на програмата.

След избирането на вторичната форма може да се зададе стойност True на характеристиката Visible за да се покажат и двете форми на екрана. При използване на функцията Show вторичната форма се показва като немодална.

При стартирането на програмата форми се създават с:

with Form3.Create (Application) do Show;

При динамично създаване на копия трябва да се унищожават всеки път при затварянето им  за да се освободи паметта:

procedure TForm3.FormClose (Sender: TObject; VAR Action: TClose);

begin

Action := caFree;

Form3 := nil;

end;

Създаване на модална форма

procedure TForm1.btnModalClick (Sender: TObject);

VAR Modal: TForm4;

begin

Modal := TForm4.CReate (Application);

try

Modal.ShowModal1;

finally

Modal.Free;

end;

end;

Понеже Show Modal можел да генерира изключение, за да си сигурен, че обекта е унищожен трябва да се унищожи блока finally. Последните стойност са само за четене, ако резултатът от функцията ShowModal е mrOk

procedure TForm1.btnSingleClick (Sender: TObject);

begin

if not Assigned (Form2) then

Form2 := TForm2.Create (Application);

Form2.Show;

end;

С този код формата се създава за първи път.

Сливането на менюта и форми - стр. 456

При това сливане главния прозорец на приложението си има главно меню. Останалите форми имат менюта със стойност True на характеристиката AutoMegre. Всяко мен трябва да има стойност за характеристиката си GroupIndex:

- ако два елемента от различни менюта имат еднаква стойност за GroupIndex, елемента от оригиналното меню изчезва.

- елементите се подреждат по нарастване на стойностите на характеристиката GroupIndex.

Диалогов прозорец се създава като се зададе стойност bsBorder за характеристиката BorredStyle. Формата ще има обикновена рамка, която няма да позволява промяна на размера. След това се казва прозореца дали ще бъде модален или немодален. (Show или ShowModal)

Пример D0901

implementation

  uses D0902;

{$R *.dfm}

procedure TForm1.FormCreate(Sender: TObject);

begin

BitBtn1.Kind := bkOK;

BitBtn3.Kind := bkClose;

Button1.Caption := ‘Изчистване’;

Button2.Caption := ‘Show Form2’;

Label4.Caption := ‘&Name’;

Label4.FocusControl := Edit1;

end;

procedure TForm1.Button1Click(Sender: TObject);

  VAR I: Integer;

begin

For I := 0 To ControlCount -1 Do

If Controls[I] is TEdit Then

      TEdit (Controls[I]).Text := “;

end;

procedure TForm1.FormActivate(Sender: TObject);

begin

  //Form2.Show;

end;

procedure TForm1.FormDblClick(Sender: TObject);

begin

  Form2.Show;

end;

procedure TForm1.Button2Click(Sender: TObject);

begin

  Form2.Show;

end;

Пример D0902

implementation

  uses D0901;

{$R *.dfm}

procedure TForm2.Button1Click(Sender: TObject);

begin

  If ListView1.Selected <> nil Then Begin

Form1.Caption := ‘Edit Idem’;

Form1.Edit1.Text := ListView1.Selected.Caption;

//Form1.ComboBox1.ItemIndex := ListView1.Selected.ImageIndex;  Form1.ComboBox1.Items.Add(ListView1.Selected.Caption);

Form1.Edit2.Text := ListView1.Selected.SubItems[0];

Form1.Edit3.Text := LIstView1.Selected.SubItems[1];

  end;//if

end;

Това не понмя какво беше

procedure TForm1.AddItem1Click(Sender: TObject);

//VAR NewItem: TListItem

begin {

Item.Caption := ‘New Item’;

Item.Clear;

if Item.ShowModal = mrOK then Begin

NewItem := ListView1.Items.Add;

NewItem.Caption := FormItem.CoboType.ItemIndex;

NewItem.SubItem.Add (FormItem.EditAuthor.Text);

NewItem.SubItems.Add (FormItem.EditCoutry.Text);

end; //if }

end;

Стандартни диалогови прозорци на Windows стр. 465

Компонентата OpenDialog може да бъде променяна със задаване на различни филтри за разширения на файлове с помощта на характеристиката Filter. Друга възможност е да се провери дали избрания файл е с разширението по подразбиране с помощта на флага ofExtensionDefferent от характеристиката Options. Възможна е и множествена селекция на файлове чрез даване на опцията AllowMultiSelect. В този случай може да получиш списъкът с избраните файлове чрез списъка от низовете Files.

Компонетата SaveDialog e същата

Компонентите OpenPictureDialog и SavePictureDialog имат сходни възможности но друга форма която показва предварителен оглед на изображението.

Компонентата FontDialog може да бъде използвана само за True Type шрифтове. Вида на прозореца може да се избира чрез характеристиката Options.

Компонентата ColorDialog също може да се покаже цялата или не в зависимост от стойностите на характеристиката Options.

Диалоговите прозорци Find и Replace са немодални но търсенето и заместването се реализират самостоятелно. Кодът се свързва към бутоните.

MessageDlg - извежда съобщение с бутони и картинка в центъра на екрана

MessageDlgPos – Същ о само че се избира позицията

ShowMessage - Извежда съобщение с един бутон

ShowMessagePos - Същото само че се избира позицията

MessageBox метод - задава заглавие на обекта Application.

InputBox - изисква въвеждане на символен низ.

InputQuery - също, връща стойност Boolean

Потребителски скрит екран - стр. 473

Пример: D0903

Procedure Delay (Seconds, MiliSec: Word);

  VAR TimeOut: TDateTime;

Begin

  TimeOut := Now + EncodeTime(0, Seconds div 60, Seconds mod 60, MiliSec);

While Now < TimeOut do

Application.ProcessMessages;

end;

procedure TForm1.Label1MouseDown(Sender: TObject; Button: TMouseButton;

  Shift: TShiftState; X, Y: Integer);

begin

  If (Button = mbRight) and (ssShift in Shift) Then Begin

Panel1.Visible := False;

PaintBox1.Canvas.Font.Name := ‘Arial’;

PaintBox1.Canvas.Font.Size := 20;

Paintbox1.Canvas.TextOut(10, 50, ‘Author: Marcro Canty’);

PaintBox1.Canvas.TextOut(10, 100, ‘Verison 1.0’);

  end; //if

end;

procedure TForm1.FormKeyPress(Sender: TObject; var Key: Char);

begin

  if Key = #27 Then

Close;

end;

procedure TForm1.Button1Click(Sender: TObject);

  VAR L, LineH: Integer;

begin

Panel1.Visible := False;

LineH := PaintBox1.Canvas.TextHeight(‘0’);

For L := 0 to LineH * 5 + 10 do

with PaintBox1.Canvas do begin

      TextOut(10, 100 - L, ‘CREDIT exampel from:’);

      TextOut(10, 100 + LineH - L, ‘Matering Delphi’);

TextOut(10, 100 + LineH * 2 - L, ‘ ‘);

Delay (0, 1);

end;

end;

procedure TForm1.Button2Click(Sender: TObject);

begin

Panel1.Visible := False;

PaintBox1.Canvas.Font.Name := ‘Arial’;

PaintBox1.Canvas.Font.Size := 20;

Paintbox1.Canvas.TextOut(40, 50, ‘Author: Marcro Canty’);

PaintBox1.Canvas.TextOut(40, 100, ‘Verison 1.0’);

end;

Създаване на начални екрани (splash screen)- стр. 475

Форми, съдържащи множество страници - стр. 479

Използва се компонентата PageControl. Има по една страница на етикет.

Може да се използва и компонентата TabControl, която има само част от заглавията, но не предлага страници, които да съдържат информация.

Характеристиката HotTrack осветява заглавието което се намира под мишката. Добавянето на страници става с десен бутон на мишката върху контролната.

Пример: D0904

procedure TForm1.FormCreate(Sender: TObject);

  VAR Z: Integer;

begin

ListBox1.Columns := 10;

For Z := 1 to 100 do

ListBox1.Items.Add(IntToStr(Z));

For Z := 0 to PageControl1.PageCount - 1 do    ListBox2.Items.Add(PageControl1.Pages[Z].Caption);

end;

procedure TForm1.ListBox2Click(Sender: TObject);

  VAR Z: Integer;

begin

Z := ListBox2.ItemIndex;

If Z > -1 Then

PageControl1.ActivePage := PageControl1.Pages[Z];

end;

procedure TForm1.Button1Click(Sender: TObject);

  VAR NewTabSheet: TTabSheet;

begin

C := C + 1;

Caption_ := ‘New Sheet ‘ + IntToStr ©;

If InputQuery (Caption_, ‘TabControl’, Caption_) Then Begin

NewTabSheet := TTabSheet.Create(PageControl1);

NewTabSheet.Visible := True;

NewTabSheet.Caption := Caption_;

NewTabSheet.PageControl := PageControl1;

PageControl1.ActivePage := NewTabSheet;

ListBox2.Items.Add(Caption_);

  end;//if

end;

procedure TForm1.PageControl1Change(Sender: TObject);

begin

  Label1.Parent := PageControl1.ActivePage;

end;

Глава 10

Рамки и дъщерни прозорци MDI - стр. 498

Създават се поне две форми, едната със стойност fsMDIForm за характеристиката си FormStyle, a другата със стойност fsMDIChild.

Дъщерните форми не се създават при стартиране а програмата и ти трябва да предоставиш начин  за създаване на един или повече дъщерни прозорци

Пример D1001.PAS D1002.PAS

Създаване на завършено меню Windows - стр. 499

Менюто има три елемента: Cascade, Tile, TileMode и Arrange Icons. За да се обработват трябва да се използват някой предварително дефинирани методи, налични за формите имащо стойност fsMDIForm на характеристиката FormStyle:

Методът Cascade подрежда каскадно всички отворени MDI прозорци. Дъщерните форми се подреждай, започвайки от горния ляв ъгъл.

Метода Tile подрежда прозорците така, че да не се припокриват. По подразбиране се подреждат хоризонтално. Това може да се промени чрез характеристиката TileMode. Тя има стойности tbVertical и tbHorizontal. Някой приложения проверяват дали е натиснат клавиша Schift при избиране на командата.

Процедурата ArrangeIcons подрежда всички минимизирани дъщерни прозорци, започвайки от долния ляв ъгъл на клиентската област. Отворените не се преместват.

Begin

  Cascade;

end;

По-добра алтернатива е да се постави компонентата Action List във формата и да се добави към нея серия предварително дефинирани MDI действия. Свързаните с това класове TWindowArrange, TWindowCascade, TWindowClose, TWindowTileHorizontal, TWindowTile-Vertical и TWindowMinimizeAll. Елементите от менюто ще извършват съответните действия и ще бъдат забранени, ако няма дъщерни прозорци.

Ето някой методи и характеристики, свързани с MDI:

ActiveMDIChild - Потребителя може да промени тази стойност, като избере  нов дъщерен прозорец, или програмата може да го промени с помощта на процедурата Next и  Prevision.

Характеристиката ClientHandle съдържа Windows манипулатора на MDI клиента, който покрива клиентската област на главната форма.

Характеристиката MDIChildCount съхранява броя на дъщерните прозорци.

Характеристиката MDIChildren представлява масив съдържащ дъщерните прозорци. Може да се употребява за намиране на прозорец.

Пример: D1001

public

{ Public declarations }

Counter: Integer;

end;

implementation

  uses D1002;

{$R *.dfm}

procedure TForm1.FormCreate(Sender: TObject);

  VAR F: TFont;

begin

F := FontDialog1.Font;

FormStyle := fsMDIForm;

Form2.Caption := ‘Child - Form 1’;

Counter := 1;

F := FontDialog1.Font;

//ShowMessage (OpenDialog1.FileName);

//ShowMessage (SaveDialog1.FileName);

end;

procedure TForm1.CreatrForm1Click(Sender: TObject);

  VAR Z: Integer;

ChildForm: TForm2;

begin

ChildForm := TForm2.Create(Application);

Inc (Counter);

ChildForm.Memo1.Text := IntToStr (Counter);

Childform.Caption := ‘Child Form ‘ + IntToStr (Counter);

end;

procedure TForm1.Cascade1Click(Sender: TObject);

begin

  Cascade;

end;

procedure TForm1.LoadFile1Click(Sender: TObject);

  VAR Name: String;

begin

Name := OpenDialog1.FileName;

Name := ‘c:\c.htm’;

(ActiveMDIChild as TForm2).Memo1.Lines.LoadFromFile(Name);

end;

procedure TForm1.ChageFont1Click(Sender: TObject);

begin

  (ActiveMDIChild as TForm2).Memo1.Font := FontDialog1.Font;

end;

procedure TForm1.SaveFile1Click(Sender: TObject);

begin

  (ActiveMDIChild as TForm2).Memo1.Lines.SaveToFile(SaveDialog1.FileName);

end;

Пример: D1002

var

Form2: TForm2;

fModified: Boolean;

 

implementation

{$R *.dfm}

procedure TForm2.FormCreate(Sender: TObject);

begin

FormStyle := fsMDIChild;

Caption := ‘Child Form 1’;

Memo1.Height := Height - 28;

Memo1.Width := Width - 9;

end;

procedure TForm2.FormResize(Sender: TObject);

begin

Memo1.Height := Height - 28;

Memo1.Width := Width - 9;

end;

procedure TForm2.Memo1Change(Sender: TObject);

begin

  fModified := True;

end;

procedure TForm2.FormCloseQuery(Sender: TObject; var CanClose: Boolean);

begin

  CanClose := Not fModified or (MessageDlg(‘File modified! Close before save?’, mtConfirmation, [mbYes, mbNo], 0) = mrYes)

end;

MDI приложения с различни дъщерни прозорци - стр. 506

Първата форма съдържа окръжност която се изчертава след натискане на мишката. Меню Circle позволява промяната цвета на запълване на окръжността.

Ако се подготви главно меню за дъщерната форма, то ще замени главното меню на рамката, когато активираме дъщерни прозорци. Дъщерния MDI прозорец не може да има собствено меню. Може да се слива менюто на рамката с дъщерния прозорец. Например: ако в тази програма менютата File и Window на рамката. Може да направите това, като зададеш следните стойности на характеристиката CroupIndex:

- Меню File, главна форма: 1

- Меню Window, главна форма: 3

- Меню Circle, дъщерни форма: 2

Втория дъщерен прозорец показва движещо се изображение. Квадрата (компонентата Shape) се премества в клиентската област на формата през фиксирани интервали от време, използвайки компонентата Timer, и отскача от краищата на формата. Идеята е, че квадратът има собствена позиции е асоцииран с Dir (съкратено от direction - направление) - другото поле на класа на формата, което може да приема една от следните стойности:

  Directions = (up_right, down_right, down_left, up_left)

Трябва да се проверяват и координатите на обекта:

if Shape1.Top <= 0 then...

if Shape1.Top + Chape1.Height >= ClientHeight then...

if Shape1.Left <= 0 then ...

if Shape1.Left + Shape1.Width >= ClientWidth then ...

procedure TBounceChildForm.Timer1Timer (Sender: TObject);

begin

case Dir of

up_right: begin

Shape1.Left := Shape1.Left + 3;

Shape1.Top := Shape1.Top - 3;

if Shape1.Top <= 0 then Dir := down_right;

if Shape1.Left + Shape1.Width >= ClientWidth then Dir := up_left;

end;//case

  ...

end;

Пример D1003

procedure TForm1.Button3Click(Sender: TObject);

begin

IF Shape1.Top > 10 Then

Shape1.Top := Shape1.Top - 10;

end;

procedure TForm1.Button1Click(Sender: TObject);

begin

If Shape1.Left > 10 Then

Shape1.Left := Shape1.Left - 10;

end;

procedure TForm1.Button4Click(Sender: TObject);

begin

  IF Shape1.Left + Shape1.Width < ClientWidth - 10 Then

Shape1.Left := Shape1.Left + 10;

end;

procedure TForm1.Button2Click(Sender: TObject);

begin

  IF Shape1.Top + Shape1.Height < ClientHeight - 10 Then

Shape1.Top := Shape1.Top + 10;

end;

Глава 11

Данни, файлове, бази данни и таблици - стр. 520

Обръщението към базата става със alias. Всяка таблица на Paradox и dBase записват данните като директории и всяка таблица е отделен файл (или множество файлове ако се използват индекси и др.).

Acces, InterBase и повечето SQL сървъри използват един голям файл, съдържащ цялата база данни с всичките индекси и др. файлове.

Приложенията за работа с бази данни в Delphi не взаимодейства директно с данните, към които се обръщат. Delphi взаимодейства с Borlad Database Engine (BDE), който има директен достъп до няколко източници на данни. Трябва да се инсталира BDE заедно с вашето приложение на клиентската машина. Delphi предлага версия на инсталационна програма (InstallShield), която може да бъде използвана, за да се подготви инсталацията на BDE, заедно с инсталацията на програмата ви. Файловете BDE са задължителни.

База данни е набор от таблици. Понякога може да се използва и простия начин описан в глава 22

Компоненти за работа с бази данни - стр. 523

Нужен е източник на данни представян от компонента DataSorce която е свързана с характеристиката DataSet. Тя може да представя таблици, резултат от изпълнението на заявка, процедура на сървър, данни получени от отдалечен сървър (с помощта на компонентите ClientDataSet), или друг собствен източник.

След като поставиш компонентата Table или Query във формата, може да използваш характеристиката DateSet на компонента DataSorse за обръщение към нея. За тази характеристика Object Inspector показва всички налични източници на данни в текущата форма или в други форми, свързани с текущата (File>UsesForm).

Таблици и заявки

Обекта Table сочи към таблица в базата данни. Задава се пътят на базата данни или псевдонима, като стойност на характеристиката DataBaseName. Object Inspector показва наличните бази данни, които са псевдоними на бази данни, инсталирани в BDE. Трябва да се зададе стойност и на характеристиката TableName след като се избере стойност на DatabaseName.  Object Inspector показва всички налични таблици в  текущата база данни (или директория)

Компонента Query e по-сложна таблица защото изисква команди на езика SQL. Има характеристика DateName но няма TableName. Таблицата се задава в SQL командите, съхранени в характеристиката SQL.

Например може да се напише следния прост израз:

select * from county

conty - името на таблицата

* - използване на всички полета.

Третата компонента, задаващ източник на данни, е StoredProc, която сочи към вградените процедури в база данни на SQL сървър. Вие можете да стартирате тези процедури и да получавате резултатите във формата на таблицата. Вградените процедури могат да се използват само за SQL сървъри, но винаги има изключения от това правило.

Състояние на източника на данни - стр. 524

Може да работиш в различни състояния. Характеристиката State ги показва, като приема различни стойности

dsEdit - източника на данни е  режим на редактиране. Това се постига, когато програмата извика метода Edit или характеристиката AutoEdit на компонента DataSource има стойност True и потребителят е започнал да редактира съдържанието на контролата, свързана с базата данни (например DBGrid и DBEEdit). Когато променените записи се изпратят към базата данни източника на данни излиза от състоянието dsEdit.

dsBrowse - източника на данни е в нормален режим за разглеждане, използва се за обхождане и търсене.

dsInser - добавяне на нов запис

dsInactive - източника на данни е затворен

dsSetKey - подготовка за търсене

dsCalcFields - когато се извършва изчисление на полетата, т.е. при всяко извикване на обработчика на събитието OnCalcFields.

dsNewValue, dsOldValue, dsCurValue - състояния на източника когато се извършва обновяване на кеша

dsFilter - при задаване на филтър

Други компоненти за връзка с база данни - стр. 525

- DataBase - използва се за контрол над транзакциите, сигурност и контрол на връзката. Използва се за свързване с отдалечени данни в клиент/сървър приложения или за да се предотвратят повтарящи се връзки към една и съща база данни от различни форми. Използва се и като локален псевдоним на базата, използван само в подпрограмата. След като веднъж локалния псевдоним е свързан с даден път, компонентите Table и Query могат да използват локални променливи.

- Sesion - предлага глобален контрол над връзките с базите данни за едно приложение, включително списък от съществуващите бази данни псевдоними, като и събитие за промяна на въвеждането над паролата за връзка (login) с базата данни.

- BatchMode - за извършване на пакетни операции: копиране, добавяне, обновяване, изтриване и др.

- UpdateSQL - позволява писането на SQL команди. Използва се като стойност на характеристиката UpdateObject.

Data - Aware контроли - стр. 527

- DBGrid - графична таблица, която може да покаже цялата таблица. Позволява превъртане и премине между записите, както и редактиране;

- DBNavigator - набор от бутони;

- DBText - показва съдържанието на поле, което не може да бъде променяно;

- DBEdit - редактиране на поле;

- DBMemo - промяна на големи текстови полета;

- DBImage - показва изображение свързано с поле от тип BLOB;

- DBListBox, DBComboBox - избиране на стойности от определено множество. Ако това множество е извлечено от друга таблица в базата данни или резултата от друга заявка, трябва да се използват DBLookupListBox, DBLookupComboBox;

- DBCheckBox - за полета тип Boolean;

- DBRadioGroup - набор от избори за взаимно изключващи се бутони;

- DBRichEditor - редактира и форматира текстов файл. Стандартна контрола за Win95;

- DBChar - data-aware версията на компонента Char.

Настройване на графичните таблици - стр. 528

Използван е примера COUNTRY.DB от базата данни DBDEMOS. Във формата се поставят компоненти: DateSource, Table и DBGrid.  Ако се зададе на компонентата Table стойност True на характеристиката Activev, данните ще се появят във таблицата още по време на редактирането. Сега може да стартираш програмата и да редактираш данните в таблицата. Това е възможно щото компонентата DBGrid има характеристика Options, включваща флага dgEditing, а характеристиката ReadOnly има стойност False. Може да се вмъкват нови записи на дадена позиция като се натисне клавиша Insert, или като се натисне стрелка на долу когато маркера е на последния запис. За изтриването на текущия запис се използва Ctrl + Del.

Компонентата DBGrid може да се променя и чрез характеристиката Columns. Тази характеристика представлява колекция от полета. Може да се избере поле което да се показва като колона в таблицата както и неговия цвят, шрифт и др. Някой характеристики като ButtonСтъуле и DropDownRows могат да се използват за създаване на падащи списъци.

Пример D1101

procedure TForm1.FormCreate(Sender: TObject);

begin

  //Това може да се избере и от Object Inspector

Table1.DatabaseName := ‘DBDEMOS’;

Table1.TableName := ‘country.db’;

Table1.Active := True;

DataSource1.DataSet := Table1;

DBGrid1.DataSource := DataSource1;

end;

{Състояние на балицата - стр. 531

Обработочика OnStateChane се изпълнява при промяна състоянието на таблицата.}

procedure TForm1.DataSource1StateChange(Sender: TObject);

VAR Title: String;

begin

  case Table1.State of

dsBrowse: Title := ‘Browse’;

dsEdit: Title := ‘Edit’;

dsInsert: Title := ‘Insert’;

else Title := ‘Other State’;

end;//case

Caption := ‘Grid Demo - ‘ + Title;

end;

Data-Aware контроли свързани с полетата на таблица

Използване на контролата DBЕdit - стр. 533

Вмъкват се три контроли DBEdit в таблицата. Избира се таблица с DateSorce, а полето се избира с DateField. Прибави контролата DBNavigator. Задава и се стойност на характеристиката Date Surce. Налага се да се забранят някой от бутоните на DBNavigator, като се премахне някой от елементите на множеството VisibleButton. В случая се премахват бутоните за изтриване и обновяване на данните а на характеристиката Flat се задава стойност True. Другите бутони автоматично се разрешават или забраняват.

DBEdit компонентите могат да се създават и автоматично. След като си задал всичко на Table контролата, щракни с десен бутон на мишката върху контролата, избери Fields Editor, прибави необходимите полета и с мишката завлечи до формата полетата които искаш да се показват в DBEdit компоненти.

Пример: D1102

Създаване на база данни - стр. 535

След като се дефинират полетата, може да се щракне с десния бутон на мишката върху компонента Table и да се избере командата Create Table. По този начин се създава нова таблица по време на дизайн. В този пример не е обходимо да се прави това, тъй като програмата създава таблицата при стартирането си, освен ако тя вече съществува.

procedure TForm1.FormCreate (Sender: TObject);

begin

if not Table1.Exists then Table1.CreateTable;

  Table1.Open;   //Активиране на таблица

  Table1.Edit    //Разрешава редактирането

end; // Това е манжда с грозде

За да може да работи този код, компонентата Table трябва да запише дефиницията на полетата в DFM файла заедно с другите си характеристики. Това става само ако е зададена стойност True на нейната характеристика StoreDefs

Дефиниране и вмъкване на полета: - стр. 536

const

  FirstName: Array [1..10] of String     = (‘koko1’, ‘koko2’, ‘koko3’,

‘koko4’, ‘koko5’, ‘koko6’, ‘koko7’, ‘koko8’, ‘koko9’, ‘koko10’);

  LastName: Array [‘1..10’] of String := (‘kiki1’, kiki2’, kiki3’,

kiki4’, kiki5’, kiki6’, kiki7’, kiki8’, kiki9’, kiki100);

NoDept = 4;

NoBranch = 30;

NewRecord = 10;

procedure TDbaForm.AddRandomDate;

var I: Integer;

begin

Randomize;

for I := 1 to NewRecords do

Table1.InsertRecords ([

LastNames [Random [Hight (LastNames)] + 1, Random (NoDept) + 1,

FirstNames [Random (Hight (FirstNames)) + 1],

Random (NoDept) + 1,

DbCombBox.Items [Random (NoBranch) + 1],

Boolean (Random (2)),

Date - Random (1000)]);

  ShowMessage (IntToStri (NewRecords) + ‘dded’);

end;

Малко компоненти:

- DBListBox - избор между предварително дефинирани елементи;

- DBComboBox - избиране и въвеждане на данни;

- DBRadioGroup - избиране на ограничени алтернативи. Позволява показване на екрана стойности съответстващи на различни вътрешни стойности чрез списък от низове Values. Имената се задават в Items.

Достъп до полетата на таблица - стр. 538

Компонентите свързани с полета (инстанции на класа TField) са невизуализирани, Date aware контролите са право свързани с Field обекти. Можем да получим достъп до стойности по индекс или по име.

Table1.Fields[0].AsString

Table1.FiiledByName (‘LastNems’).AsString

Table1 [‘lastName’].AsString

Компонентите от тип TField могат да се създават по време на дизайн чрез използване на редактора Field Editor. В този случай може да се зададат и някой от характеристиките.

Като се осъществяват достъп до стойността на полето, може да използваш фамилията от характеристиките AS, за преобразуване стойността на полето до определен тип данни (ако това е възможно), в противен случай възниква изключение).

AsBoolena: Boolen;

AsDateTime: TDateTime;

AsFloat: Double;

AsInteger: LongInt;

AsString: String;

AsVariant: Variant;

Характеристиките могат да се използват за четене или промяна стойността на полето ако е в режим на редактиране.  Може да се получи достъп до стойността на полето с характеристиката Value, която е тип TVariant.

Пример: D1103

procedure TForm1.FormCreate(Sender: TObject);

begin

{Йерархия на класовете за полета - стр. 541

Получаване достъп до характеристиката DisplayFormat на класа TFloatField.}

(Table1.FieldByName(‘Area’) as TFloatField).DisplayFormat := ‘### ### ###.##’;

//Извеждане съдържанието на поле

ShowMessage( String (Table1[’Name’]) + ‘: ‘ + String (Table1[’Population’]));

end;

procedure TForm1.Button1Click(Sender: TObject);

  VAR Z: Integer;

begin

For Z := 0 to Table1.FieldCount-1 do

Table1.Fields[Z].Alignment := taCenter;

end;

procedure TForm1.Button2Click(Sender: TObject);

  VAR Z: Integer;

begin

//Центриране на полета в DBGRid и DBEdit

For Z := 0 to Table1.FieldCount -1 do

    Table1.Fields[Z].Alignment := taLeftJustify;

end;

Наследници на класа TField

Наследник

Базов клас

Дефиниция

TstringField

TField

Текстови данни с фиксиран размер (максимум 8192 символа)

TCharField

TStringField

Съдържа един символ

TnumericField

TNumericField

Не се използва директно. Това е базов клас за всички класове, представящи числени полета

TIntegerField

TNumbericField

Цяло число (32 битово)

TNumericField

TField

Големи цели числа (64 битови)

TWordField

TIntegerField

Цяло число без знак ( 16 битово ))

TSmallInField

TIntegerField

Цяло число 16 битово

TAutoIncField

TIntegerField

Цяло, положително число, свързано с полетата с автоматично управление в Paradox, специално поле, чиято стойност се задава автоматично за всеки запис. Не винаги работят перфектно.

TFloatField

TNumericField

Число с плаваща запетая (8 байтово).

TCurrencyField

TFloatField

Парична стойност, покриващи интервала на типа Real.

TBCDField

TNumbericField

Реални числа с фиксиран брой цифри след десетичната точка.

TBooleanField

TField

Булева стойност

TDateField

TDateTimeField

Стойност на датата

TTimeField

TDateTimeField

Стойност на час

TBinaryField

TField

Не се използва директно. Това е базов клас за следващите два класа

TBbytesField

TBinaryField

Съдържа данни с голям (до 64К символа), но ограничен размер

TVarBytesField

TBytesFiled

Съдържа данни до 64К символа. Много сходен с базовия си клас, TBytesField

TblobFiled

TFiled

Двоични данни с неограничен размер (BLOB e е съкращение от Binary Large ObBject). Теоретичната граница за размера е 2GB.

TMemoField

TBlobField

Текст с произволен размер.

TGraphicField

TBlobField

Графика с произволен размер

TObjectField

TField

Базов клас за полетата, предлагащи поддръжка на обектни релационни бази данни

ТADField

TObkectField

ADT (Abstract Data Type) поле, отговарящо на поле за обект в обектно-релационни бази данни.

TArrayField

TObjectField

Масив от обекти в обектно релационни бази данни

TRefernceField

TObjectField

Указател към обект в обектно релационни бази данни

Добавяне на изчислителни полета 545

Може да се използва Fields Editor. Добави полето Calc. Избери тип Float. Изчисляването на полета става с: Избери за File Type Calculated. Избери на компонентата Table1 събитието onCalcFields:

procedure TForm1.Table1CalcFields(DataSet: TDataSet);

begin

  //Table1.Edit; // това аз го сложих трябва му при първото стартиране, после пречи

  Table1Calc.Value := Table1Population.Value / Table1Area.Value;

end;

Това правело същото но с подобрения:

try

Table1PopulationDensity.Value :=

Table1Population.Value / Table1Area.Value;

  Except

on Exception do

Table1PopulationDensity.Value := 0;

end;

Проверка за въведена стойност

if not Table1Area.IsNull and (Table1Area.Value <> 0) Then

  Table1PopulationDenstity.Value := Table1Population.Value / Table1Area.Value

 else

Table1PopulationDenstity.Value := 0;

Формата на числата в колоната зависят от характеристиката DisplayFormat. Ако се зададе стойност cbsEllipsis на характеристика ButtonStyle на компонентата DBGRid, в колоната при редакция се появява малък бутон с три точки, който генерира събитието OnEditButtonClick.

Ето го и целия пример D1101.PAS

procedure TForm1.DataSource1StateChange(Sender: TObject);

Var Title: String;

begin

  case Table1.State of

dsBrowse: Title := ‘Browse’;

dsEdit: Title := ‘Edit’;

dsInsert: Title := ‘Insert’;

else

Title := ‘Other state’;

end; //case

Caption := ‘Grid Demo - ‘ + Title;

end;

procedure TForm1.Button1Click(Sender: TObject);

begin

  ShowMessage (string (Table1 [‘Name’]) + ‘; ‘ + string (Table1 [‘Population’]));

end;

procedure TForm1.Button2Click(Sender: TObject);

VAR I: Integer;

begin

  For I := 0 to Table1.FieldCount - 1 do

Table1.Fields[I].Alignment := taCenter;

end;

procedure TForm1.Button3Click(Sender: TObject);

Var I: Integer;

begin

  For I := 0 to Table1.FieldCount - 1 do

Table1.Fields[I].Alignment := taLeftJustify;

end;

 

procedure TForm1.Table1CalcFields(DataSet: TDataSet);

begin

  //Table1.Edit; // това аз го сложих трябва му при първото стартиране, после пречи

  Table1Calc.Value := Table1Population.Value / Table1Area.Value;

end;

procedure TForm1.DBGrid1EditButtonClick(Sender: TObject);

begin

  MessageDlg ( Format (

‘the Calc (%.2n)’#13 +

‘is the Population (%.0n)’#13 +

‘devided by the Are (%.0n)’#13#13 +

‘Edit these two fields to change it.’,

[Table1Calc.AsFloat,

Table1Population.ASFloat,

Table1Area.AsFloat]),

mtInformation, [mbOK], 0);

end;

procedure TForm1.FormCreate(Sender: TObject);

begin

  Table1Calc.DisplayFormat := ‘### ###.##’;

end;

Търсене и добавяне на полета в таблица - стр. 548

За този пример  гепи таблицата EMPLOYEE.DB  в нова форма нали от DBDEMO. Използват се група от 4 бутона за смяна на записите и се свързват с методите на компонентата Table1:

Table1.First;

Table1.Prior;

Table1.Next;

Table1.Last;

Управлява се и характеристиката Enabled. Търсенето се активира с двата бутона асоциирани с текстовото поле. Първия за точно съвпадение, а втория за търсене на най-близкия елемент. И в давата случая сравняваме текста от полето с полетата LastName от Table1. Това става с операциите GotoKey, FindKey, GotoNearest и FindNearest.

За неиндексирани полета се използва метода Locate, ако полетата са индексирани търсенето става по-бързо. За индексирано търсене се задава стойност на характеристиката IndexFieldName на компонентата Table (в този случай избери LastName, FirstName от падащия списък). Ако индексът не е дефиниран се добавя вторичен индекс с помощта на Database Desktop или Databese Explorer.

Методите Find и Goto

Когато характеристиката за индекс е зададена правилно, може да се осъществи истинско търсене. Най-простия метод е FindNearest за приблизително съвпадение и FindKey за точно съвпадение.

Методите Goto - стр. 551

Тези два метода отговарят на извикванията от ниско ниво на BDE.

Характеристиката KeyFieldCount в случая показва, че трябва да се използва само първото от двете индексни полета.

За полетата без индекс се използва метода Locate. Задава се един низ, съдържащ полетата, по които се търси, и вариант със стойността или стойностите за търсене. За търсене в повече от едно поле на трябва масив от стойности (може да се създаде с функцията VarArrayCreate).

Пример: D1105

procedure M;

BEGIN

  MessageDlg('Няма никой в къщи', mtError, [mbOK], 0)

END;

procedure TForm1.FindNearestClick(Sender: TObject);

begin

  Table1.FindNearest([Edit1.Text]);

end;

procedure TForm1.FindKeyClick(Sender: TObject);

begin

  If not Table1.FindKey([Edit1.Text]) Then M;

end;

procedure TForm1.GotoNearestClick(Sender: TObject);

begin

  Table1.SetKey;

  //Table1 ['LastName'] := Edit1.Text;

  Table1.GotoNearest;

end;

procedure TForm1.GotoKeyClick(Sender: TObject);

begin

  Table1.SetKey;

  //Table1 ['LastName'] := Edit1.Text; //е да ама не мога да сменя полето за търсене

  Table1.KeyFieldCount := 1;

  if Not Table1.GotoKey Then M;

end;

procedure TForm1.LocateClick(Sender: TObject);

begin

  if not Table1.Locate ('FirstName', Edit1.Text,[]) Then M;

end;

procedure TForm1.Button1Click(Sender: TObject);

begin

  Table1.First;

end;

procedure TForm1.Button2Click(Sender: TObject);

begin

  Table1.Prior;

end;

procedure TForm1.Button3Click(Sender: TObject);

begin

  Table1.Next;

end;

procedure TForm1.Button4Click(Sender: TObject);

begin

  Table1.Last;

end;

//Сума в колона на таблица - стр. 552

procedure TForm1.SumSalaryClick(Sender: TObject);

  VAR Total: Real;

begin

  Table1.First;

  while not Table1.Eof do begin

    Total := Total + Table1Salary.Value; //Това не става ако не си прибавил полетата във FieldsEditor

    Table1.Next;

  end;//while

  SumSalary.Caption := Format ('%m', [Total]);

end;

Пример: D1106

procedure TForm1.FormCreate(Sender: TObject);

begin

  GotoBookmark.Enabled := False;

  FreeBookmark.Enabled := False;

end;

procedure TForm1.GetBookmarkClick(Sender: TObject);

begin

  BookMark := Table1.GetBookmark; //Запомняне номера на текущия запис

  GetBookmark.Enabled := False;

  GotoBookmark.Enabled := True;

  FreeBookmark.Enabled := True;

end;

procedure TForm1.GotoBookmarkClick(Sender: TObject);

begin

  Table1.GotoBookmark(BookMark);  //Препращане към запомен номер на запис

end;

procedure TForm1.FreeBookmarkClick(Sender: TObject);

begin

  Table1.FreeBookmark (BookMark);  //Изтриване на запомнен запис

  GetBookmark.Enabled := True;

  GotoBookmark.Enabled := False;

  FreeBookmark.Enabled := False;

end;

procedure TForm1.Button1Click(Sender: TObject);

begin

  Table1.First;

end;

procedure TForm1.Button2Click(Sender: TObject);

begin

  Table1.Prior;

end;

procedure TForm1.Button3Click(Sender: TObject);

begin

  Table1.Next;

end;

procedure TForm1.Button4Click(Sender: TObject);

begin

  Table1.Last;

end;

procedure TForm1.DataSource1DataChange(Sender: TObject; Field: TField);

begin

  //Актуализиране при смяна на запис

  Edit1.Text := Table1Area.AsString;

  Edit2.Text := Table1Population.AsString;

end;

По проста алтернатива е използването на характеристиката Bokmark на класа TDateSet, която съдържа отбелязване в таблицата, което трябва да бъде унищожено автоматично:

VAR Bookmark: TBookmark;

begin

  Bookmark := Table1.Bookmark;

  .........

  Tale1.Bookmark := Bookmark;

end;

VAR Bookmark: TBookmark; Total: Real;

Текущите стойности на таблицата се обработват от събитието OnDateChange на компонентата DateSource:

Пример: D1107

procedure TForm1.FormCreate(Sender: TObject);

begin

  //ComboBox1.Style := csDropDownList;

  Table1.First;

  while not Table1.Eof do begin

    ComboBox1.Items.Add(Table1Name.AsString);

    Table1.Next;

  end; //while

  Button7.Enabled := False;

end;

procedure TForm1.Button2Click(Sender: TObject);

begin

  Table1.Prior;

end;

procedure TForm1.Button1Click(Sender: TObject);

begin

  Table1.First;

end;

procedure TForm1.Button3Click(Sender: TObject);

begin

  Table1.Next;

end;

procedure TForm1.Button4Click(Sender: TObject);

begin

  Table1.Last;

end;

procedure TForm1.Button5Click(Sender: TObject);

begin

  Table1.Insert;

  ChangeEnabled;

end;

procedure TForm1.Button6Click(Sender: TObject);

begin

  Table1.Delete;

end;

procedure TForm1.Button7Click(Sender: TObject);

begin

  IF Table1.FindKey([ComboBox1.Text]) Then Begin

    Table1.Edit;

    Table1Name.AsString := Edit5.Text;

    Table1Capital.AsString := Edit1.Text;

    Table1Continent.AsString := Edit2.Text;

    Table1Area.AsString := Edit3.Text;

    Table1Population.AsString := Edit4.Text;

    Table1.Post;

  end

  else

    Table1.InsertRecord ([Edit5.Text, Edit1.Text, Edit2.Text, Edit3.Text, Edit4.Text]);

  ChangeEnabled;

end;

procedure TForm1.ComboBox1Change(Sender: TObject);

begin

  Table1.Locate('Name', String (ComboBox1.Text), []);

  EditChange;

end;

procedure TForm1.DataSource1DataChange(Sender: TObject; Field: TField);

begin

  EditChange;

end;

procedure TForm1.EditChange;

begin

  Edit1.Text := Table1Capital.AsString;

  Edit2.Text := Table1Continent.AsString;

  Edit3.Text := Table1Area.AsString;

  Edit4.Text := Table1Population.AsString;

  ComboBox1.Text := Table1Name.AsString;

  Edit5.Text := Table1Name.AsString;

end;

procedure TForm1.FormKeyPress(Sender: TObject; var Key: Char);

begin

  if not (Table1.State in [dsEdit, dsInsert]) Then

  Table1.Edit;

  //Този метод е свързан със събитието OnKeyPress на петте компоненти и е подобен на събитието OnDropDown на комбинирания списък.

end;

procedure TForm1.Table1BeforePost(DataSet: TDataSet);

begin

  IF Table1Area.Value < 100 Then ShowMessage('mara ba');

    //Raise Exception.Create('Area too small');

end;

procedure TForm1.ChangeEnabled;

  VAR Z: Integer;

begin

  For Z := 0 to ComponentCount -1 do

    IF Components[Z] is TButton Then

      (Components[Z] as TButton).Enabled := not (Components[Z] as TButton).Enabled;

    ComboBox1.Enabled := not ComboBox1.Enabled;

end;

procedure TForm1.Edit5Exit(Sender: TObject);

begin

  If (TAble1.State = dsEdit) or (Table1.State = dsInsert) Then

    if Edit5.Text <> '' Then

      Table1Name.AsString := Edit5.Text

    else Begin

      Edit5.SetFocus;

      raise Exception.Create('Undefined Countru');

    end; //else

end;

Събития свързани с бази данни - стр. 563

Пример D1108

procedure TForm1.AddToList(Str: String); //public declaration

begin

  ListBox1.ItemIndex := ListBox1.Items.Add(Str);

end;

//Това се задава на всички полета със Fields Editor. Скивай къде е декларирано!!!

procedure TForm1.FieldChange(Sender: TField);

begin

  AddToList('Field -' + Sender.FieldName + ' OnChange');

end;

procedure TForm1.Table1AfterEdit(DataSet: TDataSet);

begin

  AddToList('Table1.AfterEdit');

end;

procedure TForm1.Table2AfterEdit(DataSet: TDataSet);

begin

  //AddToList('Table2.AfterEdit');

end;

procedure TForm1.Table2EventNoGetText(Sender: TField; var Text: String;

  DisplayText: Boolean);

begin

  case Sender.AsInteger of

    1: Text := '1';

    2: Text := '2';

    3: Text := '3';

    4: Text := '4';

    5: Text := '5';

  else Text := '0';

  end;

end;

procedure TForm1.Table2EventNoSetText(Sender: TField; const Text: String);

begin

  case StrToInt (Text) of

    0 :Sender.Value := 0;

    1 :Sender.Value := 1;

    2 :Sender.Value := 2;

    3 :Sender.Value := 3;

    4 :Sender.Value := 4;

    5 :Sender.Value := 5;

  end;

end;

procedure TForm1.DBGrid2ColumnMoved(Sender: TObject; FromIndex,

  ToIndex: Integer);

begin

  AddToList('Move Field' + IntToStr (ToIndex));

end;

Компонентите MonthCalendar, DBImage и DBMemo - стр. 567

Пример D1109

procedure TForm1.DataSource1DataChange(Sender: TObject; Field: TField);

begin

  MonthCalendar1.Date := Table1Event_Date.Value;

end;

procedure TForm1.FormCreate(Sender: TObject);

begin

  DBImage1.Stretch := True; //Разпъване на картинката по цялата компонента

end;

procedure TForm1.DataSource1UpdateData(Sender: TObject);

begin

  //Table1Event_Date.Value := MonthCalendar1.Date;

end;

Избиране на таблица по време на програмата - стр. 569

Върхъ било извикването на процедурата GetDataBaseNames на глобалния обект Session. За всяко приложение работещо с бази данни, автоматично се дефинира се дефинира и инициализира обект от тип TSession, а за да имаш достъп до методите му трябва да включиш модула DBTables в клаузата uses.

Като допълнение към примера се прибавя код с който при двукратно натискане на мишката таблицата се появява в нова форма.

Пример D1110

procedure TForm1.FormCreate(Sender: TObject);

begin

  //Събиране на списъка

  Session.GetDatabaseNames(ComboBox1.Items);

  //Инициализиране на списъка

  ComboBox1.Text := 'DBDEMOS';

  ComboBox1Change(self);

  //Задаване на началната селекция

  ListBox1.ItemIndex := 0;

  ListBox1Click(Self);

  ListBox2.Columns := 3; //Броя на колоните

end;

procedure TForm1.ComboBox1Change(Sender: TObject);

begin

  //Събиране имената на таблиците в ListBox1

  Session.GetTableNames(ComboBox1.Text, '', True, False, ListBox1.Items);

end;

procedure TForm1.ListBox1Click(Sender: TObject);

  VAR Z: Integer;

begin

  Table1.Close;

  Table1.DatabaseName := ComboBox1.Text;

  Table1.TableName := ListBox1.Items[ListBox1.ItemIndex];

  Table1.Open;

  Caption := Format ('Table %s %s ', [Table1.DatabaseName, Table1.TableName]);

  //Показване списък на полетата

  ListBox2.Items.Clear;

  For Z := 0 to Table1.FieldCount -1 do Begin ListBox2.Items.Add(Table1.Fields[Z].FieldName);

  end; //next

end;

procedure TForm1.ListBox2Click(Sender: TObject);

begin

  //Промяна видимостта на полета  Table1.FieldByName(ListBox2.Items[ListBox2.ItemIndex]).Visible := not Table1.FieldByName(ListBox2.Items[ListBox2.ItemIndex]).Visible;

end;

//При двукратно натискане на мишката таблицата се появява в нова форма.

procedure TForm1.ListBox1DblClick(Sender: TObject);

  VAR Gridform: TForm1;

begin

  Gridform := TForm1.Create(Self);

  //свързване на компонентата Table с избраната таблица и нейното активиране

  with Gridform do begin

    Table1.DatabaseName := ComboBox1.Text;

    Table1.TableName := ListBox1.Items[ListBox1.ItemIndex];

    try

      Table1.Open;

      Show;

    except

      Close;

    end;

  end; //with

end;

Това не е към примера!

Може да се променя избора на всеки елемент в списъка докато модалната форма е активна. Когато тя се затвори, другата форма извлича избраните елементи от нея и задава съответните стойности на характеристиката Visible на полетата:

VAR I: Integer;

begin

FieldsForm.FieldsList.Clear;

for I := 0 to Table1.FieldCount - 1 do begin

FieldsForm.FieledsList.Items.Add (Table1.Fields[I].FiledName);

if Table1.Fields[I].Visible then

FiieldsForm.FieldsList.Select[I] := True;

end;

if FieldsForm.ShowModal = mrOK then

for I := 0 to Table1.FieldCount -1 do

Table1.Fields.Visible[I] := FieldsForm.FieldsList.Select[I];

FieldsForm.FieldsList.Clear;

end;

Използване на таблиците като файлове - стр. 574 //ебаси заглавието

Пример: D1111

Първо създай базата от данни Exampel с Dadatabase -> Explorer или по друг начин. Ако в компонентата ListBox не се появят имена на таблици създай нова.

Сега може да копираш BMP изображение от друго приложение в клип борда на Windows или да получиш копие на изображението на екрана. Това става с клавиша PrintSсreen за получаване изображението на целия екран или Alt + PrintScreen за получаване на изображение на текущия прозорец.

При натискането на бутона Add се добавя нов запис и веднага съдържанието на клип борда се записва ако разбира се то е ВМР формат. Иначе нищо не става

Та значи: сложи две компоненти Table. Втората не е свързана с компонентата DataSource. Всички контроли имат стойност True за характеристиката си Flat ама аз несъм я променял.

Метода IndexOf на списъка с низове връща индекса на низа или -1 ако той не е в списъка. Ако съществува програмата генерира изключение от класа, който е дефиниран в кода като наследника класа EDatabaseError.

Същото изключение се генерира ако в метода New1click

if InputQuery ... then

else

if Sender = self then

raise EmuDataBaseError.Create (‘Table creation abortet by the user’);

!Прибави към Uses Clipbrd;

{ Public declarations }

    DataBaseName: String;

    TBNames: TStringList;

procedure TForm1.FormCreate(Sender: TObject);

  VAR Code: Word; Done: Boolean; Bitmap: TBitmap;

begin

  DataBaseName := 'Exampel'; //Името на базата с данни

  TBNames := TStringList.Create;

  //Вземи списъка на таблиците

  Session.GetTableNames(DataBaseName, '', False, False, TBNames);

  ListBox1.Items :=TBNames;

{  While not Done do Try

    Code := MessageDlg('Ще правиш ли нова таблица?' + #13 + 'No - Зареждане на съществуваща' + #13 + 'Cansel за изход', mtInformation, [mbYes, mbNo, mbCancel], 0);

    IF Code = idYes Then New1Click(self)

    else IF Code = idNo Then Open1Click(Self)

    else Application.Terminate;

    Done := True;

    Except

    //on E:MyDateBaseError do ShowMessage (E.Message);

  end; //try except}

  IF Clipboard.HasFormat(CF_BITMAP) Then

    Image1.Picture.Bitmap.Assign(Clipboard);

end;

procedure TForm1.New1Click(Sender: TObject);

  VAR TableName: String; Z: Integer;

begin

  TableName := '';

  //Въвеждане името на таблицата

  InputQuery('NewTable', 'Enter a new table name', TableName);

  if TableName = ''  Then Begin

    //raise EMyDateBaseError.Create ('Invalid table name');

    ShowMessage('Invalid table name');

    Exit;

  end; //if

  TBNames.Clear;

//  try

    Session.GetTableNames(DataBaseName, '', False, False, TBNames);

    //Проверка дали има таблица със същото име

    if TBNames.IndexOf(TableName) >= 0 Then

    //raise EMyDateBaseError.Create ('Table already exists');

      ShowMessage('Table already exists');

      Exit;

//  finally

    TBNames.Free;

//  end;

//създаване на таблица

  Table1.Close;

  Table1.TableName := TableName;

  Table1.TableType := ttParadox;

//дефиниране на трите полета

  with Table1.FieldDefs do Begin

    Clear;

    Add('Description', ftString, 50, True);

    Add('Time', ftDateTime, 0, False);

    Add('BMP', ftGraphic, 0, False);

  end;//with

  Table1.IndexDefs.Clear;

  Table1.IndexDefs.Add('DecrIdex', 'Description', [ixPrimary, ixUnique]);

//създаване на таблица отговаряща на горната дефиниция

  Table1.CreateTable;

  Table1.Open;

  Caption := 'Create table ' + TableName;

end;

procedure TForm1.Open1Click(Sender: TObject);

  VAR Z, N, F1, F2, F3: Integer;

begin

  TBNames.Clear;

  Session.GetTableNames(DataBaseName, '', False, False, TbNames);

  ListBox1.Clear;

  Table2.DatabaseName := DataBaseName; // Това е хубаво да има

//Проверка на таблиците за ....

  For Z := 0 to TbNames.Count -1 do begin

    Table1.Close;

    Table2.Close;

    Table2.TableName := TbNames[Z];

    Table2.Open;

    //TAble2.FieldDefs.Update;

    F1 := 0;

    F2 := 0;

    F3 := 0;

    IF Table2.FieldCount = 3 then //броя на полетата

      For N := 0 to 2 do // и какъв тип са

        if Table2.FieldDefs[N].DataType = ftString Then

          Inc (F1)

        else if Table2.FieldDefs[N].DataType = ftDateTime Then

          Inc (F2)

        else if Table2.FieldDefs[N].DataType = ftGraphic Then

          Inc (f3);

      if (F1 = 1) AND (F2 = 1) AND (F3 = 1) THEN

        ListBox1.Items.Add(Table2.TableName);

   end;//next

  TbNames.Clear;

  Table2.Close;

end;

procedure TForm1.Add1Click(Sender: TObject);

  VAR Descripion: String;

begin

  IF Clipboard.HasFormat(CF_BITMAP) Then

    IF InputQuery('Добавяне', 'Въведи текста', Descripion) Then Begin

      Table1.Insert;

      Table1['Description'] := Descripion;

      Table1['Time'] := Now;

      //Table1['BMP'].PasteFromClipboard; //Изобщо не тръгва

      // Table1BMP.Assign(Clipboard); //Дъни се по време на работа

      DBImage1.PasteFromClipboard; //Затова сложих това

      Table1.Post; //ShowMessage('Добавено');

  end

  else

    ShowMessage('Копирай в клипборда BMP');

end;

procedure TForm1.ListBox1Click(Sender: TObject);

begin

//Избиране на таблица

  Table1.Close;

  Table1.TableName := ListBox1.Items[ListBox1.ItemIndex];

  Table1.Open;

  Caption := Table1.TableName;

end;

procedure TForm1.PastePicture1Click(Sender: TObject);

begin

//Промяна на текущата картинка ако в клипборда има ВМР

  IF Clipboard.HasFormat(CF_BITMAP)

    Then DBImage1.PasteFromClipboard

  else

    ShowMessage('Формата в клипборда не е BMP');

end;

procedure TForm1.DELETE1Click(Sender: TObject);

begin

  IF MessageDlg('Да забърша ли записа?', mtConfirmation, [mbYes, mbNo], 0) = idYes Then

    Table1.Delete;

end;

Компонентата DBCtrlGrid - стр. 583

Пример: D1112

Вземи компоненти Table и DataSorce, свържи ги с таблицата COUNTRY.DB. Постави компонентата DBCtrlGrid във формата, задай нейния размер и броя на редовете и колоните, а след това постави в DBCtrlGtrid две компоненти DBEdit свързани да речем с полетата Name и Capital. Ето и някой характеристики:

object EBCtrlGrid1: TDBCtrlGrid

ColCount = 2;

DataSoruce = DataSource1

RowCount = 2

  .....

procedure TForm1.FormResize(Sender: TObject);

begin

  DBCtrlGrid1.RowCount := (ClientHeight - Panel1.Height) div 60;

  DBCtrlGrid1.Height := ClientHeight - Panel1.Height;

  DBCtrlGrid1.Width := ClientWidth;

end;

 

procedure TForm1.TrackBar1Change(Sender: TObject);

begin

  DBCtrlGrid1.ColCount := TrackBar1.Position;

  DBCtrlGrid1.Width := ClientWidth;

end;

Диаграми свързани с бази данни - DBChar - стр. 585

И тази компонента я нямам. Ми ето някакъв сорс па дано се ориентирам после:

begin

DBChar1.Title.Text [0] := ‘Population of Countries’;

(DBChar1.Series [0] as TPieSeries).PieValues.ValueSource := ‘Population’;

DBChar1.Title.Text[0] := “Population of Countries’);

(DBChar1.Series (0) as TPieSeries).PieValues.ValuesSource := ‘Population’;

Глава 12

Модули за данни - стр. 590

Клиент сървър програмирането се разделя на две логически нива. Едното ниво са данните а другата програмата. В последствие се разделило на три нива. Третото е правилата за достъп до данните.

Свързването на визуална форма с възможността за достъп до компонентитеe на друга форма или модул става с File>Use Unit. Ako използваш Database Form Expert, може да използваш опциите от последната страница за създаване и форма, и модул за данни за нейните компоненти, осъществяващи достъп до данни. Модулът за данни се показва като празен прозорец, в който може да се добавят компоненти.

Класът TDataMoudle е пряк наследник на класа TComponents

Пример: D1201.PAS

Намиране на най-голямо число в колона и запомнянето номера на текущия запис:

procedure TForm1.Button1Click(Sender: TObject);

  VAR BookMark: TBookmark; Max: Integer;

begin

  BookMark := Table1.GetBookmark;

  try

    Table1.DisableControls;

    Max := 0;

    Try

      Table1.First;

      While not Table1.Eof do begin

        If Table1CustNo.AsInteger > Max Then Max := Table1CustNo.AsInteger;

        Table1.Next;

      end;

      finally

        Table1.EnableControls;

      end;

    finally

      Table1.GotoBookmark(BookMark);

      Table1.FreeBookmark(BookMark);

    end;

    Button1.Caption := IntToStr (Max);

end;

Стандартно филтриране на таблица - стр. 596

Пример D1202

Най-простата възможност е да се зададе интервал за стойностите на индексирано поле. След това може да се изберат да речем записите между две стойности.

procedure TForm1.Button1Click(Sender: TObject);

begin

  Table1.IndexName := 'ByCompany';

  Table1.SetRange([Edit1.Text], [Edit2.Text]);

  Table1.Refresh;

end;

Като алтернатива можеш да зададеш стойности на ключа в метода GotoKey, като извикаш последователно методите SetRangeStart, SetRangeEnd, ApplyRange. Най-просто е да се извика метода SetRange и да му се предадат два масива от стойности с толкова елементи, колкото са и полетата, участващи в текущия индекс. Прекратява се с Table1.CancelRange.

Собствено филтриране на таблица - стр. 597

Пример: D1203

Задава се True на характеристиката Filtered на компонента Table и за всеки запис ще се извиква обработчикът на събитието onFilterRecord. В този обработчик се задава собствен филтър:

procedure TForm1.Edit2Change(Sender: TObject);

begin

  FieldName := 'Continent';

  FindText := Edit2.Text;

  Table1.Filtered := True;

end;

procedure TForm1.Edit1Change(Sender: TObject);

begin

  FieldName := 'Capital';

  FindText := Edit1.Text;

  Table1.Filtered := True;

end;

procedure TForm1.Table1FilterRecord(DataSet: TDataSet;

  var Accept: Boolean);

begin

  IF FieldName = 'Capital' THEN

    if Copy (Table1Capital.Value,1, Length (FindText)) = FindText Then Accept := True

    else Accept := False;

  IF FieldName = 'Continent' THEN

    IF Copy (Table1Continent.Value, 1, Length(FindText)) = FindText THEN Accept := True

    else Accept := False;

end;

procedure TForm1.Button2Click(Sender: TObject);

begin

  Table1.Filtered := False;

end;

procedure TForm1.FormCreate(Sender: TObject);

  VAR T: String;

begin

  FieldName := 'Continent';

  T := Copy (Table1[FieldName], 1, 2);

  ShowMessage(T);

end;

Търсенето в първото поле става ама в континентие се дъни.

Пример D1204

Този код проверява дали стойността на текущия запис е вече включена в списъка. Ако не е се добавя. Тези два списъка трябва да се обновяват при всяка промяна на съществуващ запис. При мене и това се дъни.

procedure TForm1.FormCreate(Sender: TObject);

begin

  While not Table1.Eof do begin

  //добавяне на уникални стойности

    if ListBox2.Items.IndexOf(Table1Country.AsString) < 0 Then      ListBox2.Items.Add(Table1Country.AsString);

    //Тука повтаря празни полета

    if ListBox1.Items.IndexOf(Table1State.AsString) < 0 Then      ListBox1.Items.Add(Table1State.AsString);

    Table1.Next;

  end; //while

end;

{Това проверява дали щатът на текущия запис са между избраните елементи в списъците (които позволяват множествена но не и разширена селекция. Само дето нищо не прави де ама както и да е}

procedure TForm1.Table1FilterRecord(DataSet: TDataSet;

  var Accept: Boolean);

begin

{Ако има избран елемент в списъка с имената на страните се показва запис}

  With ListBox2 do

    Accept := Selected[Items.IndexOf(Table1Country.AsString)];

  IF Accept = True Then

    Label1.Caption := 'Country';

  With ListBox1 do

    IF Selected [Items.IndexOf(Table1State.AsString)] Then begin

      Accept := True;

      Label1.Caption := 'State';

    end;

end;

procedure TForm1.Button1Click(Sender: TObject);

begin

  Table1.Filtered := True;

end;

MDI приложение с несинхронизирани изгледи

Понякога се налага да се създават форми които показват различен запис. Главната форма на примера е MDI рамка и има следните настройки:

object FrameForm: TFrameForm

Caption = ‘MDI View’

WindowMenu = Windows1

object MainMenu1: TMainMenu

object NewRecordView1: TMenuItem...

object NewGridView1: TMenuItem....

object N1: TMenuItem....

object Close1: TMenuItem...

  object Windows: TMenuItem....

object Title1: TmenuItem...

object Cascase1: TMenuItem...

  object Help1: TMenuItem...

object Ablut1: TmenuItem...

Главната форма е единствата, която се създава по време на стартиране на програмата, така че MDI рамката ще се покаже празна. Дъщерните форми се създават с помощта на командите от менюто File и те автоматично създават собствени модули за данни. Поради тази причина, аз не само съм премахнал дъщерните форми и модула за данни от списъка с автоматично създаване форми, но са премахнати глобалните променливи, сочещи към тези обекти.

Пример D1202... ама не върви

Кодът за създаване на дъщерни форми е маса прост:

procedure TFrameForm.NewRecordView1Click(Sender: TObject);

begin

  with TRecordForm.Create (Application) do Show;

end;

При създаване на изглежда, той ще генерира модул, за данни и го задава като стойност на локалното поле на класа DM, което е дефинирано във всяка от формите. След това програмата свързва всички data-aware контроли с източника на данни от току-що създадения модул за данни.

procedure TRecordForm.FormCreate(Sender: TObject);

VAR I: Integer;

begin

DM := TCustomerDM.Create (self);

//задаване на навигатора

DBBGrid1.DataSource := DM.DataSource1;

//свързване на DBEdit контролите

For I := 0 to ControlCunt - 1 do

if Controls [I] is TDBEdit Then

TDBedit (Controls [I].DataSource : DM.DataSource;

end;

procedure TRecordForm.FormClose(Sender: TObject; var Action: TCloseAction);

begin

  Action := caFree;

end;

Модула за данни променя заглавието на свързаната с него форма за да отговаря на текущия запис:

procedure TCustomerDM.DataSource1DataChange(Sender: TObject; Field: TField);

begin

  (Owner as TForm).Caption := (Owner as TForm).Hint + ‘ - ‘ + Table1Company.AsString;

end;

Използване на компонентата Query

Тоя бил свързал компонентата Query със стандартния псевдоним DBDEMO и бил въвел текста на простата команда

select * from Country //ама не пише че се пише в характеристиката SQL

Трябвало да активираш компонентата и полетата на първия запис се появявали като обикновено в текстовите полета. {ама кои полета ???? нали} Ако искаш да промениш текста на SQL командите в заявката по време на работа на програмата, трябва първо да зададеш стойност False на характеристиката Active. След като промениш текста на SQL заявката, може да я активираш отново.

Първия радио бутон се използва за избирането на SQL заявка по подразбиране и този бутон е избрания по време на стартиране на програмата. Втория и третия избират само записите които имат определена стойност в полето Continent, като се добавя клаузата where към SQL заявката. Четвъртия бутон позволява въвеждането на текста за where клаузата в текстово поле.

Въвеждането на грешен текст може да доведе до опасни ситуации:

Пример: D1206

procedure TForm1.FormCreate(Sender: TObject);

begin

 RadioButton1.Caption := 'All table';

 RadioButton2.Caption := 'North America';

 RadioButton3.Caption := 'South America';

Query1.DatabaseName := 'DBDEMOS';

end;

procedure TForm1.RadioButton1Click(Sender: TObject);

begin

  Query1.Close;

  Query1.SQL.Clear;

  Query1.SQL.Add('SELECT * FROM COUNTRY');

  Query1.Open;

end;

procedure TForm1.RadioButton2Click(Sender: TObject);

begin

  Query1.Close;

  Query1.SQL.Clear;

  Query1.SQL.Add('SELECT * FROM COUNTRY');

  Query1.SQL.Add('WHERE CONTINENT = "' + (Sender as TRadioButton).Caption + '"');

  Query1.Open;

end;

procedure TForm1.RadioButton3Click(Sender: TObject);

begin

  Query1.Close;

  Query1.SQL.Clear;

  Query1.SQL.Add('SELECT * FROM COUNTRY');

  Query1.SQL.Add('WHERE CONTINENT <> "' + (Sender as TRadioButton).Caption + '"');

  Query1.Open;

end;

procedure TForm1.RadioButton4Click(Sender: TObject);

begin

  Query1.Close;

  Query1.SQL.Clear;

  Query1.SQL.Add('SELECT * FROM COUNTRY ');

  Query1.SQL.Add('WHERE ' + Edit1.Text);

  Query1.Open;

end;

procedure TForm1.Edit1KeyPress(Sender: TObject; var Key: Char);

begin

  IF KEY = #13 THEN BEGIN

    Query1.Close;

    Query1.SQL.Clear;

    Query1.SQL.Add('SELECT * FROM COUNTRY ');

    Query1.SQL.Add('WHERE ' + Edit1.Text);

    try

      Query1.Open;

    except

      ON EDatabaseError do ShowMessage('Invalid condition' + #13 + Edit1.Text);

    end; //try не бачка

  END;

end;

Заявка с параметри - стр. 607

Вместо да се пише всеки път нова заявка може да се напише една заявка с параметър.

Ако решим да избираме страните от даден континент, може да се напише:

select *from Country where Continent = :Continent

:Contitnent е параметъра на заявката. Може да се зададе неговия тип и стойност, с редактора на характеристиката Params на компонентата Quiry

Формата за този пример използва списък вместо радио бутони взет от базата данни по време на стартирането на програмата. Това се прави с компонентата Query2 .

След активирането на тази заявка,програмата обхожда нейния резултат извличайки всички стойности и добавяйки ги към списъка

Пример: D1207

procedure TForm1.FormCreate(Sender: TObject);

begin

  Query1.DatabaseName := 'DBDEMOS';

  Query2.DatabaseName := 'DBDEMOS';

  Query1.SQL.Clear;

  Query1.SQL.Add('SELECT * FROM COUNTRY WHERE CONTINENT LIKE :CONTINENT');

  //Query1.Params[0].DataType :=('ttString');

  Query2.SQL.Clear;

  Query2.SQL.Add('SELECT DISTINCT CONTINENT FROM COUNTRY ORDER BY CONTINENT DESC');

  Query2.Open;

  WHILE NOT Query2.Eof do begin  ListBox1.Items.Add(Query2.Fields[0].AsString;

    Query2.Params.Add;

    Query2.Next;

  end; //while

  ListBox1.ItemIndex := 0;

//  Query1.Prepare; //подготовка за отваряне на заявка

  Query1.Params[0].Value := ListBox1.Items[0];

  Query1.Open;

end;

procedure TForm1.ListBox1Click(Sender: TObject);

begin

  Query1.Close;

  //Отваряне на първата заявка

  Query1.Params[0].Value := ListBox1.Items[ListBox1.ItemIndex];

  Query1.Open;

end;

procedure TForm1.Query1BeforePost(DataSet: TDataSet);

  VAR Continent_: String;

begin

{Ако се добавя запис и е въведен нов континент той трябва да се добави в списъка:}

  Continent_ := Query1.FieldByName ('Continent').AsString;

  IF ListBox1.Items.IndexOf(Continent_) < 0 Then

    ListBox1.Items.Add(Continent_);

end;

procedure TForm1.FormDestroy(Sender: TObject);

begin

  Query1.Close;

  Query1.UnPrepare;

end;

Използване на множество таблици - стр. 610

Връзката master/detail между две таблици или заявки позволява да се избират данни от второ множество ако записите са свързани с текущия елемент от първото множество. Например може да се избере потребител от главната таблица и да му видите поръчките във втората.

Свързване, зададено в SQL заявка, може да дефинира и маса други типове взаимовръзки между таблиците.

Понеже SQL се изпълнява на сървъра, докато взаимовръзките master/detail и lookup се изпълняват от клиента, първата има по-голямо значение при клиент/сървър програмирането, докато връзките, дефинирани директно в Delphi, могат да предложат по-добра производителност при осъществяване на достъп до локални таблици.

Създаване на Master/Detail форма чрез Data Base From Wizard

Като го стартираш това чудо ще те попита дали искаш да създадеш форма, базирана на една таблица, или Master/detail форма. Master/Detail формата използва две таблици със взаимовръзки едно към много. Това поведение се дефинира с подходяща SQL команда в компонентата Query или с някои характеристики на компонентата Table. И по двата начина чудото само генерира кода. Сега примера ще използва таблиците Customer и Order.

Ето стъпките които трябва да направиш за да създадеш примера МastDet:

1.

Създай нов проект и стартирай чудото Dababase Form Wizard.

2.

В първата страница избери опциите master/detail формата и Table обекти.

3.

Избери (от втора страница) tablicata CUSTOMER.DB от базата DBDEMOS (Program Files\Common Files\Borland Shared\Data) като главна. Избери някой от нейните полета и задай хоризонтално или вертикално подреждане;

4.

Като вторична таблица избери ORDERS.DB. Избери някой полета от тази таблица и избери подразбиращо се подреждане;

5.

Сега вече си в последната страница. Избери индекса CustNo и след това избери същото поле и в двата списъка с полета;

6.

Натисни бутона Add;

7.

Генерирай формата като главна, без да избираш радиобутона Form and DataModule, и програмата е завършена.

object Table1: TTable

DataBaseName = ‘DBDEMOS’

TableName = ‘CUSTOMER.DB’

end

object Table2: TTable

DataBaseName = ‘DBDEMOS’

TableName = ‘ORDERS.DB’

IndexFieldName = ‘CustNo’

MasterFields = ‘CustNo’

MasterSource = DataSource1

end

Структурата Master/Detail и компонентата Query - стр. 613

Сега се свързват таблиците ORDERS.DB с ITEMS.DB с Query. Таблиците могат да се свържат с полета OrderNo. Като се генерира кода, програмата ще изглежда така:

select

Items.”OrderNo”,

Items.”ItemNo”,

Items.”PartNo”,

Items.”Qty”

from

Items

where

“Items”.”OrderNO” = :”OrderNo”

Полето OrderNo е свързано с първата компонента Query, защото характеристиката DaAtaSource на Query2 има стойност DataSource1, която е свързана с Query1. Всеки път когато текущия запис от първия източник на данни се променя, компонента Query2 се обновява като всички други компоненти, свързани с DataSource 1. Полето, използвано за връзка в този случай, е полето със същото име като параметъра на заявката.

Използване на комбиниран списък LookUp

Компонентата DBLookupListBox може да се свърже с два източника на данни, единия който съдържа истинските данни, а втория - данни за показване. Сега се задава стойност CustNо на характеристиката DateSource1 обаче ще се показват данни от другата таблица. След това се добавя нов източник на данни DateSource3 свързан с Table1, която е свързана с CUSTOMER.DB.

DataSource := DataSource1;

DataField := ‘CustNo’;    //Полето в което ще се сменя стойността

KeyField := ‘CustNo’; //Полето от което се взема стойността за смяна

ListField := ‘Fax’; //Полето което ще се показва в контролата

//ListSource := DataSource3; //Свързан с Table1

ListSource := DataSource2;

Може да се покажат и повече от едно поле ако се зададе по-голяма стойност на характеристиката DropDownWidth (Само дето липсва).

Пример: D1208

Lookup поле в графична таблица - стр. 615

Пример D1209.PAS

Може да се постави падащо меню с фиксиран избор в DBGrid с помощта на подхарактеристиката PickList на характеристиката Columns. За настройването на графична таблица се използва FieldEditor:

В примера потребителя може да избере от списък служителите. Добави компоненти Table и DataSource и ги свържи с таблицата EMPLOYEE.DB. След това отвори Fileds Editor за втората таблица (с поръчките) и добави всички нейни полета. Избери полето EmpNo и задай стойност False на характеристиката му Visible, за да се премахне от таблицата.

Следва дефиниция на lookup полето. Използва се Fields Editor на Table2 за дефиниране на новото поле Employye и избери радио бутона LookUp. В Object Inspector задай следните характеристики на Table2Employee щото иначе се дъни:

LookupDataSet = Table1

KeyFields = EmpNo

LookupKeyFields = EmpNo

LookupResultField = LastName

 

procedure TForm1.FormCreate(Sender: TObject);

begin

Lookup := True;

end;

Така ще се появят всички записи от полето LastName в падащия списък. Само дето малко се шашка компютъра ама то може би грешката е в техниката.

Разширено използване на контролата DBGrid - стр. 617

За да промениш рисуването на контролата трябва да зададеш стойност False на характеристиката DefaultDrawing и да обработиш събитието OnDrawColumnCell. По този начин може да се прибави повече от стандартното рисуване на таблицата. Друго решение е да се поеме цялото рисуване на графичната таблица. В този пример се използва таблицата BioLife.DB ot DBDEMOS.

Обработчикът на събитието OnDrawColumnCell се извиква по веднъж за всяка клетка от графичната таблица и има няколко параметъра, задаващи правоъгълника, съответстваща на клетката, номер на колоната, която се черта, самата колона (с полето, подравняването и други подхарактеристики) и състоянието на клетката. Ако трябва да се промени цвета на колоната трябва да се смени и цвета на осветената клетка. Тогава може да се изрисува текста от характеристиката DisplayText на полето:

Проверка дали клетката е избрана става с флага gdSelectede. Преди да се рисува текста трябва да се изтрие повърхността на клетката, чрез функцията DrawFocusRect

Изчертаването на memo полетата става с характеристиката AsString вместо да се използва характеристиката DisplayText. За графичните полета се използва създаването на временен обект от тип TBitMap. Свържи графичното поле с него и изрисувай изображението в таблицата:

Пример: D1210

procedure TForm1.FormCreate(Sender: TObject);

begin

  Caption := Table1.TableName;

  with DBGrid1 do Begin

    Align := alClient;

    DataSource := DataSource1;

    DefaultDrawing := False;

    Font.Height := -16;

    Font.Style := [fsBold];

    OnDrawColumnCell := OnDrawColumnCell;

  end;//with

end;

procedure TForm1.DBGrid1DrawColumnCell(Sender: TObject; const Rect: TRect;

  DataCol: Integer; Column: TColumn; State: TGridDrawState);

  VAR Code: Integer; Bmp: TBitmap;

begin

  If Column.Field = Table1Graphic Then Begin

  //рисуване на изображението

    Bmp := TBitmap.Create;

    try

      Bmp.Assign(Table1Graphic);

      DBGrid1.Canvas.StretchDraw(Rect, Bmp);

    finally

      Bmp.Free;

    end;

  end; //if

  //избиране на цвят

  if (Column.Field = Table1Lengthcm) and (Table1Lengthcm.AsInteger > 100) Then

    DBGrid1.Canvas.Font.Color := clRed

  else

    if gdSelected in State Then

    DBGrid1.Canvas.Font.Color := Column.Font.Color;

    // стандартен текст

    DBGrid1.Canvas.TextRect(Rect, Rect.Left, Rect.Top, Column.Field.AsString);

   if gdFocused in State Then

    DBGrid1.Canvas.DrawFocusRect(Rect);

end;

Поле за отметка в графична таблица - стр. 621

Пример: D1211

Сложи в компонентата TAble1 файла VENDORS от DBDEMOS. Изкарай една компонента DBCheckBox и скивай къде се появява и кога. Не слагай всички полета в DBGrid.

procedure TForm1.DBGrid1DrawColumnCell(Sender: TObject; const Rect: TRect;

  DataCol: Integer; Column: TColumn; State: TGridDrawState);

begin

  IF (gdFocused in State) and (Column.Field = Table1Preferred) Then Begin

    DBCheckBox1.SetBounds(Rect.Left + DBGrid1.Left + 1, Rect.Top + DBGrid1.Top + 1, Rect.Right - Rect.Left, Rect.Bottom - Rect.Top);

  end; //if

  {Самото поле се показва или скрива, когато потребителя влезе или излезе от съответната колона, чрез обработчикът на събитието OnColEnter (може и тука). Колоната не може да се адресира по нейната позиция защото те се преместват.}

  IF DBGrid1.Columns[DBGrid1.SelectedIndex].Field = Table1Preferred Then

    DBCheckBox1.Visible := True

  ELSE DBCheckBox1.Visible := False;

end;

procedure TForm1.DBGrid1KeyPress(Sender: TObject; var Key: Char);

begin

{Ако е натиснат Enter се сменя състоянието на DBCheckBox. Това ми беше дадено в KeyPress на DBChecBox ама там по се дъни а тука не променя съдържанието на таблицата}

  IF (DBCheckBox1.Visible = True) and (Ord (Key) = 13) THEN BEGIN

    DBCheckBox1.Checked := not DBCheckBox1.Checked;

    //Key := #0;

    //ShowMessage('mara');

  END; //IF

end;

Графична таблица позволяваща множества - стр. 623

Пример: D1212

Това става като се зададе елемента dgMultiSelect от характеристиката Options на графичната таблица. След това с натискането на клавиша Ctrl и натискане с мишката  се избират множество редове дори и ако не са последователни.

procedure TForm1.Button1Click(Sender: TObject);

  VAR Z: Integer; BookmarkList: TBookmarkList; Bookmark: TBookmark;

begin

  //съхраняване на текущата позиция

  try

  //изчистване на списъка

    ListBox1.Items.Clear;

    BookmarkList := DBGrid1.SelectedRows;

    Bookmark := Pointer (BookmarkList[0]);

    For Z := 0 to BookmarkList.Count -1 DO BEGIN

    //минаване през всички избрани редове

      Table1.GotoBookmark(Pointer (BookmarkList[Z]));

      //добавяне към списъка     ListBox1.Items.Add(Table1CustNo.AsString);

    END; //NEXT

  finally

  //Връщане към първоначалния запис

    Table1.GotoBookmark(Bookmark);

//    Table1.FreeBookmark(Bookmark); //Тука блокира

  end;

end;

Речникът на данните - стр. 624

Маса често се използват полета със сходно подреждане (например една и съща маска на показване) в едно или повече че приложения. Ако използваш цели числа, десетични числа, проценти, телефони и факс номера и други стандартни полета, е крайно досадно да задаваш вида на всяко от тях. Поради тази причина Delphi има речник на данните. Това е вид база данни, която съхранява характеристиките на тези стандартни полета, използвайки речника може лесно и просто да се копират от съществуващи полета. При клиент/сървър програмирането (когато работят няколко програми), речникът на данни може да се намира на сървър, за да може да обменя допълнителна информация.

Речникът на данните и Fields Editor

Повечето операции, използващи речника на данните, се извършват във Fields Editor на компонентите Table и Query. Контекстното меню на редактора предлага пет команди, свързани с речника на данните.

Команда

Значение

Assoiate Attributes

Тази команда се използва за асоцииране на множество атрибут с дадено поле. На практика може да се избере някое от множествата атрибути от речника, което ще се използва с текущото поле. Когато асоциираш поле с множество атрибути от речника да данните, атрибутите се копират в характеристиките на полето.

Unassociate Attributes

Тази команда е обратна на командата Associate Attributes. Тя прекратява асоциацията между полето и множеството атрибути.

Retrieve Attributes

Тази команда се използва за получаване на текущите стойности от свързаното множество атрибути. Тя може да бъде използвана само ако полето е асоциирано с множество атрибути. Вие може да мислите за нея като команда за зареждане.

Save Attributes

Обратна на горната. Копира стойностите на характеристиките на текущото поле васоциираното множество атрибут. Ако няма асоциирано множество атрибути, ще бъдеш попитан за ново име както е при долната команда.

Save Attributes As

Същото като горното само че трябва да се зададе име в появилия се прозорец.

Това също работи и върху полета, които са получени помощта от заявка, но само ако създадеш необходимите обекти от тип TField.

Какво представлява множеството - стр. 626

Елемент (запис) от речника с данни. Едно множество атрибути задава няколко характеристики на обект от тип TField, но също така включва и някой по-общи характеристики. някой от характеристиките на различните наследници на класа TField:

Aalignment

Display

EditMask

ReadOnly

BlobType

DisplayValues

MaxValue

Required

Currency

DisplayWidth

MinValue

Transliterate

DisplayFormat

EditFormat

Precision

Visible

Може да се зададе характеристика като Precision (стойност използвана само за числа с плаваща запетая) и след това да асоциираш множеството атрибути с поле от тип TStringField. Тогава тази стойност ще се игнорира.

Няколко стойности от множества атрибути дефинират общото поведение на полето и се използват, за да се определи как да се създаде нов обект по зададено поле от таблица или заявка:

Атрибут

Употреба

TField Class

Показва типа на полето (наследникът на TField), което да се създаде, когато се добави поле към множеството от данни.

TControl Class

Определя типа на data-aware компонентата, когато завлечеш компонентата извън Fields Editor. Ако нямаш зададена стойност, Delphi ще използва стандартния си подход, който зависи от типа на обекта.

Based On

Това е стойност, която трябва да зададеш при записването на множеството атрибути под ново име. Тя показва множеството атрибути, на което е базирано текущото множество. Това означава, че ако направиш промени в оригиналното множество и тези промени не се препокриват от атрибутите в текущото множество, промяната ще се отрази и в текущото множество. Една аналогия, са стиловете в текстообработващите програми, които могат да се базират с други сходни стилове.

Разглеждане на речника - стр. 627

Ново множество от атрибути се дефинира като запишеш текущите атрибути от полето Fields Editor. Може да дефинираш и собствени множества или да разглеждаш съществуващите, като използваш DataBase Exporer (е да ама на картинката 12.18 пише SQL Explorer). Стартирай програмата, избери страницата Dictiornary и ще видиш подразбиращия се речник на данните (DefaultDD), базиран на таблицата BDESSD от тип Paradox. Ако добавиш нови речници на данни ще видиш и тях. Под елемента Dictionary ще намериш две поддървета Databases и  Attribute Sets.

Под елемента Attribute Sets ще намериш списък от всички множества атрибути, заедно със стойностите на техните характеристики. Има и списък от таблици, използващи всяко от множествата и на другите множества, базирани на множества. Можеш да използваш Database Explorer за създаване на нови множества, атрибути или да ги променяш.

Асоциациите между полетата от таблици и множество атрибути могат да бъдат видени и променяни чрез разглеждане на другото поддърво - Databases. След като избереш поле от таблица, един комбиниран списък ще ти позволи да го асоциираш с някое от наличните множества от атрибути в речника на данните.

Когато започваш нов проект и си планирал неговите таблици, не е лошо да зададеш и няколко множества от атрибути в асоциации, преди да започнете работата си с Delphi. Ако твоя проект не е добре планиран, може да използваш Fields Editor за създаван на речник на данните заедно с останалите таблици и след това да използваш Database Explorer за да получиш представа за текущата ситуация и да я документираш.

Обработка на грешките при базите данни - стр. 628

Има три основни похвата

Може да включиш в try-catch блокове проблемните операции с базата данни, както например извикването на метода Open на заявката или метода Post на множеството от данни. Това не е възможно когато грешката е възникнала при комуникацията между data-aware контролите в базата данни.

Може да обработваш и конкретни събития на множествата от данни, свързани с грешките, като например: OnPostError, OnEditError, OnDeleteError и OnUpdateError.

При базите данни може да се покаже списък от грешки, на локалните кодове на грешките на BDE, както и специфичните за SQL сървъра, с който се свързваш. Освен характеристиката Message, класът EDBEngineError има още две характеристики: ErrorCount и Errors. Последната характеристика представлява списък от грешките:

property Errors [Index: Integer]: TDBError;

Всеки елемент от този списък е обект от клас TDBError, който има следните характеристики:

type TDBError = class

  public

property Category: Byte read Get Category;

property ErrorCode: DBIResult read FErrorCode;

property SubCode: Byte read GetSubCode;

property Message: string red GetSubCode;

property NativeError: LongInt read FNativeError;

end;

Следващия пример показва подробности за грешките в компонентата Memo. За целта се задава обртаботчик на събитието OnException на глобалния обект Application. Тази операция се извършва в обработчика на събитието OnCreate на формата:

Пример: D1213

procedure TForm1.FormCreate(Sender: TObject);

begin

  Application.OnException := MyError;

end;

{Методът MyError извиква специален метод, използван за показването на подробност за грешки, в случая от тип EDBEngineError:}

procedure TForm1.MyError(Sender: TObject; E: Exception);

begin

  Beep;

  IF E is EDBEngineError Then ShowError(EDBEngineError(E))

  else ShowMessage(E.Message);

end;

//Извеждане на съобщения за грешки в Мемо полето

procedure TForm1.ShowError(E: EDBEngineError);

  VAR Z: Integer;

begin

  Memo1.Lines.Add('');

  Memo1.Lines.Add('Eror: ' + (E.Message));

  Memo1.Lines.Add('Number of Erors: ' + IntToStr (E.ErrorCount));

  //Цикъл за обработка на всички грешки

  For Z := 0 to E.ErrorCount -1 Do Begin

    with Memo1.Lines do Begin

      Add('Category: ' + IntToStr (E.Errors[Z].Category));

      Add('Error Code: ' + IntToStr (E.Errors[Z].ErrorCode));

      Add('Sub Code: ' + IntToStr (E.Errors[Z].SubCode));

      Add('Native Error: ' + IntToStr (E.Errors[Z].NativeError));

      Add('End Message ----------------' + #13 + #10);

    end; //with

  end; //Next Z

end;

{Събития свързани с получаване съобщенията за грешки. Трите събития OnPostError, OnDeleteError и OnEditError имат една и съща структура}

procedure TForm1.Table1PostError(DataSet: TDataSet; E: EDatabaseError;

  var Action: TDataAction);

begin

  Memo1.Lines.Add('Post Error -> ' + E.Message);

end;

{Ако не се зададе действието, както в горния код, се използва подразбираща се стойност daFail и изключението достига до глобалния обработчик. Използването на стойност daAbort спира изпълнението и може да се използва, ако твоя обработчик на грешките показва съобщенията. Ако можеш да определиш грешката и да я оправиш, задай стойност daRetry, за повторение на операцията.}

{Предизвикване на грешки}

procedure TForm1.Button1Click(Sender: TObject);

begin

  //Таблицата не е в режим на редктиране

  Table1.FieldByName('Name').Value := 'Mara ba';

end;

 

procedure TForm1.Button2Click(Sender: TObject);

  VAR S: String;

begin

  //Генериране на грешка key violation

  S := Table1.FieldByName('Name').Value;

  Table1.Insert;

  Table1.FieldByName('Name').Value := S;

  Table1.Post;

end;

 

procedure TForm1.Button3Click(Sender: TObject);

begin

{Неправилни SQL заявки}

  Query1.SQL.Clear;

  Query1.SQL.Add('SELECT * FROM KOKO');

  Query1.Open;

end;

procedure TForm1.Button4Click(Sender: TObject);

begin

  Query1.SQL.Clear;

  Query1.SQL.Add('SELECT * FROM country.db WHERE POPULATION > 10000');

  Query1.Open;

end;

Многопотребителски приложения използващи бази данни на Paradox - стр. 633

За достъп до десетина потребители до един файл могат да се използват локални файлове на Paradox или Acces, които са поделени по мрежата.

Ниско ниво на BDE

BDE приема заявките от програмите и използвайки драйвер, ги транслира в команди, разпознавани от базата данни. Всяко приложение, използващо BDE, се разглежда като клиент, имащ отделна връзка към BDE която се нарича сесия и глобалната компонента Session във VCL осигурява такава връзка. Session извиква функцията DbiInit за да създаде връзката.

Едно приложение може да създаде множество независими връзки, като използва множество компоненти тип TSession. Това ще се разгледа в галава 20.

При извикване на ниско ниво на BDE са задължителни някои параметри под формата на манипулатори:

HDBIDB - манипулатор на текущата база данни, с която работи приложението. Тази стойност се получава от полето DBHandle на компонентата DataSet или характеристиката Handle на компонента от тип TDataBase.

HDBICur - манипулатор на резултата, изгледа на данните в таблицата или заявката. Може да превърташ данни в този изглед, тъй като достъпа се осъществява запис по запис. Може да получиш стойността на този манипулатор, като използваш характеристиката Handle на компонентите Table и Query.

При всяко извикване на функция на BDE, тя ще ти върне код за грешка. Проверяването на този код (и генерирането на изключение) става с функцията Check на VCL (дефинирана в модула DBTables).

Пакетиране на локална таблица - стр. 635

Пример: D1213

D1210.PAS

Операция за физическо премахване на изтрити записи. Компонентата TTable не предлага тази възможност. За пакатерине на DBase таблица се използва функцията DbiPackTable:

uses BDE;

procedure TForm1.PackDBaseTable (Table: TTable);

  VAR WasActive: Boolean;

Begin

WasActive := Table.Active;

Screen.Cursor := crHourGlass;

try

//Затваряне на таблицата ако е отворена

if WasActive Then Table.Close;

//отваряне на таблицата в специален режим

Table.Exclusive := True;

Table.Open;

//Пакетиране на таблицата

Check (DbiPackTable (Table.DBHandle, Table.Handle, nil, nil, True));

//Премахване на специалния режим

Table.Close;

Table.Exclusive := False;

  finally

Screen.Cursor := crDefault;

//евентуално повторни отваряне на таблицата

if WasActive then Table.Open;

  end;

end;

{Преди да извикаш таблицата в специален режим трябва да я затвориш. Като алтернатива можеш да предадеш манипулатор на курсор като втори параметър, освен това може да му зададеш стойност nil и да предадеш като трети параметър името на таблицата и константата szDBase като четвърти параметър.]

{Файловете на Paradox нямат функция за пакетиране. Като алтернатива може да се прекструктуира таблицата, като накараш DBE да обнови данните, премахвайки записите маркирани за изтриване. Това става с функцията DbiRestructure която пък била маса сложна. Изиксквала един или повече дерскриптори на таблицаи, от тип CRTblDesc (предавани като трети параметър), и броя на дескрипторите (предаван като втори параметър)}

procedure TForm1.PackpaParadoxTable (Table: TTable);

VAR TableDesc: CRTblDesc;

WasActive: Boolean;

hDataBase: hDbiDB;

Begin

WasActive := Table.Active;

Screen.Cursor := crHourGlass;

try

//отваряне на таблицата ако не е отворена за да се получи валиден DBHandle

if not WasActive then Table.Open;

//Получаване на манипулатор на таблицата и нейното затваряне

hDataBase := Table.DBHandle;

Table.Close;

//запълване на дескриптора на таблицата

FillChar (TableDesc, SizeOF(CRTblDesc), 0);

with TableDesc do begin

StrPCopy (szTblName, Table.TableName);

StrPCopy (szTblType, szPARADOX);

bPack := True;

end;//with

//Преструктуриране на таблицат, като я пакетираме

if hDataBase <> nil then

Check (DbiDoRestructure (hDataBase, 1, @TableDesc, nil, nil, nil, False))

else

ShowMessage (‘DataBase hadnle is nil’);

//евентуално повторно отваряне на таблицата

  finally

Screen.Cursor := crDefault;

if WasActive then Table.Open;

Table.Open;

  end;

End;

{Повечето от полетата на дескриптора на таблицата и повече от параметрите на функцията не се използва, истинското преструкториране изисква маса повече код. Единственото нещов горния метод е задаването на стойност True на параметъра bPack на дескриптора на таблицата, за да се форсира пакетриащата операция при преструкторирането}

{Показване списъка на таблиците с необходимите разширения:}

procedure TForm1.FormCreate(Sender: TObject);

begin

//получаване имената на таблиците

Session.GetTableNames (Table1.DatabaseName, ‘*.db’, True, False, ListPdx.Items);

Session.GetTableNames (Table1.DatabaseName, ‘*.dbf’, True, False, ListDBaase.Items);

ListPdx.ItemIndex := 0;

ListDBaase.ItemIndex := 0;

end;

procedure TForm1.Button1Click(Sender: TObject);

begin

  PackDBaseTable (Table1);

end;

Иползване на бази данни на Paradox по мрежа - стр. 638

Програмата може да се намира на мрежовото устройство но да се изпълнява на локалната машина. На всеки компютър трябва да се инсталира BDE  и правилните псевдоними на базата данни. Може да се генерира проста програма за като използва копето на InstallShield Express, което се разпространява с пакетите на Delphi. При всички случаи може спокой да разпространяваш файлове на BDE, коти са пакетриани във формата на CAB файл за целите на програмирането за Интернет.

Има няколко настоики на BDE, които трябва да провериш, преди да стартираш множество клиентски иприложения, свързани с база данни на Paradox по мрежа:

Задай стойност True на Local Share. Това се прави в страницата System на BDE Configuration Utility. Тази операция е необходима само ако един и същ файл се изпозлва от приложения, които не използват BDE и ако не изпозлваш файл съврвър на Novell.

Използвай мрежовтаа директория за съхраняване на данните от базата. Това е стойността на Alias в BDE Configuration Utility или временен псевдоним в компонентата Database. Важното е мрежовото устройств, което използваш, предварително да е свързано с една и съща буква на устройството на всички клиентски машини; в противен случай BDE няма да може да управлява правилно тяблиците, които се поделят между клиентите. В действителност, те се обръщат към таблиците, използвайки име на устройство.

Използвай и мрежовата директория за контролния файл, Paradox.Net. Тази директория е стойност на параметърa NetDir, която може да се зададе чрез BDE Configuration Utility или е стойност на характеристиката NetDileDir на компонентата Session. Този контролен файл съдържа информация за заключването, което предпазвса от едновременно редактиране на един запис от повече от един потребители.

Контрол над конкурентността - стр. 639

В тази област не можеш да направиш нищо. Когато има множество потребители, те евентуално ще се опитат да обновят една и съща информация по едоно и също време, като се предизвика конфликт при обновяването. Различните сървъри за бази данни се справят по различен начин. Има два основни подхода: Paradox и повечето SQL сървъри.

При Paradox когато се постави запис в режим на редактиране той се заключва. Останалите потребители могат само да го четат.

Повечето SQL сървири използват оптимистичен подход. Te позволяват на множество потребители да рекатират един и същи запис и позволяват на приложенията да им изпращат и оригиналните, и променените данни, така че потребителите да проверят дали някой друг вече е променил записа.

Когато се използва Paradox в мрежова среда, заклчващата информация за всеки потребител се записва в общия файл Paradox.NET. Дори и да няма мрежа може да се получи този ефект.

Състоянието на записа може да се контролира с извикване на try-catch блок. По този начин може да се извежда предупредаващи съобщения и повтаряне на операцията. Проблема в случая е, че имаш малък контрол над продължителността на операциите по редактиране. Това се коригира с:

- Ипозлване на non-data-aware контроли за управляване чрез програмен код операцията по обновяване на данните;

- Задаване на времеви интерал за продължителност на операциите по редактиране (използвайки контролата Timer). Ако направиш това трябва да записваш временните промени влокален буфер или нещо от сорта.

В някой случай може да поискаш информация за състоянието на записа. Това става с функцията DbiIsRecordLocked. При множество потребители обаче това не е от полза защото проверката се прави само в текущата сесия. Няма BDE функция която да проверява дали записът е бил заключен от друг потребител.

Може да симулираш операция, която се извършва в режими на редактиране. В този случай VCL задава заключвване на записа. При извършване на операцията върху текущата таблица, тя може да промени неговото състояние. Пради тези прищчина е по-добре да създадеш копие на таблицата и след това да приложиш операцията върху нея:

Пример: D1211.PAS

function TForm1.IsRecordLocked (Table: TTable): Boolean;

VAR Locked: BOOL; hCur: hDBICur; rslt: DBIResult;

Begin

Table.UpdateCursorPos;

//проверка дали записът е заключен

Check (DbiIsRecordLocked (Table.Handle, Locked));

Result := Locked;

// в противен случай  се проверяват всички сесии

if (Result = False) then Begin

Check (DbiCloneCursor (Table.Handle, False, false, hCur));

try

//опит за заклюючване на записа

rslt := DbiGetRecord (hCur, dbiWRITELOCK, nil, nil);

//не извиквай Check в случая се предприема специално действие вместо да се вика изключение

if rslt <> DBIERR_NONE then Begin

//Ако е възникнала грешка при заключването

if HiByte (rslt) = ERRCAT_LOCKCONFLICT Then

Result := True

else

//друга грешка

Check (rslt); //генерира изключение

end //if

else

//Ако функцията е завършила успешно премахни заключването

Check (DbiRelRecordLock (hCur, False));

finally

Check (DbiCloseCursor (hCur));

end;

  end;//if

End;

Procedure TForm1.TestLockStatus;

Begin

//Ако таблицата не е в режим за редактиране

if Table1.State in [dsEdit, dsInsert] then

Caption := ‘LockTest - Record in edit mode’

  else if IsRecordLocked (Table1) Then begin

DBEdit1.ReadOnly := True;

DBEdit2.ReadOnly := True;

DBEdit3.ReadOnly := true;

Caption := ‘LockTest - Record already locked’;

end

else begin

DBEdit1.ReadOnly := False;

DBEdit2.ReadOnly := False;

DBEdit3.ReadOnly := False;

Caption := ‘LOckTest - Record not Locked’;

  end;

end;

{Този метод се извиква от компонентата Timer в източника на данни}

procedure TForm1.DataSource1DataChange(Sender: TObject; Field: TField);

begin

//Ако записът е променен

if (Field = nil) then

TestLockStatus;

end;

 

procedure TForm1.Timer1Timer(Sender: TObject);

begin

  TestLockStatus;

end;

Транзакция на бази данни - стр. 645

Идеологията на транзакциите може да бъде описана като сериоя от операции, които се приемат за едно “атомично” цяло. Под атомично се разбира нещо неделимо.

Представи си, че увеличавашщ заплатите на всички работници с определена сума. Сега по време на операцията ако възникне грешка, може да искаш да възстановиш предишното състояние на нещата. Ако приемеш операцита “повишаване заплатите на всички работници” като една транзакция, трябябва да бъде напъоно завършена или изцяло пренебрегната.

По подразбиране всяка заявка за редактиране или потвърждение се смята за транзакция, но това може да се промени чрез явно указване на транзакацията. Добави компонетеата Database към формата или модула за данни, свържи всички таблици или заявяки и използвай следните методи на класа TDatabase:

- StartTransaction - маркира началото на транзакция;

- Commit - потвърждава всички промени, направени до този момент;

- RolBack - отказва всички промени, направени до този моемнт.

Пример D1212.PAS

procedure TForm1.FormCreate(Sender: TObject);

begin

  with Database1 do begin

AliasName := ‘DBDEMOS’;

Connected := True;

DatabaseName := ‘MyData’;

SessionName := ‘Default’;

TransIsolation := tiDirtyRead;

end;

with Query1 do begin

BeforeEdit := Query1BeforeEdit;

DatabaseName := ‘MyData’;

RequestLive := True;

SQL.Add (‘select * from Employee’);

  end;

end;

{При натискане на първия бутон започва транзакция и разрешаване на другите два бутона}

procedure TForm1.BtnStartClick(Sender: TObject);

begin

Database1.StartTransaction;

//задаване на състоянеито на бутоните

BtnStart.Enabled := False;

BtnCommit.Enabled := True;

BtnRollBack.Enabled := True;

end;

 

procedure TForm1.BtnCommitClick(Sender: TObject);

begin

Query1.Post;

Database1.Commit;

//задаване състоянието на бутоните

end;

 

procedure TForm1.BtnRollBackClick(Sender: TObject);

begin

Query1.Cancel;

Database1.Rollback;

Query1.Refresh;

//задаване състоянието на бутоните

end;

{не е обходимода се натиска бутона Start защото кода му се изпълява всеки път при започване на промяна}

procedure TForm1.Query1BeforeEdit(DataSet: TDataSet);

begin

//стартиране на танзакция ако не е стартирана

if not Database1.InTransaction then BtnStartClick (self);

end;

Използване на кешираните обновявания като транзакции - стр. 647

В момента на промените те се запазват в паметта и се изпращат само веднъж. Това се прави, когато прилагаш промените с извикването на метода ApplyUpdates на множеството от данни, или същият метод на базата данни. Не забавай да приложиш промените, когато използваш кеширани обновявания, в противен случай те ще се изгубят!

procedure TForm1.FormCreate(Sender: TObject);

begin

  with Query1 do begin

CachedUpdates := True;

DatabaseName := ‘DBDEMOS’;

RequestLive := True;

SQL.Add (‘select * from Employee’);

  end;

end;

procedure TForm1.BtnApplyClick(Sender: TObject);

begin

  try

//потвърждаване на промените

Query1.ApplyUpdates;

Query1.CommitUpdates;

//установяване на бутоните

BtnApply.Enabled := False;

BtnCancel.Enabled := False;

  except

//код обработващ изключението+

  end;

end;

procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);

begin

  //ако има необработени промени се запитва какво да се прави

  if Query1.UpdatesPending and (MessageDlg (‘Apply the pending updates?’, mtConfirmation, [mbYes, mbNo], 0) = mrYes ) then

Query1.ApplyUpdates;

end;

{При редакция на запис, BDE го заключва но след изпращането на заявката от локалния кеш го отключва. Поради това двама потребители могат да обновят в кеша си един запис но при изпращането ще се примат промените на първия, а втория ще получи съобщение за грешка събитието OnUpdateError като му се придават няколко параметър.}

procedure TForm1.Query1UpdateError(DataSet: TDataSet; E: EDatabaseError;

  UpdateKind: TUpdateKind; var UpdateAction: TUpdateAction);

{Първия параметър е множество от данни, втория е съобщението за грешка, третия е описание на обновяването (ukMOdify, ukInsert и ukDelete) и последния действието което трябва да се направи(uaFail, uaAbort, uaSkip, uaRetry или uaApplied).

За определяне причинителя на грешката се изполват характеристиките Old/Value и NewValue както и функцията UpdateStatus за да разберем каква операция се е извършила. За поправяне на грешката може да се зададе правилна стойност на характеристиката NewValue и да изпълниш програмата. Това може да доведе до безкраен цикъл.}

VAR strDescr: String; I, nRow: Integer;

begin

nRow := 0;

try

//Създаване на диалогов прозорец

//  ErrorForm := TErrors.Create (nil);

//  ErrorsForm .Caption := ‘Record: ‘ + DateSet.FieldByName (‘Lastname’).AsString;

 

  for I := o to DataSet.FieldCount - 1 do

if DataSet.Fields [I].OldValue <> DataSet.Fields [I].NewValue then begin

//добавяне на ред в графичната таблица

Inc (nRow);

//  ErrorsForm.StringGrid1.RowCount := nRow + 1;

//копира данните на новия ред

with ErrorsForm.StringGrid1.DateSet.Fields [I] do begin

Cels [0, nRow] := FieldName;

Cels [1, nRow] := string (OldValue);

Cels [2, nRow] := string (NewValue);

end;

end; //if

  //Ако новите елементи са добавени показва диалоговия прозорец

  if (nRow > 0) and (ErrorsForm.ShowModal = mrOk) then Begin

//връщане на записа и скриване на съобщението

(DateSet as TQuery1).RevertRecord;

UpdateACtion := uaAbort

end

else

//Пропускане на записа

UpdateAction :uaSkip;

  finally

ErrorsForm.Free;

  end;

end;

procedure TForm1.Query1AfterScroll(DataSet: TDataSet);

begin

  case Query1.UpdateStatus of

usUnmodified: StatusBar1.SimpleText := ‘Non Modified’;

usModified: StatusBar1.SimpleText := ‘Modified’;

usInserted: StatusBar1.SimpleText := ‘Inserted’;

  end;

end;

{При възникване на грешка се показва сложна форма която поставя в графичната таблица всички полета на текущия запис в друга форма.

Тази форма се създава по време на изпълнение на програмата в метода FormCreate. Истинската иницализация на формата се извършва при възникване на грешка.}

Възстановяване след грешки - стр. 651

Първия начин е описан в документите “Technical Information” на http://inprise.com. Има и няколко други специфични препоръки, които може да поселдваш, за да направиш вашите приложения по надежни:

Функцията DBISaveChanges записва всички буфери за работа с базата данни на диска. Обикновено се извиква в обработчикът на събитието AfterPost на таблицата или в обработчика на събитието OnIdle на глобалния обект Application.

Когато има грешка в индекса на база данни на Pradox може да изтриеш този индекс и след това да създадеш нов.

Може да забранш кеширането на диска, който съдържа файловете на базта данни, така че данните веднага ще се зписват на диска.

Може да използваш специално средтава за поправяне на таблици, налично по д формта на динамична библиотека, TUtility.DLL за 16 битовите версии и TUtil32.DLL за 32 битовите версии. Маса компоненти използват тези две библиотеки.

Глава 13

Клиент сървър - стр. 656

Това е система при която заявките се изпълняват на сървъра а клиента получава само резултата.

Количество данни: Таблиците на Paradox не могат да надвишават 2 GB но и при 300 MB се получава сериозно забавяне ако трябва да се прехвърлят всичките данни

Необходимостта от конкурентен достъп до данни: Paradox използва файла Paradox.NET за да следи достъпа на всеки потребител до данните. Подходът на Paradox за обработка на множество потребители е базиран на песимистичното заключване. Когато потребителя започне операция за редактиране на даден запис, никой от останалите потребители не може да прави същото В система с десетки потребители това може да доведе до сериозни проблеми, защото един потребител може да блокира работата на всички останали. Базите данни на SQL сървър използват оптимистично заключване - подход, който позволява множество потребители да работят върху едни и същи данни, като отлага контрола на конкурентността за времето, когато потретилите потвърдят промените си.

Защита за сигурност: SQL сървърът обикновен има много повече защитни механизми, отколкото паролата,която може да добавиш към таблица на Paradox. Когато едно приложение се базира на локални файлове, злонамерените или невнимателните потребители могат просто да изтрия всички тези важни файлове. SQL сървърите, базирани операционни системи, предлагат множество нива на защита, улесняват архивирането и възстановяването на данни и често предоставят възможност само на администратора за модифициране структурата на таблиците.

Програмируемост: Една база SQL сървър може да съдържа правила под формата на процедури на сървъра, тригери, изгледи на таблици и други техники. Изборът на разделянето на кода между сървъра и клиента е една от главните предимства на клиент/сървър програмирането.

Компонентата Database - стр. 659

В локалните приложения обръщението към база данни може да става с псевдоним или пътя не нейните файлове в характеристиката Database на компонентите Table и Query. Една алтернатива е да се използва компонентата Database, за да се дефинира локален псевдоним и след това всички компоненти DataSet да използват този псевдоним.

Пример D1301.PAS

procedure TForm1.FormCreate(Sender: TObject);

begin

  with Database1 do begin

//AliasName := ‘IbLocal’; // така е в книгата

//AliasName := ‘DBDEMOS’;

//Connected := True;

DatabaseName := ‘Ib’;

LoginPrompt := False;

//Params.Strings //!така е в книгата

//Дъни се

//Params.ADD (‘USER NAME = SYSDBA’);

//Params.Add (‘PASSWORD = masterkey’);

//SessionName := ‘Default’;

end;

with Table1 do begin

DatabaseName := ‘Ib’;

TableName := ‘EMPLOYEE’;

end;

with Query1 do begin

DatabaseName := ‘Ib’;

SQL.Add (‘select Max (Salary) from Emploee’);

  end;

end;

Компонентата Database е необходима за дефиниране на връзката и параметрите за идентификация (име и парола в характеристиката Params) и за управление на транзакциите.

Компонентата Database установява връзка с SQL сървъра, представлявайки един от клиентите в системата. Ако едно приложение или компютър използва множество връзки към сървъра, то той може да ги разглежда като различни клиенти! С характеристиката KeepConnection се задава дали връзката да бъде активна дори и да няма DataSet компонента.

Ролята на BDE - стр. 660

Клиентската програма все още трябва да работи с локалното копие на BDE, което е инсталирано на компютъра. BDE не знае как да комуникира с SQL сървър, а използва драйвери, наречени SQL Liniks, за за извършат тази операция. Като алтернатива, BDE може да комуникира с ODBC драйвери. Inprise представляват специални драйвери на BDE за InterBae, Oracle, Informix, MS SQL, Sybase и DB2.

Когато BDE е поставен в режим на пропускане на командите директно ги предава към сървъра. Така се увеличава скоростта на приложението. Този режим се активира с BDE Administrator.

BDE може да помогне за създаване на приложения предназначени за работа с множество сървъри. На практика това е трудно поради различието на диалектите използвани от различните SQL сървъри. Ако една таблица е поставена на два сървъра, които имат различни типове данни, Delphi ще се нуждае от различни комплекти TField обекти.

BDE приема файловете като записи, а не като множества както е при SQL сървърите.

Алтернатива на използването на BDE е директното използване на специфичните за сървъра API функции, какъвто например предлага InterBase. Това що донесе до по-голяма производителност на програмата но ще се изгуби преносимостта. Директното използване на API функциите било сложно но имали повече VCL компоненти, които имали директен достъп до сървъра.

Примери за такива компоненти за InterBase са IBObject (http://Ibobject.com) и FreeIBCombonents нa ()http://interbase.com/download), за  Direct Oracle Access на http://allroundautomations.nl, а за Microsoft SQL Server може да се използва TSQLQuery (http://component-store.com).

Еднопосочни курсори - стр. 662

При локалните бази данни таблиците са последователни файлове, в които наредбата на данните е или физическа, или зададена чрез индекс. SQL сървърите работят с множества данни, които не са свързани с физически ред. Сървърът за релационни бази данни управляват данните в термините на релационния модел -математически модел, базиран на теорията на множествата.

Това, което е важно за разглеждането е, че записите в таблицата се идентифицират не по позицията си, а чрез първичен ключ, базиран на едно или повече полета. След като имате множество от записи, сървърът добавя към всеки от тях указател към следващия, което ускорява преминаването на един запис към следващия, но е ужасно бавно  да се върнете към предишния запис. Поради тази причина, често се казва, че SQL сървърите използват едно посочен курсор.

Свързването на таблица или заявка с контролната DBGrid ще бъде много бавно, когато графичната таблица се разглежда в обратен ред. Всъщност, BDE помага много в случая, като запазва в локални кеш записите, които вече са заредени. таке че, когато се преместваме към следващите записи, те се вземат от SQL сървъра, а когато се връщаме обратно, BDE пренебрегва сървъра и предоставя данните.

Ако връщането назад може да доведе до проблеми, имай в предвид, че преминаването към последния запис на таблицата може да е дори по-лошо: обикновено при тази операция се предизвиква прехвърляне на всички записи. Подобна ситуация е и с характеристиката RecorCount на множествата данни. Изчисляването на броя на записите често довежда до прехвърлянето на всички данни към клиентския компютър. Това е причината, поради която плъзгачите на вертикалните позиционни линии на компонентата DBGrid работят за локални, но не и за отдалечени таблици. Ако искаш да разбереш броя на записите, стартирай отделна заявка и писка от сървъра (а не от клиента) да извърши изчислението. Ето и пример за броене на полета в таблицата Employye където заплатата е по-голяма от 50 000:

SELECT COUNT(*) FROM EMPLOYEE WHERE SALARY > 50000

Компонентите Table и Query в клиент/сървър програмирането - стр. 663

При използване на клиент/сървър приложение се използва компонентата Query щото компонентата Table имала недостатъци. Ето и списък на сравненията между двете компоненти:

Table компонентата трябвало да се използва за таблици с малък размер. Всички данни се прехвърлят на локални компютър;

При използване на Table компонентата първо се изисква от сървъра информация за структурата на таблицата а след това данните. Ако се активира опцията schema Cashing на BDE, логическата структура на таблицата ще се пази на локалния компютър. Това може да доведе до кахъри ако логическата структура на таблицата се промени на сървъра.

При Query кеширането на данните се задава с характеристиката Unidirectional.

Редактирането на резултата на проста заявка става със задаване стойност True на характеристиката RequestLive. За по-сложни заявки се използва компонентата UpdateSQL.

Table прехвърля целия запис към клиента дори и при използване на Fields Editor. Същото става и ако се зададе стойност True на характеристиката RequestLive на заявката.

Query може да се използва за вмъкване и изтриване на записи. Когато Query връща множество от данни, се активира чрез метода Open (или чрез задаване стойност True на характеристиката Active). Когато Query се използва за извършване на операцията на сървъра, трябва да се активира чрез извикване на метода ExecSQL.

Заявки с параметри и стойността Null - стр. 66

А бре били маса яки. Позволявали стартирането на маса заявки с различни множества от резултати, докато сървъра трябва само да си изработи стратегия за достъп, за да изпълни заявката само веднъж.

Тава можело да се извика чрез стратегията за достъп, като извикаш метода Prepare на компонентата Query. Чрез тази операция сървърът получава заявка, проверява нейния синтаксис и докато я компилира, определя как да се използва индексите си и другите техники за достъп. Ако след това заявката се изпълни няколко пъти, тя ще бъде по-бърза. Накрая се викало метода Unprepare, за освобождаване на някой от ресурсите на BDE.

Ако използваш характеристиката Filter на компонентата Table можеш да зададеш условие за работа с определени записи от таблица.

Local InterBase сървър - стр. 665

Това е потребителска верския на SQL съръвра на Inprise, включена в Professional и Client/Server в Delphi. Полезна е за тестване клиент/сървър приложения. След инсталирането на Local InterBase можеш да го активираш от Start менюто на Windows. (IBServer.EXE). Когато е стартиран се вижда иконата му до часовника на Windows.

Има две главни средства за директна комуникация. Едното е Server Manager, което се използва за администриране като на локален, така и на отдалечен сървър. Другото е Windows Interactive SQL (WISQL).

SELECT FIRSTNAME, LASTNAME FROM EMPLOYEE WHERE SALARY > 30000

Малко SQL команди

ISQL

Interactive SQL, версията за командния ред на WISQL

GBAK

Извършва операциите по архивиране и възстановяване на базата данни

GSPLIT

Може да раздели резултата от GBAk на множество файлове от диска, за да избегне евентуалното ограничение за размера на файловете, налагано от операционна система

GPIX

Позволява задаване на различни характеристики на  базата данни, като откриването на някой случаи и грешки в базата.

GSEEC

Конфигурация на потребителите

GSTAT

Позволява гледането на някой статистики от базата данни

IBLOCKP (или N GDS_KLOCK_PRINT

Показва заявките за заключване

 

SQL: език за описание - стр. 669

Създаване на файл

CREATE DATABASE “KOKO.GDB”;

Файла може да се създаде и със WISQL като се използва File > Create Database. Точката и запетаята в горния пример при WISQL се използва за край на команда. Обраната операция на горната е DROP DATABASE. Промяна на някой от параметрите се прави с ALTER DTABASE.

Типове данни - стр. 570

След създаване на базата данни може да се прибавят към нея таблици с CREATE TABLE. Трябва да се зададе типа на всяко поле:

char, character (n)

Символен низ с n символа (n = 32767 символа при InterBase

int, integer

Цяло число 4 байта

Smallint

Цяло число 2 байта

Float

Число с плаваща запетая

Double precision

Число с плаваща запетая и висока точност

Numberic

Число с плаваща запетая със зададена точност и размер

date

Дата, реализацията зависи от сървъра

blob

Голямо количество от двоични данни (Binary Large Object)

varchar

Символен низ променлива дължина

Тука липсват логически полета. Типа AutoInt се поддържа от Mircosof SQL Server но не и от InterBase.

Демени - стр. 671

Използват се за дефиниране един вид потребителски типове данни на сървъра. Базира се на съществуващ тип данни, който може да е ограничен и до негово подмножество. Може да предотврати проверката на един и същ интервал от множество полета и да направи дефиниция на преглежда.

CREATE DOMAIN BOOLEAN AS SMALLINT DEFAULT 0 CHACH (VALUE BETWEEN 0 AND 1);

Тази конструкция позволява да се зададе стойност по подразбиране и някой ограничения чрез същата нотация, използвана при създаването на таблица (и която ще видим в следващата част). Това е дефиниция на домен Boolean.

Използването и обновяването става с alter domain

Създаване на таблици - стр. 672

CREATE TABLE KOKO (

CUST_NO INTEGER NOT NULL PRIMARY KEY,

FIRST_NAME VARCHAR (30) NOT NULL,

LAST_NAME VARCHAR (30) NOT NULL,

ADDRESS VARCHAR (30),

PHONE_NUMBER VARCHAR (20) )

 - NOT NULL - показва, че това поле винаги трябва да има дефинирана стойност

 - DEFAULT - задава стойността по подразбиране за това поле, която може да бъде една от  следните видове: константна стойност, null или user.

- едно или повече ограничения, с задължителното име след ключовата дума constraints. Възможни ограничения са primaray key, unique (всеки запис трябва да има различни стойности), references (полето е указател към поле от друга таблица) и check (полето има специална проверка за валидност).

Ограниченията в таблицата могат да включват първичен ключ базиран на множество полета:

CREATE TABLE KOKO (

CUST_NO INTEGER NOT NULL

FIRST_NAME VARCHAR (30) NOT NULL

  .....

PRIMARY KEY (CUST_NO, NAME)

Таблица се премахва с DROP TABLE

CREATE TABLE KOKO ( CUST_NO INTEGER,

FIRST_NAME VARCHAR (30), PRIMARY KEY

(CUST_NO))

Индекси - стр. 673

CREATE INDEX CUST_NAME ON CUSTROEMR (NAME)

Премахването става с DROP INDEX, временно се деактивира с ALTER INDEX.

Има 4 основни команди: select, insert, update и delete. Всички те могат да бъдат изпълнени чрез компонентата Query, но само SELECT връща като резултат набор от данни. За останалите команди трябва да отвориш заявката с ExecSQL, вместо с Open (или характеристиката Activate).

Командата SELECT

SELECT <полета> FROM <таблица>

В частта <полета> може да се укаже повече колони на таблицата, разделени със запетая, символа * за всички колони или да се укаже операция, която да се приложи върху едно или повече полета:

SELECT UPPER (COUNTRY), ( COUNTRY ||”, “|| COMPANY ) AS FULLNAME FROM CUSTOMER

Символите || служат за свързване на символни низове, а думата AS задава ново име на предхождащия израз.

SELECT  * FROM CUSTOMER WHERE COUNTRY = “US” OR COUNTRY = “CANADA

Добавяне на клаузата WHERE позволява получаването само записи които отговарят на определени условия. Критериите за избор могат да включват и +, -, >, <, =, <>, >= и <=.

- IS NULL - проверява дали на полето е зададена стойност.

- IN <списък> - връща true, ако стойността е в списъка, зададен след оператора.

- BETWEEN <min> and <max> - показва дали стойността е в дадения интервал.

SELECT * FROM CUSTOMER WHERE ADDRES IS NOT NULL AND CUSTNO BETWEEN 100 AND 200

Ако например искаш да намериш всички имена започващи с В се прави:

SELECT * FROM EMPLOYEE WHERE LASTNAME LIKE “B&”

Използването на оператора UPPER позволява търсенето да е независимо от регистъра на буквите.

Информацията може да се сортира с:

SELECT * FROM EMPLOYYE ORDER BY LASTNAME

Указването вида на сортирането става с операторите asc и dsc

Командата DISTINCT премахва всички повтарящи се записи:

SELECT DISTINCT CITY FROM CUSTOMER

SELECT може да връща агрегатни стойности със стандартни функции:

- AVG - средна стойност на колона.

- COUNT - броя на елементите от резултата

- MAX и MIN - изчислява най-високата и най-ниската стойност в зададена колона

- SUM - изчислява сумата на всички елементи в указаната колона (работи само с числови полета)

SELECT AVG (CUSTNO) FROM CUSTOMER

Клаузата GROUP BY позволява групирането по някакви показатели, като се прилагат агрегатните функции, например може да видиш максималната и средната заплата на работниците по отдели:

SELECT MAX (SALARY), AVG (SALARY), DEPARTMENT FROM EMPLOYYE GROUP BY DEPARTMENT

Агрегатните стойности могат да се използват, за да се разграничат записите в крайния резултат. Не могат да използват клаузата WHERE, но е предвидена частта HAVING. Следващия пример връща максималната заплата във всеки отдел, но само ако е по-голяма от 40 000

SELECT MAX(SALARY) AS MAXSAL, DEPARTAMENT FROM EMPLOYEE GROUP BY DEPRATMENT HAVING MAX (SALARY) > 40000

Използване на вложени заявки:

SELECT FIRSTNAME, LATNAME FROM EMPLOYEE

  WHERE SALARY = (SELECT MAX(SALARY) FROM EMPLOYYE

Вътрешно и външно свързване на таблици - стр. 679

Вътрешно свързване на таблица може да се укаже с клаузата WHERE:

SELECT * FROM <таблица1>, <таблица2>   WHERE <таблица1.ключово_поле> = <таблица2.ключово_поле>

Това свързване е полезно при таблици с връзка едно към едно (на всеки запис от едната таблица съответства един запис от другата таблица. Свързването може да стане и с:

SELECT * FROM <таблица1> LEFT JOIN <таблица2>

WHERE <таблица1.ключово_поле> = <таблица2.ключово_поле>

Външното свързване може да стане само с:

SELECT * FROM <таблица1> LEFT OUTER JOIN <таблица2>

  WHERE <таблица1.ключово_поле> = <таблица2.ключово_поле>

Основната разлика между двете свързвания е, че при външното свърьвне избраните редове няма да съдържат непопълнени полета от втората таблица. Има и други типове на свързвания, включващи: самостоятелно свързване, при което таблицата се слива със себе си; множествено свързване което позволява да се свържат повече от две таблици; и Декаратово произведение - присъединява, при което липсва клаузата WHERE, което свързва всеки ред н една таблица с всеки ред от втората, като обикновено резултат е доста голямо множество. Вътрешното свързване е най-често срещаното свързване.

Командата INSERT - стр. 680

INSERT INTO EMPLOYEE (EmpNo, LastName, FirstName, Salary) VALUES ( 0, “Kokov”, “Koko” , 40)

Може да се вмъкне в таблицата и резултата, върнат от заявката SELECT: (Ако таблиците имат една и съща структура).

INSERT INTO <таблица> <SELECT израз>

Командата UPDATE

UPDATE EMPLOYEE SET EmpNo = 1 WHERE EmpNo = 1

Командата DELETE

DELETE FROM EMPLOYEE WHERE EmpNo = 1

Използване на SQL Builder

Активирал се с десен бутон на мишката върху компонентата Query > Explore. Използва се за задаване на заявка или за получаване на графично представяне на вече съществуваща (Само че на мене ми излиза DBE Explorer).

Избира се базата, след това се избира една или повече таблици. След това избери Query > Run Query (или F9), за да скиваш резултата от заявката, или командата Query > Show SQL (F7), за да видиш кода на командата Select.

В избраните таблици можеш да маркираш полетата, които искаш да видиш в резултата.

При затваряне на SQL Builder, текстът на заявката също ще бъде записан в съответната компонента Query.

Процедури на сървъра - стр. 685

Трябва да се извикват от клиента. Следващата процедура е написана за Inter Base. Тя получава датата и изчислява най-високата заплата между служителите,постъпили на тези дати;

CREATE PROCEDURE KOKO (OFDAY DATE)

RETURNS ( MAXSAL DECIMAL (8, 2) )

AS

BEGIN

  SELECT MAX (SALARY) FROM EMPLOYEE WHERE HIREDATE = :OFDAY

INTO :MAXSAL;

END

Процедурите на сървъра се активират с компонентата Query: SELECT * FROM KOKO (“01/01/1990”) или StoredProc. Появявал се диалогов прозорец StoredProc1 само дето не пише как се вика ама както и да е.

Тригери и генератори - стр. 686

Приличат на събитията и се активират автоматично. Могат да имат собствен код или да извикват процедури на сървъра. Активират се при три основни команди за обновяване: INSERT, UPDADE и DELETE.

Почти всички SQL сървъри предлагат брояч които може да се използва за задаване на нов идентификатор, който трябва да се използва по-късно в таблицата. InterBase нарича тези броячи генератори, докато Oracle ги нарича последователности. Ето и пример за InterBase:

CREATE GENERATOR CUST_NO_GEN;

.....

GEN_ID (CUST_NO_GEN, 1)

Ето тригер към таблицата CUSTOMER.

CREATE TRIGER SET_CUST_NO FOR CUSTOMER

BEFORE INSERT POSITION 0 AS

BEGIN

NEW.CUST_NO = GEN_ID ( CUST_NO_GEN, 1)

END

Този тригер се активира при вмъкване на нов запис.

Кеширано обновяване и заявки, позволяващи обновяване - стр. 686

При използване на компонентата Query данните не могат да се редактират освен ако не се зададе True на характеристиката RequestLive. Ако работиш с локални таблици, заявките винаги ще се изпълняват от BDE. Всички свързвания на таблици трябва да са външни, не може да се използва ключът distinct; не може да има сливане; не може да има клаузи group и having, освен ако се поддържа индекс. При използване на локални файлове по-добре е да се използва компонентата Table.

Ако BDE установи, че данните не могат да бъдат обновявани и задава стойност False на характеристиката CanModify. Компонентата DataSource проверява тази стойност преди да позволи редакция. Този кахър се премахва ако не използващ data aware компонентите.

Компонентата UpdateSQL - стр. 689

Предоставя заявка с нужните команди за обновяване. Нейни ключови характеристики са: DeleteSQL, InsertSQL и ModifySQL. Съществен елемент е характеристиката UpdateObject на свързаната с нея компонента Query. SQL командите за обновяване се изпълняват като се потвърдят кешираните промени. След написването на заявка може да свържеш с нея компонентата UpdateSQL и да активираш редактора на компонентата за автоматично генериране на SQL команди. Този редактор се извиква с 2 пъти клик върху компонентата.

Примерът UpdadeSQL - стр. 590 обаче липсва

object EmpUpdate: TUpdateSQL

  ModifySQL.String = (

‘update EMPLOYEE’

‘set’

  FIRST_NAME = :FIRST_NAME,’

  LAST_NAME = :LAST_NAME,’

  DEPT_NO = :DEPT_NO,’

  JOB_CODE = :JOB_CODE,’

  JOB_GRADE = :JOB_GRADE,’

  JOB_COUNTRY = :JOB_COUNTRY’

‘WHERE’

  EMP_NOI = :OLD_NO’)

InsertSQL.String = (

‘Insert into EMPLOYEE’

  (FIRST_NAME, LAST_NAME, SALARY, DEPT_NO, JOB_CODE, JOB_GRADE, JOB_COUNTRY)’

‘VALUES’

  (:FIRST_NAME, :LAST_NAME, :SALARY, :DELP_NO, :JOB_CODE, :JOB_GRADE, :JOV_COUNTRY)’ )

DeleteSQL. String = (

‘delete from EMPLOYEE’

‘WHERE’

  EMP_NO = :OLD_EMP_NO’)

end

За да се изтрие запис от таблицата със служителите,програмата използва процедура на сървъра, която се намира в примерната база данни и е свързана със следната компонента:

object spDelPmployye: TStoredProc

DatabaseName = ‘AppDB’

StoredProcName = ‘DELETE_EMPLOYEE’

ParamData = <

item

DataType = ftInteger

Name = EMP_NUM’

ParamType = ptInput

end>

end

Събитието OnUpdateRecord на компонентата Query използва процедура на сървъра вместо подразбираща се компонента UpdadeSQL за изтриване на записи.

procedure TdmData.qryEmployeeUpdateRecord (DataSet: TDataSet; UpdateKind: TUpdateKind; var UpdateAction: TUpdateAction);

begin

  // когато се изтрива запис, се използва процедура на сървъра

  if UpdateKind = ukDelete then begin

// задаване стойност на emp_no

with dmData Do

spDelEmployee.Params [0].Value := qryEmployeeEMP_NO.oldValue;

try

//извикване на процедура на сървъра която се опитва да изтрие работник

dmData.spDelEmployee.ExecProc;

UpdateAction := uaApplied // успех

except

UpdateAction := uaFail;

end;

end

else

try

//потвърждаване на промените

dmData.EmpUpdate.Apply (UpdateKind);

UpdateAction := uaApplied;

except

UpdateAction := uaFail;

  end;

end;

Потвърждение или отказване на промените:

procedure TMainForm.FormcloseQuery (Sender: TObject: var Canclose: Boolean);

VAR Res: Integer;

begin

  with dmData do

if qryEmployee.UpdatesPending then begin

Res := MessageDlg (CloseMsg, mtInformation, mbYesNoCancel, 0);

if Res = mrYes then

AppDB.ApplyUpdates ([qryEmployee]);

CanClose := Res <> mrCancel;

end;

end;

Втората форма служи за обновяване на полетата, които са свързани с други таблици.

procedure TmainForm.DBGrid1EditbuttonClick (Sender: TObject);

begin

//проверява се дали е полето department

if DBGRid1.SelectedField = dmData.qryEmployeeDEPARTMENT then

with TfmDepartments.Create (self) do

try

dmData.qryDepartment.Locate (‘DEPT_NO’,

dmData.qryEmployeeDEPT_NO.Value[]);

if ShowModal = mrOk then

with dmData do begin

if not (qryEmployee.State in [dsEdit, dsInsert]) then

qryEmployee.Edit;

qryEmployeeDEPT_NO.Value :=

qryDepartment.Fields[0].Value;

qryEmployeeDEPARTMENT.Value :=

qryDepartment.Fields[1].Value;

end;

finaly

Free;

end

else //аналогичния код на полета job....

Накрая бутона Apply

procedure TMainForm.btnApplyClick (Sender: TObject);

begin

  with dmData do

if qryEmployee.UpdatesPending then begin

AppDB.ApplyUpdates ([qryEmployee]);

//обновяване на данните

qryEmployee.Close;

qryEmployee.Open;

btnApply.Enabled := False;

end;

end;

Конфликти при обновяване - стр. 694

Когато множество потребители работят с един запис преди обновяването му може да се изведе съобщение за грешка. Това поведение може да се коригира ръчно с компонентата UpdateSQL (задавайки включването на всички полета, прочетени от заявката), или като използваш характеристиката UpdateMode на компонентите Table и Query. Подразбиращата се стойност, upWhereAll, задава, че заявката за обновяване ще има клаузата where, в която ще участват всички оригинални полета в записа. В много случаи, фактът, че друг потребител е променил поле, различно от тези, които сме променили, не е грешка и тогава ние можем да зададем стойност upWhereChanged, за да позволим на Delphi да генерира изключение и да покажем съобщение за грешка само ако друг потребител е променил съответните полета. Третата алтернатива е да използваме ключово поле, за да идентифицираме записа. което означава , че конфликтите при обновяването ще се игнорират и последния потребител, който си е потвърдил промените, просто ще премахне предишните промени. Това трябва да се избягва при многопотребителските клиенти.

Използване на транзакции - стр. 695

 

Откриване и замяна на  char

var S: string;

begin

  S := '   123.5';

  { Convert spaces to zeros }

  while Pos(' ', S) > 0 do

    S[Pos(' ', S)] := '0';

end;

Многонишково програмиране

Тука съм пуснал две нишки

Пример: D2001

 

type

  T1 = class(TThread)

    public

      I: Integer;

      procedure Mostra;

    protected

      procedure Execute; override;

  end;

 

  T2 = class(TThread)

  private

    public

      I: Integer;

      procedure Mostra;

    protected

      procedure Execute; override;

  end;

 

private

    { Private declarations }

    T1_Yes_No: Boolean;

    T2_Yes_No: Boolean;

  public

    { Public declarations }

  end;

 

procedure T1.Execute;

  var LI: Integer;

begin

  inherited;

  FOR LI := 1 to 2000 DO BEGIN

    I := LI;

    Synchronize (Mostra);

  END;

end;

 

procedure T1.Mostra;

begin

  Form1.Label1.Caption := IntToStr(I)

end;

 

procedure T2.Execute;

  VAR LI: INTEGER;

begin

  inherited;

  FOR LI := 1 TO 2000 DO BEGIN

    I := LI;

    Synchronize(Mostra);

  END;

end;

 

procedure T2.Mostra;

begin

  Form1.Label2.Caption := IntToStr(I);

end;

 

procedure TForm1.Button1Click(Sender: TObject);

begin

  T1_Yes_No := not T1_Yes_No;

  IF T1_Yes_No THEN BEGIN

    Button1.Caption := 'True';

  END

  ELSE Button1.Caption := 'False';

  t1.Create(False);

end;

 

procedure TForm1.Button2Click(Sender: TObject);

begin

  T2_Yes_No := not T2_Yes_No;

  if T2_Yes_No then

  begin

    Button2.Caption := 'True';

  end

  else Button2.Caption := 'False';

  T2.Create(False);

end;

procedure TForm1.FormCreate(Sender: TObject);

  VAR T1_: T1;

      T2_: T2;

begin

  T1_.Mostra;

end;

 

Hosted by uCoz