Delphi Study Notes
Delphi Study Notes
RAD Rapid Application Development IDE Integrated Development Environment GUI Graphical User Interface
Form Designer: Design user interface for program (design mode, execute program run mode) Object Inspector: set initial properties of all components in user interface Two tabs Properties and Events ObjectName.PropertyName := value; Object Properties: Caption caption on top of component (button, label, form, radio button) text string to identify object to user Name Program code refer to component name. Change during design time only. TabOrder Order which component receive focus when user press <Tab> or during runtime set component focus reday for input from user: edtText.SetFocus; Enabled component is active or not (True or False) Color colour of object (clPurple, clYellow,clBlue) Height Vertical size of component (pixels) Width Horizontal size of component (pixels) Top Vertical position of top-left corner relevant to form (pixels) Left Left side of component on form (pixels) Kind Bitmap Button type (bkClose, bkOk, bkYes, bkNo, bkRetry) Text Text for entering text during design or run time. Valid for Edit Components Clear to clear component property during runtime: edtText.Clear; Readonly able to enter text (True or False) Hint Text to hint when cursor over item Showhint Show or hide hint (True or False) Naming conventions: TButton: btnName Bitmap Button: bmbName TLabel: lblText TEdit: edtText TForm: frmName TRadioButton: radName TSpindEdit: sedName
Event Handler: procedure TForm1.button1Click(Sender: TObject); begin //marks beginning of program statements make up event handler Form1.Color := clYellow; Form1.Caption := Yellow; Form1.Enabled := True; end; // end of procedure end. // end of program, using full stop Events: OnClick user click component Accelerator (hot) keys: Allow user operate program buttons from keyboard. Insert ampersand (&) in front of appropriate letter in Caption property. Assignment statement: Assign values to component properties (text, caption, color, ...) lblText.Caption := New Text; frmForm1.Color := clPurple; btnButton.Enaled := True; Transfer value of one object property to another: bntCalc.Caption := edtEdit.Text; Assignment Operator := Components from Standard Menu: Button // Press button on form to do action Edit //input text Label // display an output string value Frame // CheckBox RadioButton Components from Additional Menu: BitBtn Popup Menu during design time: press <Ctrl-Space> Constructing Comments: //Text entered after double-slash { Text between left and right brace} (* Tesxt between left-parenthesis-plus-asterisk and asterisk-plus-right-parenthesis *) Delphi distinguishes between simple statement and compound statements. A simple statement is one executable line of code (not one line of code in Code Editor). A compound statement is a block of simple statements surrounded by begin/end keywords.
//simple statement Form1.Caption := 'Ha! New caption '; //compound statement begin Top := Top - 10; Width := Width + 5; end
Associating Variables with variable names: var NewText: string; Variables in Pascal hold informations (values). Variables have to be declared before they can be used. We do this after the var keyword. The var keyword can be used in several places in the code, such as at the beginning of the code of a function or procedure, to declare variables local to the routine, or inside a unit to declare global variables. When declaring a variable, we must state its type. The type of a variable defines the set of values the variable can have.
var SomeValue NewAdress IsThisWorthy GimmeMoney Something A, B, C Lookout
: : : : : : :
can declare more than one variable of the same type by placing a comma between them. (SomeValue is variable of Integer type.) Assigning values to variables After declare variable, use to manipulate data in memory. Uses the := sign for assigning values to variables.
GimmeMoney := 323,23; //Curreny type variable IsThisWorthy := True; //boolean type NewAdress := 'My new home adress'; //string type
Delphi enforces strict rules on kind assignments make between variable types. Can't assign string value to currency type variable. Constants (values that do not change during program execution) are declared using the const keyword. To declare a constant you don't need to specify a data type, but only assign an initial value.
const Min Max Middle ErrStr MaxAmount
= = = = =
// Series of characters // Specify maximum length for string // Numbers // True or False // Sing Character
Rules for variable names: 1) Name any length, but Delphi only use first 255 characters 2) First character must be letter or an underscore 3) Characters following first can be letters, numbers or underscores 4) No spaces allowed, and is case insensitive
ListBox components Add components to list box: Var Boy: string; Boy := edtName.Text; lstBoys.Items.Add(Boy); // Add input from text box to list editName.clear; editName.SetFocus; String Concatenation Concatenate one string onto end of another string using + sign TheOtherString := SthisString + ThatString; SpinEdit component: Found component Samples tab under tool pallet Value from SpinEdit component: Answer := SedName.Value;
What is a Delphi unit? A Delphi unit is a separate file used to store procedures and functions. If you know what a form is, a unit is exactly the same, except it has no visual interface. So you can't put windows controls on it like buttons and edit boxes. A form has windows controls and their associated code behind them, a unit only has the code. Converting Integer to String: edtAnswer.Text := IntToStr(Answer); Numbers in strings: var Ninety: string; Ninety := 90; Cannot do any arithmetic calculations Integer Arithmetric: + Addition Substraction * Multiplication DIV Division producing integer component (no remainder) MOD Integer remainder after division of integers Sum := 10 + 3; Difference := 10 3; Product := 10 * 3; IntDivide := 19 DIV 5; // result is 3, discarding remainder Remainder := 19 MOD 5; // result is remainder 4 Floating point (real) numbers: Fractional parts, floating point or real data types Single 2.5 X 10E-45 ... 3.4 X 10E38 Double 5.0 X 10E-324 ... 1.7 X 10E308 Extended 3.6 X 10E-4951 ... 1.1 X 10E4932 Currency -922337203685477.5808 ... 922337203685477.5807 Convert Real number to string: edtEuro.text := FloatToStrF(Euro, ffFixed, 15,2); FloatToStrF(Value, Format, Precision, Digits); Also use FloatToStr(FloatValue);
Constants: Declaration of reserved word const followed by identifier, followed by equal sign and constant value const ExchangeRate = 0.1236; Declare more than one const: const Greeting = Hello; //string DaysInWeek = 7; // integer InchToCm = 2.54; //real
Conversion Functions:
IntToStr StrToInt StrToFloat FloatToStrF FloatToStr Convert integer to string Convert string to Integer Convert string to floating-point number Convert floating-point to string and format the string (digits after decimal) Convert floating-point to string (without formatting)
Boolean Expressions:
Mathematical Notation = < > Delphi Notation = <> < > <= >= Meaning equal to not equal to less than greater than less than or equal to greater than or equal to
Price > 20.00 {True if Price greater than 20.00, otherwise false} Value <> 0 {True if value not equal to 0, otherwise false} X >= y {True if x greater than or equal to y, otherwise false}
The if ... then ... else statements If value1 = value2 then lblEqual.caption := TRUE else lblEquation.Caption := FALSE; !note no semi-column after if statements before else to continue Difference between = and := llblEquation.Caption = FALSE and lblEqual.Captioni := FALSE First is Boolean expression comparing values of left side of = with value of right. Values to either True or False. Second is assignment statement assign value on right of := to property (or variable) on left. Comparing Strings Same way as comparing numbers with relation operators (< and >). String A is less than B, x is greater than a. One first in alphabet is smaller. With long strings, individual letters are compared one-by-one from left to right. adam is less than eve, the a comes before e, and apple is greater than adam, because p comes after d. Uppercase letters come before lowercase. Z smaller than a, A smaller than a. Eve less than adam. Ignoring CASE when comparing strings Use UpperCase or LoweCase functions if UpperCase(value1) = UpperCase(value2) then lblEqual.caption := TRUE else lblEqual.caption := FALSE;
Alternative CASE conversion Edit component has CharCase property which can be set to ecUpperCase, ecLowerCase or ecNormal. If CharCase for edtValue1 to ecUpperCase in Object Inspector, the users input will be converted to upper case as he type.
The if ... then ... else statement General Format: If Condition then Statement1 else statement2; Condition is Boolean expression. If True, then Statement1 execute. If False statement2 execute. Can leave out Else part if nothing should happen if statement is False. Statements can be single program, or compound statement group placed between Begin and End. Never use Semicolon after word Then or after word Else. No semicolon between statement1 and Else.
Logical operators in Boolean expressions Addition to operators (<,>,=), Boolean can include logical operators and, or and not. Combine conditions with and and or operands to form compound Boolean expression. (value1 > 10) and (value1 < 20) (value1 = 10) or (value1 = 20) If apply not operator to condition, it returns negation of conditions value. Change value from True to False not(value1 > 10) return False if value1 is greater than 10 and True if less than or equal to 10. Value1 True True False False Value2 True False True False (Value1 and Value2) True False False False (Value1 or Value) True True True False Not(Value1) False False True True
Order of precedence (operators in expression have same precedence and evaluated left to right) 1 Bracketed Expressions 2 The NOT operator 3 And 4 Or 5 Relational operators(>,<,=, etc). = = = = = = A and B or FALSE and FALSE or FALSE or TRUE A and FALSE and FALSE and FALSE C TRUE //and has precedence over or TRUE //evaluate 1st part (give FALSE) //FALSE or TRUE give result TRUE TRUE) //brackets have precedence // evaluate 2nd part (give TRUE) // FALSE and TRUE give FALSE
(B or C) (FALSE or TRUE
Min <= x and x <= Max, Delphi evaluate x and x, result compile error (Min <= x) and (x <= Max), Boolean expressions both sides of and evaluated and then and is applied to resulting Boolean values.
Compound statements If frmColour.color = clYellow then begin frmcolour.color := clPurple; frmcolour.caption := Purple; end else begin frmColour.color := clYellow; frmColour.Caption := Yellow; end; When more than one statement appear in Then or Else part, statements must be enclosed by Begin and End. Note no semicolon before else statement. Structuring if...then...else If Salary > 7000.00 then tax := salary * highTax else Tax := salary * lowTax; If slalary > 7000 then Tax := salary * highTax else Tax := Salary * lowTax; If salary > 7000 then Tax := slalary * highTax Else Tax := salary * lowTax; Semicolons in if...then...else statements Rule1: Semicolon is not used after the word Then Semicolon after Then interpreted as end of if...then...else If x >= 50 then; //wrong semicolon after then Result := Pass Else //error occur since else not //considered as part of statement Result := Fail; If x >= 50 then; Result := Pass; //if x >= 50 then do nothing //this will always be executed
Rule2: Semicolon not used directly after word else. Semicolon after else will not cause syntax error, cause program to give incorrect result. If x >= 50 then Result := Pass Else; //else do nothing Result := Fail // always execute Rule3: Semicolon directly before else will always report syntax error
TMemo
Properties: Lines contain lines of text displayed in the Memo ScrollBars vertical/horizontal scrollbars (ssVertical, ssHorizontal, ssBoth and ssNone) WordWrap If true word displayed on next line if not fit into a line lines. LoadFromFile loads lines of text from text file Lines.SaveToFile Save to file Give user space to type text of more than one line
Methods: Use:
Nested IF statement if IsMammal then lblClass.Caption := Mammal; if IsReptile then lblClass.Caption := Reptile; if not (IsMammal) and not(IsReptile) then lblClass.Caption := Unknown; If first if is true, the rest should not be executed. Replace IF structure with Nested IF statement if IsMammal then lblClass.Caption := Mammal else if IsReptile then lblClass.Caption := Reptile else if not (IsMammal) and not(IsReptile) then lblClass.Caption := Unknown; Layout of Nested IF Statements If Condition1 then If Condition2 then Statment1 else Statement2 else Statement3; Else belong to nearest IF preceding it which does not already have an Else part. Alternative:
If Condition1 then if Condition2 then Statement1 else Statement2 else Statement3;
Consider: If Condition1 then If Condition2 then Statement1 else Statement2; One Else part. Apply rule first IF preceding Else, line 2. IF in line 1 has only Then part. If want Else part belong to outer If, must place nested if...then between begin and end: If Condition1 then begin if Condition2 then Statement1; end else Statement2; Then part of first IF consist only of compound statement between begin and end. Could not put semicolon after Statement1 to indicate second IF. Delphi syntax rule state never use semicolon in front of Else since will cause compile error. Rule1: No semicolon after Then Rule2: No semicolon directly after Else Rule3: No semicolon after Else
Nested If structure with multiple If statements: Series of nested If statements where every Else part consist of neste if..,then...else statements. if Condition1 then Statement1 else if Condition2 then Statement2 else if Condition3 then Statement3 else Statement4;
TCheckBox:
Properties: Prefix: Use: Checked If true a tick appears in box; if false box is empty chk... True/False (or Yes/No) choice
Const Myvalue = 100; Var SelectValue: integer; Begin SelectValue := seDemo.value; Case SelectValue of Myvalue : lblDemo.Caption := Option type 40, 50, 60 : lblDemo.Caption := Option type 70..99 : lblDemo.Caption := Option type 1..39, 41..49, 51 : lblDemo.Caption := Option type Else lblDemo.Caption := Not in one of the options; end; end; For Char: A..H I..L, N..P M
1; 2; 3; 4;
: : :
Can also be compound statements enclosed within begin ... end. When is CASE statement preferable to IF statement: CASE statement generally preferable to multiple alternative IF because easier to programmer to read and understand. CASE statement used only if all alternative options depend on value of same ordinal variable. If choice depends on different variables, real or string variable, nested IF statements must be used. CASE statement can be replaced with multiple IF structure, but not all multiple IF can be replaced with CASE
Input Dialog boxes Input box is standard Delphi dialog box. Includes space for user to type input value in string format and always has OK and CANCEL button Surname := InputBox(User input, Enter a surname, ); Will open dialog box with caption User input, the prompt Enter a surname and the empty string as default input value. When click OK, InputBox statement return string to program where assigned to variable Surname. If click CANCEL, the default string is returned.
String Processing
1) 2) 3) 4) 5) Delete specific section in string Insert substring into string at given location Get length of string Find position of substring in string Extract part of string
Returns an integer specifying the position of the first occurrence of one string within another, where the search starts at a specified position. PosEx looks for the first complete occurence of Str in Source, beginning the search at StartFrom. If it finds one, it returns the character position in Source of the first character in Str as an integer value, otherwise it returns 0. PosEx also returns 0 if StartFrom is greater then Length(Source) or if StartPos is < 0
Removes Count characters from a string S, starting at Index. Delphi leaves the string unchanged if Index is not positive or greater than the number of characters after the Index. If Count is greater than the rest of the characters after the Index, the rest of the string is deleted.
function Length(const S: string): integer function Length(const S: array): integer
Returns an integer containing the number of characters in a string or the number of elements in an array. For an array, Length(S) always returns Ord(High(S))-Ord(Low(S))+1
Inserts a substring Source into a string S at a given position Index. If Index is zero or negative Source is inserted at the beginning of S. If Index is greater than the Length of the string, Source is added to the end of S.
Trim String
function Trim(const S: string): string;
Returns a string containing a copy of a specified string without both leading and trailing spaces and non-printing control characters.
General format of MessageDlg function: MessageDlg(StringToDisplay, TypeOfMessage, whichButtons, HelpTopic); - StringToDisplay is message displayed inside dialog - TypeOfMessage is Delphi constant (mtWarning or mtError) determains icon and box caption - whichButtons is Delphi constants represent selection of standard buttons. Exmaple OK button ([mbOK]), OK and Abort ([mbOK, mbAbort]) and [mbYes, mbNo] - HelpTopic is integer value, normally 0 Handlin exceptions using message dialogs Procedure TfrmComission, btnCalculateClick(Sender: TObject); Var Sales, Comission: doubl; Begin Try Sales := StrToFloat(edtSales.Text); Except If MessageDlg(unable to process sales value + edtSales.Text, mtError, [mbOK, maAbort],0) = mrOK then begin // user click OK edtSales.SetFocus; exit; //jump out of event handler end else // user click Abort Close; //end the program End; // end of try ... except
Description The MessageDlg function is used to display messages to the user. These messages may be informational, or warnings or whatever. There is complete freedom over the choice of buttons that the user may press to acknowledge the dialog. For example, the user may be shown an error message, and be allowed to abort, retry or cancel the erroneous process. The DialogType may have one of the following enumerated values: mtWarning Displays a exclamation symbol mtError Displays a red 'X' mtInformation Displays an 'i' in a bubble mtConfirmation Displays an question mark mtCustom Displays just the message The Buttons value may be one or more of the following enumerated values : mbYes Displays a 'Yes' button mbNo Displays a 'No' button mbOK Displays an 'OK' button mbCancel Displays a 'Cancel' button mbAbort Displays an 'Abort' button mbRetry Displays a 'Retry' button mbIgnore Displays an 'Ignore' button mbAll Displays an 'All' button mbNoToAll Displays a 'No to all' button mbYesToAll Displys a 'Yes to all' button mbHelp Displays a 'Help' button
You specify these values comma separated in square brackets, as in the second code example. Delphi provides a number of predefined button combinations: mbYesNoCancel = [mbYes,mbNO,mbCancel] mbYesAllNoAllCancel =[mbYes,mbYesToAll, mbNo,mbNoToAll,mbCancel] mbOKCancel =[mbOK,mbCancel] mbAbortRetryCancel =[mbAbort,mbRetry,mbCancel] mbAbortIgnore =[mbAbort,mbIgnore]
Now Delphi seem to have made a design error when setting the return value from the dialog box. Instead of specifying the enumeration value of the button pressed, it uses a completely different set of enumeration names: mrYes = 6 mrNo = 7 mrOK = 1 mrCancel = 2 mrAbort = 3 mrRetry = 4 mrIgnore = 5 mrAll = 8 mrNoToAll = 9 mrYesToAll = 10 The values given are the numerical values of these enumerations, given in the numerical order that the mb equivalents are defined. This is very odd. Additionally, these values are defined in the Controls unit, not the Dialogs unit. Note that the Help button has no equivalent return value. This is because it does not terminate the dialog. The HelpContext value is used in conjunction with the Help button. It's use is beyond the scope of Delphi Basics.
Repetition
The For Loop
for count := sedLower.Value to sedUpper.Value do Starts a loop that executes a finite number of times Counter Driven Loop
1 for Variable := Integer Expression to|downto Integer Expression do statement; for Variable := Char Expression to|downto Char Expression do Statement; for Variable := Enum Expression to|downto Enum Expression do Statement; // Loop 5 times For i := 1 to (10 div 2) do ShowMessage('i = '+IntToStr(i)); For c := 'E' downto 'A' do ShowMessage('c = '+c);
2 3
Structure: For CounterVariable := LowValue to HighValue do Statement; For x := 10 to (y*10-30) do ... For x := 100 to 50 do ... //never executed since smaller //bigger than upper value For c := a to z do ... //execute 26 times For c := a to a do ... //executed once For z := HighValue downto LowValue do ... //count down Can also be used with compound statements starting with BEGIN and ending with END;
The Random function generates random numbers. They can be floating point numbers in the range : 0 <= Number < 1.0 or integer numbers in the range : 0 <= Number < LimitPlusOne Delphi uses a pseudo random number generator that always returns the same sequence of values (232) each time the program runs. To avoid this predictability, use the Randomize procedure. It repositions into this random number sequence using the time of day as a pseudo random seed.
Randomize
Reposition the Random number generator next value
procedure Randomize ;
The Randomize procedure is used in conjunction with the Random function. It repositions the random number generator in its sequence of 232 pseudo random numbers. Randomize uses the time of day as the seed for this repositioning, so should provide a reliable method of creating an unpredictable sequence of numbers, even if they are a part of a predetermined sequence.
// Get an integer random number in the range 1..100 ShowMessage('Fixed first 5 random numbers'); for i := 1 to 5 do begin int := 1 + Random(100); // The 100 value gives a range 0..99 ShowMessage('int = '+IntToStr(int)); end; // Now randomize to reposition Randomize; ShowMessage(''); // Get an integer random number in the range 1..100 ShowMessage('Random next 5 numbers'); for i := 1 to 5 do begin int := 1 + Random(100); // The 100 value gives a range 0..99 ShowMessage('int = '+IntToStr(int)); end; Fixed first 5 random numbers int = 1 int = 4 int = 87 int = 21 int = 28
next 5 numbers = 35 = 74 = 45 = 50 = 31
Can replace any For Loop with While Loop. Sum := 0; for count := sedLower.value to sedUpper.value do sum := sum + count; While loop:
Sum := 0; Count := sedLower.value; While count <= sedUpper.value do begin Sum := sum + count; Count := count + 1; End; For loop automatically sets Count initially to the value in sedLower, While loop have to include assignment to do this.
Rules for using While..do statements: Rule1: Variable(s) appear in loop condition must be initialized before While Loop encountered. Called loop control variable(s). Rule2: Inside loop body the value of loop control variable(s) must be modified to ensure loop condition become False at some stage, otherwise program will continue forever.
Arrays
Is a data structure allows storing set of data items of same type under single name. Compare array with Items property of ListBox (or Memo), which also store list of data items in one structure. Difference is elements of array can be of any Delphi data type, not just strings as ListBox. Var NamesArray: array[1..20] of String; Refer to individual elements with NameArray[1], ect, or with integer NamesArray[Index] 1 to 20. Lower bound of index range neet not be 1 Var TotalRainfall: array[1990..1999] of double; Holds 10 rainfall figures relevant to year as index, more meaningful than using 1 to 10. Assignment statement TotalRainfall[1990] := StrToFloat(edtInput.Text); Or NamesArray[1] := James; Looping through an array For Loops useful accessing and manipulating arrays. Sum := 0; For Counter := 1990 to 1999 do Sum := Sum + TotalRainfall[Counter];
Dynamic Arrays
Do not know how many elements there will be. When declare dynamic array, leave out the index range Var ManyWord: array of string; Number of elements is unknown. Before using array, size must be set by calling SetLength procedure SetLength(ManyWords, 10); Dynamic array indexed from 0 to number of elements 1. In example, ManyWords[0] is first element and ManyWords[9] is last element.
Two-dimensional Arrays
Multi-dimensional arrays store tables with several rows and columns (like spreadsheet) Var Rainfall : array[1995..1999, 1..12] of double; Declare two-dimensional array with 5 rows (indexed 1995 to 1999] and 12 columns (indexed 1 to 12). Each row store the 12 monthly rainfall figures for the relevan year.
Use Const
Edit box with scrollable drop-down list attached. User can either select from list or type directly into edit box.
Cities: array[1..3] of string = (Durban, Cape Town, Johannesburg); For I := 1 to 3 do cboFromCity.Items.Add(Cities[I]); //Add cities to ComboBoxes dboFromCity.ItemIndex := 0; // default index of ComboBox to 1st City
ListBoxes
TListBox Properties ItemIndex Item.Count lst... Gives index of currently selected item in list. Value -1 no item selected Number of items listed in ListBox
Prefix
Suppose have ListBox called lstName, then lstNames.Items[0] is first item and lstNames.Items[NoOfItems 1] is the last item. NoOfItems is number of items in ListBox and given by property lstNames.Items.Count Load from file: lstNames.Items.LoadFromFile(dlgLoad.FileName); To clear liest use: lstNames.Clear; lstNames.SetFocus; Deleting item from the list lstNames.Items.Delete(Index); Index is the current item to be deleted. Index appear in round brackets. Delete method of Items property and Index is parameter (not an index) that is passed to that method. When value in square brackets follow reference list (eg lstNames.Items[Index], where lstNames.Items refer to list), value is an index to specific value in the list.
RadioGroups
Special GroupBox that contains only RadioButtons. To add RadioButtons to RadioGroup edit Items property in Object Inspector. Each string in Items represent Caption of a RadioButton. Number of strings in Items property determines number of RadioButtons in the group. ItemIndex property (integer) indicates which RadioButton in group currently selected. If value 0, 1st button selected. If none, value is -1. TRadioGroup Properties
OnClick rgp... GroupBox contains only RadioButtons. Number of RadioButtons and Captions determined by strings caontained in Items propert.
Contains Captions of RadioButtons appear in group Index of currently RadioButton in RadioGroup. Value is -1 if none Selected Number of RadioButtons in RadioGroup Integer value determine number of columns in which RadioButtons Arranged inside RadioGroup. Default value 1 makes buttons appear in single column Activated if user clicks any RadioButtons in RadioGroup
Using Case statements with Radio Group Case rgpAverage.ItemIndex of -1 : MessageDlg(Indicate average score., mtWarninig, [mbOK],0);
0 : Amount := R10 000.00; 1 : If chkAccounting.Checked or chkEconomics.Checked then Amount := R8 000.00; Else Amount := R5 000.00; 2 : If chkAccounting.Checked then Amount := R3 000.00; End;
Sender - The object that detected the mouse action. Button - Indicates which mouse button was involved: mbLeft, mbMiddle, or mbRight. Shift - Indicates the state of the Alt, Ctrl, and Shift keys and the state of the mouse buttons when the event occurred. X, Y - The pixel coordinates of the mouse pointer in the client area of the Sender.
OnMouseMove occurs when the user moves the mouse pointer while the mouse pointer is over a control. Since the OnMouseMove event handler will be called relatively frequently, any code inside this event handler will be executed often. As we know, we can combine the keyboard with the mouse. For example, we can have the ShiftMove combination draw a circle. Let's see MouseMove in action (new project/new form):
procedure TForm1.FormMouseMove(Sender: TObject; Shift: TShiftState; X,Y: Integer) ; begin if ssShift in Shift then Canvas.Ellipse(x-20,y-20,x+20,y+20) else if ssCtrl in Shift then Canvas.Rectangle(x-20,y-20,x+20,y+20) ; end; With this code, moving the mouse over the form causes circles to follow the mouse if Shift key was pressed during move, and rectangles if Ctrl key was pressed
procedure TfrmShow.bmbCloseMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer); begin bmbClose.Font.style := [fsBold,fsunderline]; end; //Change font when mouse move over botton procedure TfrmShow.FormMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer); begin bmbClose.Font.Style := []; end; //reset font back when mouse move over form
if button = mbLeft then frmMousePos.Canvas.Rectangle(x-10, y-10, x+10, y+10); end; end. TMousButton can have values mbLeft, mbRight, mbMiddle. TShiftState can have values none, one or more of ssShift, ssAlt, ssCtrl, ssLeft, ssRight, ssMiddle, ssDouble. Show whether any keyboard buttons where held down when OnMouseDown event occurred and whether event was initiated by mouse left, right or middle button or double-click.
Same coordinates as for Rectangle. Ellipse then drawn as largest ellipse to fit in rectangle. Specified bounding box of ellipse. If bounding box is square, ellipse become a circle. Canvas methods used: Procedure TextOut (X, Y: Integer; const Text: String); Procedure Rectangle(X1, Y1, X2, Y2: Integer); Procedure Ellipse(X1, Y1, X2, Y2: Integer);
Additional Notes on Canvas Drawing (not from book): ********************* DrawBtnClick ******************} procedure TForm1.DrawBtnClick(Sender: TObject); {Draw some ellipses on the canvas - random size and color} var i:integer; cx,cy:integer; begin with image1, canvas do begin for i:= 1 to 10 do begin cx:=random(width); cy:=random(height); brush.color:=random(256*256*256); ellipse(cx,cy,cx+random(100), cy+random(100)); end; end; end; {******************* ClearBtnClick ****************} procedure TForm1.ClearBtnClick(Sender: TObject); {Erase the canvas by drawing a big rectangle} begin with image1, canvas do begin brush.color:=clwhite; canvas.rectangle(clientrect); end; end; {***************** SaveBtnClick *******************} procedure TForm1.SaveBtnClick(Sender: TObject); {Make a bitmap, copy canavsa to it and save it in a listbox} var b:TBitmap; begin b:=TBitmap.Create; b.height:=image1.height; b.width:=image1.width; b.canvas.copyrect(rect(0,0,b.width,b.height),image1.canvas,image1.cl ientrect); listbox1.items.addobject('Image #'+inttostr(listbox1.items.count+1),b); end; {******************* ListBox1Click *****************} procedure TForm1.ListBox1Click(Sender: TObject); {Retrieve bitmap from listbox and copy it to the canvas}
var b:TBitmap; begin if listbox1.itemindex>=0 then with listbox1 do begin b:=TBitmap(items.objects[itemindex]); image1.Canvas.CopyRect(image1.clientrect,b.canvas,rect(0,0,b.width,b .height)); end; end; {******************** Image1MouseDown **************} procedure TForm1.Image1MouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); begin with image1.canvas do begin pen.width:=3; pen.color:=clblack; drawing:=true; moveto(x,y); end; end; {*************** Image1MouseMove **************} procedure TForm1.Image1MouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer); begin If drawing then with image1 do begin cursor:=crNone; {to keep cursor redraw from erasing part of our line} canvas.lineto(x,y); cursor:=crdefault; end; end; {***************** Image1MouseUp ****************} procedure TForm1.Image1MouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); begin drawing:=false; end; {******************* Formcreate *************} procedure TForm1.FormCreate(Sender: TObject); begin clearbtnclick(sender); {draw initial rectangle to clear image canvas} end; procedure TForm1.Button1Click(Sender: TObject); var b:TBitMap; begin If savedialog1.execute then image1.picture.savetofile(savedialog1.filename); end;
private { Private declarations } procedure DisplayPrice (Product, Price: String); public { Public declarations } end; var frmSixpenceJoe: TfrmSixpenceJoe; implementation {$R *.dfm} procedure TfrmSixpenceJoe.DisplayPrice(Product, Price: String); begin lblCostText.Caption := Product + ' cost R' + price + ' per kilogram'; end; {proc TfrmSixpenceJoe.DisplayPrice} procedure TfrmSixpenceJoe.rgpPoductsClick(Sender: TObject); begin case rgpPoducts.ItemIndex of //using TRadio Group 0 : displayPrice('Flour', '12.99'); 1 : displayPrice('Rice', '4.39'); 2 : displayPrice('Sugar', '4.10'); 3 : displayPrice('Mealie', '2.16'); end; end; {procedure TfrmSixpenceJoe.rgpPoductsClick} end. Procedures 1) Reduce amount of programming, result shorter simpler program 2) Make changing program simpler 3) Less likely to cause errors 4) Lead to library of re-usable procedures that may be useful in future programs Calling (invoking) a procedure Own procedure similar to writing event handler and calling procedure similar to calling a standard method. When user clicks RadioButton, Delphi calls associated event handler. Event handler code calls procedure DisplayPrice, passing required values for Product and Price string variables as parameters. Procedure call has form: DisplayPrice (Flour, 12.99); Can create procedure that does not use parameters. Parameters of call must match header in order, number and type of parameter. General comments on procedures Parameters provide communication between subroutine and surrounding program. Within subroutine parameter has value that can be manipulate like other variable. Unlike local variables declared within subroutine, parameters also have meaning outside subroutine. Points when using procedures: 1) Parameter list of procedure call and procedure definition must mach in respect of type, order and number of parameters. 2) Within procedure, parameter matching is by name, not by order. 3) Procedure that are methods must be declared in classs type declaration 4) Values send in procedure call van either be constants or variables 5) If procedure is method part of a class, procedure is bound to that class. When define procedure must explicitly include class name in header.
Components as parameters Extend concept of parameters to include components as parameters unit C12e07u; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls; type TfrmStudyGroups = class(TForm) btnNextName: TButton; gpbGroup: TGroupBox; lblGroup: TLabel; lstGroup1: TListBox; lstGroup2: TListBox; lstGroup3: TListBox; lblGroup1: TLabel; lblGroup2: TLabel; lblGroup3: TLabel; procedure btnNextNameClick(Sender: TObject); private { Private declarations } procedure ShowGroup (GrpNo, Surname: string; GroupList: TListBox); public { Public declarations } end; var frmStudyGroups: TfrmStudyGroups; implementation {$R *.dfm} procedure TfrmStudyGroups.ShowGroup (GrpNo, Surname: string; GroupList: TListBox); {pass component} begin lblGroup.Caption := 'Group ' + GrpNo; gpbGroup.Caption := Surname; GroupList.Items.Add(Surname); //component TListBox end; // procedure TfrmStudyGroups.ShowGroup procedure TfrmStudyGroups.btnNextNameClick(Sender: TObject); var Surname: string; begin Surname := inputbox('User Input', 'Enter Surname', ''); if surname <> '' then //input box not empty begin case upcase(surname[1]) of 'A'..'G' : ShowGroup('1', Surname,lstGroup1); 'H'..'O' : ShowGroup('2', Surname,lstGroup2); 'P'..'Z' : ShowGroup('3', Surname,lstGroup3); else //first letter not alphabet lblGroup.Caption := '?' end; //case end //if surname else //User press Cancel or left input box blank showmessage('No surname provided');
end; end. Why and when should we use procedures? When same or similar task more than once. Using methods consolidate program statements connected to particular operation in one place. Good practise to choose name that describes clearly what method does. Comment clearly, include brief comment after methods header to describe what the method does and role of each parameter. Returning values from a procedure Can write procedure that sends data in other direction, out of procedure to the calling statement. Place the reserved word var in the parameter list in front of the parameter whose value we want to calculate end then send out from the procedure to the calling statement. The Inc(x) procedure Inc() use variable parameters. Inc(x) is same as x := x + 1; Example of parameter as method to use to provide return value. unit C12e08u; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, Spin; type TfrmFee = class(TForm) sedTakings: TSpinEdit; btnFee: TButton; lblFee: TLabel; procedure btnFeeClick(Sender: TObject); private { Private declarations } procedure CalcFee (Takings: integer; var FeeStr: string); public { Public declarations } end; // end TfrmFee = class(TForm) var frmFee: TfrmFee; implementation {$R *.dfm} procedure TfrmFee.CalcFee (Takings: integer; var FeeStr: string); var Fee: double; begin Fee := Takings * 0.075 + 20; //Calculate fee FeeStr := 'The fee is ' + FloatToStrF(Fee, ffCurrency, 15, 2) end; // procedure CalcFee procedure TfrmFee.btnFeeClick(Sender: TObject); var FeeStr: string; begin CalcFee(sedTakings.Value, FeeStr); // Proc call: derive string lblFee.Caption := FeeStr; //display it end; // procedure TfrmFee.btnFeeClick
end. CalcFee takes two parameters: Takings, value parameter of type integer, and FeeStr, variable parameter of type string. Reserve word var is immediately before FeeStr in parameter list. Have both variable parameters, declared in the method header and local variables, declared between method header and the begin statement. Parameters provide communication between calling statement and procedure and has value outside the method. Local variables are available only inside a method.
If allowAllUp is True, all SpeedButtons in group can be unselected. If False, group acts like group of RadioButtons. To select SpeedButton, set Down property to True. SpeedButtons can function independently or in group. If GroupIndex is zero, it functions independently. If several SpeedButtons have same nonzero value for GroupIndex, they function as a group
Prefix Use
spd... Speed buttons often have images on their faces and used with panels to create toolbars. Can function either independently or in groups.
unit C12e09u; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, Buttons, StdCtrls; type TfrmMarkUp = class(TForm) gpbWholesale: TGroupBox; gpbMarkUp: TGroupBox; gpbSelling: TGroupBox; edtWholesale: TEdit; lblSelling: TLabel; spdFifteen: TSpeedButton; spdTwenty: TSpeedButton; spdTwentyFive: TSpeedButton; bmbReset: TBitBtn; procedure spdFifteenClick(Sender: TObject); procedure bmbResetClick(Sender: TObject); procedure spdTwentyClick(Sender: TObject); procedure spdTwentyFiveClick(Sender: TObject); private { Private declarations } procedure SellingPrice (Cost: String; Markup: double; var SellStr: string); // new method public { Public declarations } end; var frmMarkUp: TfrmMarkUp; implementation {$R *.dfm}
procedure TfrmMarkUp.SellingPrice (Cost: String; Markup: double; var SellStr: string); // new method var Wholesale, Selling: double; begin Wholesale := StrToFloat(Cost); Selling := Wholesale + Wholesale * Markup; // add markup SellStr := FloatToStrF(Selling, ffCurrency, 15, 2); end; // procedure TfrmMarkUp.SellingPrice procedure TfrmMarkUp.spdFifteenClick(Sender: TObject); var SellStr: string; begin SellingPrice(edtWholesale.Text, 0.15, SellStr); lblSelling.Caption := SellStr; end; // procedure TfrmMarkUp.spdFifteenClick procedure TfrmMarkUp.spdTwentyClick(Sender: TObject); var SellStr: string; begin SellingPrice(edtWholesale.Text, 0.2, SellStr); lblSelling.Caption := SellStr; end; procedure TfrmMarkUp.spdTwentyFiveClick(Sender: TObject); var SellStr: string; begin SellingPrice(edtWholesale.Text, 0.25, SellStr); lblSelling.Caption := SellStr; end; procedure TfrmMarkUp.bmbResetClick(Sender: TObject); begin edtWholesale.clear; // clear wholesale price lblSelling.Caption := ''; //clear selling price edtWholesale.SetFocus; spdFifteen.down := false; // setet 15% button spdTwenty.down := false; // setet 15% button spdTwentyFive.down := false; // setet 15% button end; end.
Constant parameter restricted version of value parameter since cannot change values. Using where appropriate can limit chance of changing values by mistake. Out parameter restricted version of variable parameter, can only return value from method and cannot bring value into method the way variable parameter can. Procedure SellingPrice (const Cost: string; const Markup: double; out SellStr: string); User Interface Factors Reversibility reversibility state user should be able to backtrack when choices are made. User should ideally fell free to experiment with interface without being concerned that they will do something wrong that cannot be undone. Conflicting principles Different user interface design principles may conflict with each other (eg reversibility and economy).
There are similarities between functions and procedure. Also differences: functions designed particularly for doing arithmetic and mathematical operations and supply single value without using variable or out parameters. Functions may be either methods (part of class) or standalone functions. Delphis standard functions doing string handling NewValue := StrToInt(2468); CapitalsStr := UpperCase(This is a crazy string); Message Dialogs also involve functions If MessageDlg(Do you want to exit?, mtConfirmation, [bYes, mbNo], 0) = mrYes then // executed if user clock Yes Else // executed if user clicks No Functions different from procedures, functions name carries a value. Examples StrToInt ot UpperCase have values assigned to variables, MessageDlg has value that can be tested in IF statement. Procedure names do not carry any values, supply through the VAR and OUT parameters in their parameter list. unit C13e01u; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, Buttons; type TfrmTax = class(TForm) gpbSell: TGroupBox; gpbTax: TGroupBox; edtSell: TEdit; lblTax: TLabel; bmbOK: TBitBtn; bmbClear: TBitBtn; procedure bmbOKClick(Sender: TObject); procedure bmbClearClick(Sender: TObject); private { Private declarations } function TaxIncl (Selling, Rate: double): double; //added public { Public declarations } end; var frmTax: TfrmTax;
implementation {$R *.dfm} function TfrmTax.TaxIncl (Selling, Rate: double): double; //added begin Result := (Selling * Rate) + Selling; end; // function TfrmTax.TaxIncl procedure TfrmTax.bmbOKClick(Sender: TObject); const Rate = 0.14; var Selling, Tax: double; begin try Selling := StrToFloat(edtSell.Text); //convert input to double Tax := TaxIncl(Selling, Rate); // calculate tax via function lblTax.Caption := FloatToStrF(Tax, ffCurrency, 15,2); //display except ShowMessage('Invalid Input'); bmbClear.Click; // Reset interface by 'Clicking' bmbClear end; end; procedure TfrmTax.bmbClearClick(Sender: TObject); begin lblTax.Caption := ''; edtSell.Clear; edtSell.SetFocus; end; //procedure tFrmTax.bmbClearClick; end. General form of function declaration: Function FunctionName (ParameterList): ReturnType; Use only value or constant parameters to bring values into function. Function calcuolate value of type ReturnType attached to functions name, and functions name similar to variables name. Heade definition is similar to function declaration except class name attached to front of function name Declaration: function TaxIncl (Selling, Rate: double): double; Function header includes name of class: function TfrmTax.TaxIncl (Selling, Rate: double): double; Delphi create temporary Result variable when a function is called. Use Result to set value calling statement receives when function end. Alternative use function name (TaxIncl) instead of Result: TaxIncl := (Selling * Rate) + Selling;
Else Result := false; Alternative: Result := (Sum > 0) and (Leftover = 0); Statment: If Validate (A, B, C) = true then ShowAccessStatus (true) Else ShowAccessStatus (false); Alternative: ShowAccessStatus (Validate (A, B, C));
Exception Handling
When exception occurs in subroutine that does not have exception handler, that subroutine terminates and exception return occurs to the calling subroutine. If subroutine has exception handling path, exception is steered through it. If not, subroutine is also abandoned and exception return happens to its calling subroutine and so on. If not include any specific exception handling for particular exception anywhere in program, Delphi calls default exception handler which then display message. Program from example: Procedure TfrmAccess.ReadCode (out A, B, C: integer); Var strValue: string; begin strValue := edtCode.Text; C := StrToInt(strValue[3]); B := StrToInt(strValue[2]); A := StrToInt(strValue[1]); End; //procedure TfrmAccess.ReadCode Procedure TfrmAccess.bmbOKClick(Sender: TObject); Var
A, B, C: integer; Begin ReadCode(A, B, C); ShowAccessStatus(Validate(A, B, C)); ResetUI; End; // procedure TfrmAccess.bmbOKClick; When user enter alphabetic string into edtCode, StrToInt function raises exception. ReadCode is abandoned and execution returns to subroutine call. Because exception not handled it is still alive. bmbOKClick does not have calling routines and Delphi default exception action and display exception message.
Accepting three characters only Use RAISE keyword to generate own exception: Procedure TfrmAccess.ReadCode (out A, B, C: integer); Var strValue: string; begin strValue := edtCode.Text; if length (strValue) <> 3 then raise Exception.create (Length Invalid); C := StrToInt(strValue[3]); B := StrToInt(strValue[2]); A := StrToInt(strValue[1]); End; //procedure TfrmAccess.ReadCode Must change bmbOKCLick to handle the exception in orderly way and update display accordingly Procedure TfrmAccess.bmbOKClick(Sender: TObject); Var A, B, C: integer; Begin try ReadCode(A, B, C); Except A := 0; B := 0; C := 0; Showmessage (Recovery from an exception); //test mess End; ShowAccessStatus(Validate(A, B, C)); ResetUI; End; // procedure TfrmAccess.bmbOKClick; If user enter alphabetic character or code not three digits long, an exception occurs in ReadCode, causing exception return to bmbOKClick. bmbOKClick now has exception handling and execution is not abandoned but continues in the exception handling path. Parameters set to zero and suitable message displayed. Exception can be raised in one place and handled somewhere else. Exception raised either in StrRoInt ot ReadCode procedure but handled in bmbOKClick event handler. The try...finally construction Can be used when finalisation code must be executed irrespective of whether or not exception occurs. Want to ResetUI to run irrespective of whether or not exception occurred. Can wrap a try...finally around a try...except statement: Procedure TfrmAccess.bmbOKCLick(Sender: TObject); Var A, B, C: integer; Begin Try Try readCode(A, B, C); ShowAccessStatus(Validate(A, B, C));
Except ShowAccessStatus(false); //force error display End; //end try...except Finally resetUI; end; // end try...finally end; // procedure TfrmAccess.bmbOKClick; IIf axception occurs in call line execution continues which calls ShowAccessStatus with parameter set to false. The end; terminates the try..except section and finally introduces code that runs irrespective of whether exception has occurred or not. Delphi guarantees ResetUI will always run. We give no notification that exception handling has been performed.