Глава 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
Служат за свързване на кода на
// Предварителна декларация
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 от менюто на
Модули и структура на програмата
При програмиране усилено се използват модули (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 за да се добави компонентата към палитрата с компоненти на
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
относителна спрямо долния десен ъгъл. Постави бутона в долния десен ъгъл, задай
стойност 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 се изпълняват от клиента, първата има
по-голямо значение при клиент/сървър програмирането, докато връзките,
дефинирани директно в
Създаване на 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 = “
Добавяне на клаузата 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
(“
Тригери и генератори - стр. 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;