202 38 9MB
English Pages 161 [163]
Code Faster in Delphi Alister Christie
00000001
Copyright Copyright © 2020 by Alister Christie All rights reserved. No part of this publication may be reproduced, distributed, or transmitted in any form or by any means, including photocopying, recording, or other electronic or mechanical methods, without the prior written permission of the publisher, except in the case of brief quotations embodied in critical reviews and certain other noncommercial uses permitted by copyright law. For permission requests, email [email protected]. ISBN: 9798686719118 First Edition
Dedication For Alex, the best reason for this book being so late.
001
00000010
Table of Contents Copyright................................................................................................1 Dedication..............................................................................................1 Table of Contents..................................................................................2 Foreword................................................................................................6 Preface....................................................................................................7 Acknowledgements...............................................................................8 Introduction..........................................................................................9 Conventions Used in this Book..............................................................................9 Scope.......................................................................................................................10 Code Samples..........................................................................................................10
Code Faster by Typing Faster..............................................................11 Touch Typing..........................................................................................................11 Getting Started with Touch Typing.......................................................................13 Know Thy Keyboard Shortcuts.............................................................................15
The Delphi Code Editor.......................................................................16 Keyboard Shortcuts...............................................................................................16 CodeInsight............................................................................................................26 Code Templates......................................................................................................27 MultiPaste...............................................................................................................31 The Editor Toolbar.................................................................................................32 IDE Insight..............................................................................................................33 Structure View........................................................................................................34 The Class Explorer.................................................................................................36 Code History...........................................................................................................37 Macros....................................................................................................................39 Surround................................................................................................................40 SyncEdit..................................................................................................................41
The Delphi Form Designer.................................................................42 Keyboard Shortcuts...............................................................................................42 Quick Edits.............................................................................................................44 Quick Actions.........................................................................................................45 Add Control and Add Component........................................................................45 Object Inspector.....................................................................................................46 Structure View........................................................................................................47 The Component Palette........................................................................................49 Editing the Form’s Source.....................................................................................51
002
00000011
Editing the Clipboard............................................................................................52 Aligning Controls...................................................................................................52 Position....................................................................................................................... 53 Alignment (and Size)............................................................................................... 53 VCL Guidelines.......................................................................................................... 56 Windows Magnifier.................................................................................................. 57
Customising the IDE...........................................................................58 IDE Layout..............................................................................................................58 Unpinning and Undocking......................................................................................58 Desktop Speedsettings............................................................................................. 61 Changing the ToolBar and ToolButtons.............................................................63 Welcome to the Dark Side.....................................................................................64 Write Your Own IDE Plugin..................................................................................66 Further Learning...................................................................................................... 70
Language Features...............................................................................71 Interfaces................................................................................................................71 Further Learning...................................................................................................... 73 Generics..................................................................................................................74 Generic Collections.................................................................................................. 76 Anonymous Methods............................................................................................77 Variable Capture....................................................................................................... 80 Anonymous Threads..............................................................................................81 Further Learning....................................................................................................... 81 Inline Variables and Type Inferencing................................................................82
Know the RTL......................................................................................84 Measuring Time....................................................................................................84 Generic Collections...............................................................................................86 TDictionary................................................................................................................ 88 Further Learning...................................................................................................... 90 Parallel Programming..........................................................................................90 No Parallel Example................................................................................................. 91 Background Thread Example.................................................................................93 Multiple Tasks Example......................................................................................... 94 Parallel For Example................................................................................................ 97 Further Learning...................................................................................................... 99 Regular Expressions.............................................................................................99 IP Address Validation............................................................................................. 100 IsMatch...................................................................................................................... 101 Match........................................................................................................................ 102 Matches..................................................................................................................... 103 Replace...................................................................................................................... 104 Summary.................................................................................................................. 104
003
00000100
Further Learning.................................................................................................... 105 Enhanced RTTI.....................................................................................................105 Reading Properties................................................................................................. 105 Writing Properties.................................................................................................. 107 Further Learning.................................................................................................... 108 FireDAC.................................................................................................................108 TFDConnection....................................................................................................... 109 Adding a TFDQuery.................................................................................................. 111 But There’s More..................................................................................................... 113 Further Learning..................................................................................................... 113
Tools and Plugins..............................................................................114 Third-Party Tools................................................................................................114 cnWizards / cnPack..............................................................................................114 Structural Highlighting......................................................................................... 114 Tab Order................................................................................................................... 117 Component Prefix Wizard.....................................................................................119 ModelMaker Code Explorer................................................................................120 Live Documentation................................................................................................ 121 Class Browser........................................................................................................... 122 Tip of the Day.......................................................................................................... 124 The MMX Toolbar................................................................................................... 124 Navigator..............................................................................................................125 Bookmarks............................................................................................................128 CodeSite................................................................................................................129 Further Learning..................................................................................................... 131 GExperts................................................................................................................131 Clipboard History.................................................................................................... 131 File Favorites........................................................................................................... 132 AutoCorrect.............................................................................................................. 132 Backup Project......................................................................................................... 133 Other Non-Delphi Specific Tools.......................................................................135 Third-Party Libraries..........................................................................................135
Metaprogramming............................................................................136 Case study - BDE Replacement............................................................................136 Find and Replace...................................................................................................137 In the IDE.................................................................................................................. 137 Turbo GREP.............................................................................................................. 137 Delphi AST............................................................................................................140 DFM Parser............................................................................................................141 reFind....................................................................................................................143 Mida Converter.....................................................................................................145 cnWizards Property Corrector............................................................................146
004
00000101
GExperts Replace Components...........................................................................147
Your Physical Environment.............................................................149 Hardware..............................................................................................................149 Keyboard.................................................................................................................. 149 Mouse........................................................................................................................ 149 Computer................................................................................................................. 149 Screens...................................................................................................................... 150 Chair.......................................................................................................................... 150 Desk........................................................................................................................... 150 Other Considerations...........................................................................................151 Environmental......................................................................................................... 151 Interruptions............................................................................................................ 151 Multitasking............................................................................................................ 152
Sharpening the Saw...........................................................................153 Where to go when you are Stuck.........................................................................153 Google is Your Friend............................................................................................. 153 Asking Questions.................................................................................................... 154 Stack Overflow......................................................................................................... 154 Recommended Reading.......................................................................................154 Social Networks....................................................................................................155 Facebook................................................................................................................... 155 LinkedIn.................................................................................................................... 155 Twitter...................................................................................................................... 155 Meetup...................................................................................................................... 155 YouTube.................................................................................................................... 156 StackOverflow......................................................................................................... 156 Delphi-PRAXiS........................................................................................................ 156 Becoming Known as an Expert...........................................................................156 What’s Improved Productivity Worth................................................................157 As an Employer........................................................................................................ 157 As an Employee....................................................................................................... 158 Self Employed.......................................................................................................... 158 Diminishing returns on investment...................................................................158 Further Learning.................................................................................................... 159
Final Words and Conclusion............................................................160
005
00000110
Foreword I’ve been around Delphi since the very beginning. I was lucky enough to be invited onto the beta after being a long-time member (on CompuServe!) of the Borland Pascal community. Somewhere in my basement is still a copy of “Wasabi” -- an early code name for Delphi -- on floppy disks. Can you imagine delivering Delphi on floppies today? Anyway, Delphi was a revelation. Real, compiled code done in a RAD way. I was at the launch event on February 14, 1995, in San Francisco. People were ten deep in the Borland booth watching demos. There was a considerable commotion as people waited to get a free CD with a pre-release copy on it. Jaws dropped at the evening demo when Delphi gracefully recovered from a General Protection Fault. It made a huge splash, and soon was one of the dominant tools in the marketplace. Why? One word: Productivity. You could simply build things faster with Delphi. Start adding the flood of native, built-in-Delphi components that soon followed, and you were running circles around the C++ and VB developers. You could build great-looking, sophisticated, data-accessing Windows applications with ease. Many business applications were made, many companies were founded, and many ISVs released outstanding products -- all because of Delphi. Delphi was productive because it was designed that way. It was designed to make it easy to use, easy to build components for it, easy to use those components, and generally easy to do things that used to be really hard. The people who designed Delphi used it themselves, and so were driven to make it productive and capable. After all – they benefitted just like we all did. And that’s what Alister’s book is all about: productivity. How do you use Delphi to be better, faster, stronger? How do you get what is in your head out and into the Code Editor faster? How do you leverage what the tool provides to make things better? What is Code Insight all about? Read this book and you’ll know. You’ll learn about keyboard shortcuts and productivity hacks and useful RTL classes and a whole lot more. It’s a long stream of productivity goodness. I’m a book author too. I know how much work it takes to write and publish a book. Take it from me. This is one of the good ones. Nick Hodges Boyertown, PA June 2020
006
00000111
Preface I like a book title that has a double meaning, and this is one of them. Firstly, this book is aimed at making you faster at programming. If you read and absorb the contents of this book, it will certainly do just that. Secondly is the implication that programming in Delphi is faster - which I fully agree with. Very powerful and useful applications can be built in Delphi without too much effort and often much less effort than in other languages or tools. To get the most out of this book you will only need a basic knowledge of Delphi, however, this book will be useful to both beginner and expert alike. No matter how long you’ve been using Delphi, you're going to have a whole lot of “I can’t believe I didn’t know that Delphi does that!”, and “I’ve wasted so much time in the past doing this manually!”, and, “where was this book years ago?”. Yeah, I hear you. You’ve got it now, and that’s what counts. This book will not teach you the fundamentals of how to program. This book will not teach you the fundamentals of the Delphi language. This book WILL teach you to Code Faster in Delphi. This book will make you faster at coding, but not better at programming in Delphi - at least not directly - that’s my next book, “Code Better In Delphi”. By writing more code in a shorter space of time, you will, of course, get better in less time because you’ll hopefully have more time to think and spend much less time pounding keys like a monkey.
007
00001000
Acknowledgements There are always a great many people to thank for helping to get a book published, and the book you are reading is no different. While all of the content is original, it still takes many eyes to spot mistakes, omissions, and just to make the text easy to read. Thank you to these people who have helped make this book possible. Joachim Dürr Primož Gabrijelčič Malcolm Groves Michael Riley Harry Stahl Jackie Thomas-Teague Shane van de Vorstenbosch Danny Wind And of course, Nick Hodges for writing such a lovely foreword.
008
00001001
Introduction Coding can be fun, and the faster you can code the more fun you are going to have. This book is all about making you more efficient in the Delphi IDE, starting with something that is not even related to Delphi specifically - touch typing. If you’ve been hunting and pecking for a while now, I hope you’ll be convinced to put in the effort to make this change to drastically improve your output. Next, we move onto getting the most out of the Code Editor and Form Designer by taking advantage of shortcuts, tips and tricks - when put together will significantly improve your productivity. I have included a shortcut table which will give you the perfect place to find ways to make your life easier and help stop you from doing it the hard way. I’ll show you how you can customise the IDE to make things easier to find and faster to reach. Next, we examine some language features that can make your code easier to write and more expressive. I’ll reveal some powerful features of the Delphi Run-Time Library (RTL) that can allow you to do some amazing things with very little code. Sometimes the Delphi IDE needs a little help, and we take a look at tools and plugins that can offer some fantastic labour saving features. Why not build some software to write your code for you? This is what we look at doing in the Metaprogramming section - the art of using tools or writing code to write or modify code. Good programmers should be in good physical shape too. You need your fingers to input code, and they are connected to the rest of your body - hopefully that’s no surprise. So I’ll share with you some tips on not getting bent out of shape by your physical environment.
Conventions Used in this Book
009
Code is in a mono-spaced font: Value := GetValue(42); Shortcut keys in italics: Ctrl+C or Escape. In some instances, I’ve placed square brackets around a key to make it easier to read, such as [;] or Ctrl+ [+] Menu items and commands are in bold: Edit|Copy or Save all References to controls are in Bold: Button1 Instructions to type something are quoted: Give the variable the type of “string”
In this book I’m going to cover a vast number of topics - many should be (and are) entire books in their own right, which means that I can only cover them briefly here. I hope this will give you enough of a taste for you to check out the resources in the Further Learning sections. Some may even be expanded on in future books of mine.
00001010
Scope
I invite you to let me know what you want to know more about and sign up for my newsletter on LearnDelphi.tv to receive several bonus tips not covered in this book. On my website you will also find a large number of free video learning resources.
Code Samples There are a few code samples included in this book, to get their full listing you can download the source code via Git. You can find the code samples on BitBucket: https://bitbucket.org/alisterchristie/books If you have Git installed, you can clone the repository: git clone https://bitbucket.org/alisterchristie/books.git This repository includes the source for all my books - you will be most interested in the CodeFasterInDelphi sub-directory. If you want to maximise your learning, you are best to manually type out the code samples so that you can understand them more fully.
010
00001011
Code Faster by Typing Faster Touch Typing Chickens hunt and peck, not software developers. One thing developers do is type significant amounts of text. To write code quickly you need to be able to touch type, that is to say, you type without looking at the keyboard. This is an important skill, really important. It allows you to keep looking at the screen - rather than continually swapping your attention between the keyboard and your source code. One cool and creepy trick you can do: when someone requires your attention, continue to finish typing whatever statement you are working on while looking directly at them - this alone makes it worth learning to touch type. Touch typing can take many years to develop. Find out how fast you can type by going to TypingTest.com (I type at about 70 words per minute or wpm, which is about six characters per second). You probably want to type at a speed of 45 wpm or better (41 wpm is the average ‘computer typist’ speed, but if you want something to aspire to, the world record is 212 wpm). Handwriting is about 30 wpm so even if you are an average typist you can out-type your colleagues who are still handwriting their code. I can’t think of a good reason for you to write code with paper and a pen, so you can add this to your list of useless facts. Although if you discuss this in a large enough group, you inevitably have someone say something that begins with “In my day…”, mentions something about ‘punch cards’, and ends with “walking miles barefoot through the snow”. Keyboards are important, and yes my mechanical keyboard allows me to type slightly quicker and more accurately than a squishy membrane keyboard, but your mileage may vary. I spent a bit of time in a real bricks and mortar computer store trying out various keyboards - and I’ve been using a keyboard that has something similar to CHERRY MX Brown switches. You can go down a serious rabbit hole learning about the relative merits of different keyboard switches (such as volume, speed, travel, clickiness and RGB-ness). Some people are very opinionated about the type of keyboard they use. I’ve used ergonomic keyboards, cheap keyboards and expensive keyboards, but I would recommend that if you can touch type, invest in a good mechanical keyboard. They’re also suitable for
011
00001100
gaming - at last, a business justification for upgrading your tech to play firstperson shooters. Typing.io is specifically designed to test your skills at typing in a programming context. This is important as programming tends to use lots of less commonly used symbols, for comments, array indexing, etc. As with any new skill, initially, your productivity will suffer as you concentrate on remembering where the keys are located, however, over time you will greatly surpass the speed of your “hunt and peck” contemporaries. There are a vast number of resources online and just Googling “how do I speed up my typing” will give you a good start. I learnt to touch-type at school (at around 11 years of age) on large electric typewriters when living in Canada. I’ve never had to use an electric typewriter in my career. However, the skill has served me exceptionally well in the intervening decades and probably will continue to be a valuable skill for a few decades to come. I’m still waiting for my future brain interface that will finally be faster than reaching for the keyboard, but for now, touch typing is the quickest way of entering text. There have been a few other keyboard innovations. For one thing, the QWERTY layout is not the most efficient. It wasn’t designed for typing efficiency, instead, it was designed so that the old manual mechanical keyboards wouldn’t lock up by moving the commonly used keys away from each other - effectively designed to slow down typing speed. Different keyboard layouts such as Dvorak allow you to type even faster (speed typing records are set on this layout), however, if you change your keyboard layout to Dvorak, swapping between yours and other people’s keyboards it can do your head in. If you want to try it though, you can change the settings on your computer without needing to buy a new keyboard. What the keys say on your keyboard won’t matter, you aren’t looking at the keys anyway, right?
Caps Lock Shift
Shift
Dvorak keyboard (From Wikipedia, public domain image)
012
00001101
Near where I live there are parking meters that require you to type in your license plate details and the keyboards are in alphabetical order. This sounds simple in principle, but I find myself having to hunt down the keys, it takes forever just to enter six characters. I wonder if the rest of the population, like me, would find a QWERTY layout easier? Another observation on keyboards. I live on a lifestyle block (Hobby Farm, or Smallholding) where we have WWOOFers and HelpX visitors stay with us for a New Zealand rural experience. If you don’t know what those things are, they are travellers who get food and accommodation while they help out on the farm. They are quite often German or French, and when they hand me their phone to type in the wifi password I often mistype. It takes serious concentration not to assume an English QWERTY layout - even when using an on-screen keyboard and there are no physical keys - both the French and German keyboard layouts are different from English ones, AZERTY and QWERTZ respectively.
° ˆ
Strg
! 1
" § $ % & / ( ) = ? ` 2 ² 3 ³ 4 5 6 7 { 8 [ 9 ] 0 } ß \ ´ Q W E R T Z U I O P Ü * @ € + ~ A S D F G H J K L Ö Ä ' # > Y X C V B N M ; : _ < | µ , . (Win)
Alt
Alt Gr
(Win) (Menu)
Strg
The German QWERTZ keyboard layout (Wikipedia, released under the Creative Commons Attribution-Share-Alike License 3.0)
Getting Started with Touch Typing If you look at your keyboard, you should notice some raised bumps on the F and J keys on your keyboard (and the 5 on the numeric keypad). These bumps allow you to find these keys without looking at the keyboard and indicate where you place your index fingers (your pointing, or trigger fingers, the ones next to your thumbs). Your left index finger on the F and right index finger on the J. Line up your other fingers on the keys either side of your index fingers. Your fingers should rest on the ASDF for the left hand and JKL; for your right hand. Your thumbs should naturally rest on the space bar. This is called the home position, and it’s where your fingers return when you are not typing. In the beginning, after you type a single letter your finger should
013
00001110
return to its home key, but as you get faster your fingers dance over the keyboard, and return to the home keys only when you pause to think.
Touch Typing fingering Each key will be controlled by a specific finger, the colour coded diagram above shows which finger you should use for each key (if you are reading this in colour). You will notice that your pinkies (little fingers, on the yellow keys) have to reach for far more keys (including Shift, Ctrl, etc.), particularly your right little finger which is responsible for many symbols that are quite commonly used in programming. To practice, you need to type text without looking at the keyboard, which can be very challenging in the beginning. You can place a towel over your hands and keyboard to prevent you from looking if you find yourself cheating. By cheating, you only cheat yourself (or some other equally trite saying, but really, you’re not harming me or anyone else - so cheat away if you desire). Take whatever steps necessary to move through the frustration rather than be defeated by it. Initially, you have to think where each letter is located and which finger to use, later you are automatically able to type the letter just by thinking of it. When you become skilled, you will just think of the word, and the characters appear magically on the screen - almost as quickly as it will when we get that brain implant interface mentioned earlier. It is this level you want to be able to achieve. This will be the point where you realise how much you need to improve your spelling because you can no longer blame it on your erratic typing technique. What is life if not the opportunity for constant self-improvement? The thing that still challenges me is the symbol keys that I have to reach with my right pinkie finger as many of these are quite a stretch, this is where I find myself glancing at the keyboard - or worse yet lifting my hand and using one of my other fingers. Don’t do it Alister, don’t do it! And neither should you.
014
00001111
Know Thy Keyboard Shortcuts You should avoid reaching for your mouse as much as possible. Each time you have to take your hands away from the keyboard, you are missing out on valuable keystrokes. This isn't ergonomic advice about Occupational Overuse Syndrome / Repetitive Strain Injury (OOS/RSI), just what makes you an efficient software developer. Of course, you should take regular breaks, and build up your wrist strength by doing some real exercise - no, gaming on your computer doesn't count as exercise. Keyboard shortcuts are almost always faster than reaching for the mouse and click navigating menus, so take time to become familiar with them. Initially, you may find yourself reaching for the mouse, but as you are about to click on a menu item notice the keyboard shortcut listed beside it. Cancel your mousing, and use the keyboard shortcut instead. This initially makes you slower as you are both reaching for the mouse then using the shortcut. It will get better. Eventually, you will learn the shortcut and not reach for the mouse at all. Touch typing also gives you the added advantage of not needing to take your eyes off the screen. You can find a list of keyboard shortcuts below in the Delphi Code Editor and Form Designer sections. You can also change the shortcuts if, for instance, you prefer Visual Studio or Emacs emulation. However, I recommend sticking with the default setting, especially if, at some point in your career, you work in a team. If you do any peer programming sharing a keyboard, you may find your sanity being tested, as all the key combinations that you’ve spent years internalising do the wrong thing entirely. Make sure your teammates also aren’t the sort to go reinventing the wheel on this either, or tears may be shed. Some IDE plugins can also redefine/overwrite some of the default shortcuts (and those of each other). Frustration may ensue, so install them at your peril (actually you will probably want to install at least one as they can improve productivity significantly, but more on this later). Examples of plugins that overwrite shortcuts are GExperts, cnWizards and MMX (Model Maker Code Explorer). You may want to reconfigure these shortcuts back to the defaults if you like to have them. The actual keyboard shortcuts are covered later in chapters The Delphi Code Editor (next) and The Delphi Form Designer.
015
00010000
The Delphi Code Editor As a Delphi developer, you don’t have much choice here, other than the particular version of Delphi. Different versions of Delphi have differing levels of stability (Delphi 2005 bad, Delphi 2007 better. Codenamed versions even better - Seattle, Berlin, Tokyo, Rio, and Sydney, at time of writing). Generally speaking, IDE stability has been improving over successive versions, so you are usually better off with a newer version of Delphi. General performance and start-up times can also be worthy of consideration, some versions were quite slow, while others received improvements. Over time additional features and shortcuts have been added, these can also significantly improve your productivity as long as you take advantage of them. As a rule, you will almost always be better off with the latest version of Delphi. In this section, I’m going to look at some of the features and tools available within the IDE, many of these are extremely underutilised. But first, let’s have a look at some keyboard shortcuts (actually it’s going to be LOTS of keyboard shortcuts).
Keyboard Shortcuts Some of these shortcuts have been in Delphi since Version 1, and some are very recent. Many I use without even thinking about them, others I use less often, but are still beneficial. I’ve included as many keyboard shortcuts as I could find, this could be pretty close to a definitive list. Some are not used very frequently but can save lots of effort (e.g. Extract Method), others get used all the time (Copy/Paste). I’ve taken these from the Delphi DocWiki1, Fandom Wiki2 and other sources. Hopefully you will be able to execute your most commonly used ones without looking at the keyboard because we are going for speed here. Shortcuts for the Form Designer are covered in a later chapter, although many are the same.
1 2
http://docwiki.embarcadero.com/RADStudio/en/Default_Keyboard_Shortcuts https://delphi.fandom.com/wiki/Default_IDE_Shortcut_Keys
016
00010001 017
Shortcut
Name
Description
Ctrl+C Ctrl+X Ctrl+V
Copy, Cut, and Paste
You should certainly know these ones, they are your basic editing commands - but I’ve seen too many people use the mouse to use these from the Edit menu.
Ctrl+Insert Shift+Del Shift+Insert
Copy, Cut, and Paste
These are alternative keystrokes used by Delphi and other applications, which date back to 1987. They can be used in the version control window (e.g. Subversion Commit) as the conventional shortcuts don’t work there.
Ctrl+Z Alt+Backspace
Undo
If you never make mistakes then you don’t need this one, but for us mere mortals…
Ctrl+Shift+Z
Redo
Typically used if you have pressed Undo too many times (or held it down too long) - i.e. to undo the Undo command.
Ctrl+Y
Delete line
I use this shortcut all the time, particularly when removing blank lines.
Ctrl+Shift+Y Ctrl+Q+Y
Delete to end of line
It’s slightly faster than pressing Shift+End, Delete.
Ctrl+T
Delete to end of word
Deletes from the cursor to the end of the current word.
Ctrl+Mouse Drag on Selected Text
Copy Text
Using this or the Shift alternative is an easy way of copy / cut / paste text, without affecting the clipboard (I’ll often use these if I have something in the clipboard already).
Shift+Mouse Drag Selected on Text
Shift Text
See above.
Ctrl+/
Comment Line
Adds or removes // at the beginning of the line, then moves to the next line. Allows you to comment out a number of lines fairly quickly. You can also highlight a block of text and comment it all at the same time. It can also be used to remove comments.
Ctrl+Space
Code Completion
See the section on CodeInsight, this one is a major time-saver, so it gets its own section.
Ctrl+Shift+Space
Parameter insight
Displays the parameters for a procedure.
Ctrl+Alt+Shift+P
Synchronise Prototypes
Use this to synchronise the procedure parameters between the interface section and
Editing Commands
Ctrl+Shift+C
Class Completion
Use this to create an implementation of a class from the signature of its interface (or in reverse).
Ctrl+Backspace
Delete back word
Works like backspace, but deletes entire words/identifiers.
Ctrl+J
Code Templates
Brings up the code templates selection popup, or executes a code template if it’s only one that matches. Also, see the section on Code Templates.
Ctrl+Shift+J
SyncEdit
Allows you to alter all matching identifiers in a selected block of text. See the section on SyncEdit.
Ctrl+Shift+G
Insert GUID
Use this to create a new GUID, particularly useful for interfaces.
Ctrl+Shift+I Tab Ctrl+K+I
Indent Block
Indents a selected block of code. You can also select the text and press Tab, which is a newer addition and has all but replaced this shortcut.
Ctrl+Shift+U Shift+Tab Ctrl+K+U
Unindent Block
The reverse of above.
Ctrl+Shift+R
Record Macro
Allows you to record a number of keystrokes as a macro. This can be very useful if you have a large number of repetitive keystrokes to perform. Macros get their own section below.
Ctrl+Shift+P
Playback Macro
Playback the macro you’ve recorded.
Ctrl+H
Show Hint
Displays the popup that appears when you hover the mouse over an identifier. When you are hovering with the mouse the hint will disappear, but with the shortcut it is more persistent.
Ctrl+Shift+T
Add To-Do Item
Pops up the Add / Edit To-Do item.
Ctrl+S
Save File
Saves the current file that is being edited.
Ctrl+Shift+S
Save All
Save all files that have changes pending.
Insert
Toggle Insert /
This toggles the Insert / Overwrite mode in
00010010
its implementation (works both ways). This shortcut is super handy when you change the parameters of a procedure.
018
00010011
Overwrite
the editor. I find this annoying, and don’t use it - see the example below in Write Your Own IDE Plugin.
Ctrl+M
Same as Enter
Ctrl+N
New Line
Adds a newline after the cursor, but stays on the current line.
Ctrl+D
Format Source
This will format the current file or selected text based on the configuration in Tools| Options|Language|Formatter|Delphi
Ctrl+I
Insert Tab
Inserts a Tab character. Your keyboard is almost guaranteed to have a Tab key these days, might be useful if it’s broken? Otherwise, why use two keys when one will do?
Ctrl+O+O
Insert compiler options
This will insert compiler options at the top of the unit currently in effect.
(, [, and {
Smart Surround
If you have some text selected, typing an opening bracket will automatically place a closing bracket at the end of the selection.
Selection
019
Double-click
Select identifier
Ctrl+W
Select identifier, then expand
A really cool shortcut, the first time you press it, whatever identifier under the cursor is selected. If you press it repeatedly, the selection will be expanded to ever bigger scope until the entire file is selected. Outside of Delphi Ctrl+W is ‘close window’ - you’ve been warned.
Shift+Keyboard Navigation
Select
Do this with combinations of navigation shortcuts, e.g. Ctrl+Shift+END to select to the end of the file.
Ctrl+A
Select All
Selects the contents of the current file in the editor or the contents of an edit box.
Highlight with Mouse
Select text
Obvious, but I mention it anyway.
Alt+Highlight with Mouse or Shift+Alt+Left, Right, Up, Down
Column Selection
Handy for deleting a column of text, or adjusting an indent by a single character. Can also use PageUp and PageDown if you want very tall columns.
Ctrl+Alt+Shift+Home/ End
Column Selection
Column selects to the first/last line of the file.
Block selection mode
Toggles block selection on, i.e. it behaves like Alt is held down when making text selection (e.g. Shift+Arrow).
Ctrl+K+T
Select word
Selects the current word under the cursor.
Ctrl+O+L
Toggle line selection mode
When you toggle this on, Shift+Up/Down will select entire lines.
Ctrl+O+K
Switch to regular selection
If you are in block or line selection mode, this will switch you back
Ctrl+Q+B
Beginning of block
If you have a block of text selected, it will take you to the beginning of it. This is handy if your cursor is at the end of the block, and you want to expand your selection upwards. Use this shortcut then Shift+Up or Shift+Left.
Ctrl+Q+K
End of block
Similar to above, but moves the cursor to the end of the selection block.
Ctrl+Shift+E
Rename
It’s hard to name something correctly. If you change your mind, use this to rename something project-wide.
Ctrl+Shift+M
Extract Method
Extracts a selected block of code into its own method, where possible. Sorts out parameters and local variables. Doesn’t like With statements (but you shouldn’t be using these anyway) or Break / Exit statements.
Ctrl+Shift+V
Declare Variable
A frequently used refactoring. Click within an undeclared variable and the IDE will create it and determine its type where possible.
Ctrl+Shift+D
Declare Field
Similar to Declare Variable, but will add a field to a class or record.
Ctrl+Shift+A
Add Uses
Place the cursor over code that is missing a unit, and this will find and add the unit to the uses section. Can be a bit (i.e. very) slow on larger projects.
Ctrl+Shift+X
Change parameters
Do this on a method or procedure and it will bring up a dialog that allows you to add, remove, or change parameters. I prefer the Change Prototypes (Ctrl+Alt+Shift+P) shortcut these days.
00010100
Ctrl+O+C
Refactoring
Navigation Commands
020
00010101
Ctrl+Shift+Num
Set Bookmark
Where Num is a number from 0 to 9. Sets a bookmark on a particular line of code. I use bookmarks frequently to help navigate code.
Ctrl+Num
Goto Bookmark
Returns to a preset bookmark.
Ctrl+Left/Right
Navigate by word
Much faster than just Left and Right character by character.
Alt+G Ctrl+O+G
Go to line number
Handy if you have and know a particular line number you need to go to directly.
Ctrl+Click Alt+Up
Go to Declaration
I use this all the time to navigate to where things are declared. Use the keyboard shortcut and you won’t need the mouse.
Alt+Left
Navigate Back
Navigate back through the stack of locations where you’ve used Ctrl+Click.
Alt+Right
Navigate Forward
Return to somewhere you have navigated back from.
Ctrl+Enter Ctrl+O+A.
Open file at cursor
Execute this when the cursor is inside a unit name in the uses to open it.
Ctrl+Up/Down
Scroll Screen
Keep the cursor stationary but scroll the window.
Ctrl+Home/End
Beginning / End of File
Shifts the cursor to the very beginning or end of a file.
Ctrl+Shift+Down
021
Jumps between implementation and interface. Works on methods and uses. UP also does the same.
Ctrl+TAB
Next Tab
Will ‘tab’ forwards between open files with the IDE.
Ctrl+Shift+TAB
Previous Tab
As above, but in reverse.
Alt+[ Alt+]
Match bracket
If the cursor is on a bracket, this will navigate to the other bracket in the pair.
Ctrl+Q+L
Toggle Ctrl+Alt navigation
Toggles the Ctrl+Alt+Navigation scope to either be limited by the current class or current unit (effects the two shortcuts below).
Ctrl+Alt+Wheel or Ctrl+Alt+Up, Ctrl+Alt+Down
Navigate Procedures
Will put the cursor at the beginning of the next/previous procedure.
Ctrl+Alt+Home Ctrl+Alt+End
Navigate Procedures
Navigate to the first/last procedure.
Next item in message view
Handy if you’ve just compiled, this will allow you to navigate through several hints, warnings, or errors in the message view one at a time.
Ctrl+Q+D
End
Move the cursor to the end of the line.
Ctrl+Q+S
Home
Move the cursor to the beginning of the line.
Ctrl+Q+C
Ctrl+End
Move the cursor to the end of the file.
Ctrl+Q+R
Ctrl+Home
Move the cursor to the beginning of the file.
Ctrl+PageUp Ctrl+Q+E
Cursor to window top
Move the cursor to the first line in the window.
Ctrl+PageDown Ctrl+Q+X
Cursor to window bottom
Move the cursor to the last line in the window.
Ctrl+Q+T
Window top to cursor
Move the editor window so that the first line is the current cursor position.
Ctrl+Q+U
Window bottom to cursor
Moves the editor window so that the last line is the current cursor position.
Ctrl+Q+P
Previous position
Use this to return the cursor to its previous position, e.g. after Ctrl+Home or PageDown. Press it again to go back.
Ctrl+Shift+K+O
Toggle On / Off
Will enable or disable code folding in the IDE, disabling it gives you a tiny bit extra horizontal space if you don’t fold.
Ctrl+Shift+K+A
Expand All
Any collapsed folds in the current unit will expand.
Ctrl+Shift+K+E
Collapse Current
Press repeatedly to fold additional scope.
Ctrl+Shift+K+U
Expand Current
The reverse of above.
Ctrl+Shift+K+T
Toggle Current
Folds or unfolds the current structure.
Ctrl+Shift+K+R
Collapse All Regions
Everything defined within any [$region ‘My Region’}...{$endregion} will be collapsed
Ctrl+Shift+K+P
Collapse Nested Procedures
These are the procedures within procedures. This can be handy if you want to navigate to the first line of a method when there are lots of nested procedures.
Ctrl+Shift+K+M
Collapse
Collapse all the methods in the current unit.
00010110
Ctrl+Q+W
Code Folding
022
00010111
Methods Ctrl+Shift+K+C
Collapse Classes
Collapse all the class definitions in the current unit.
Ctrl+Shift+K+G
Collapse to Primary Groups
Collapse down to interface and implementation.
Ctrl+Shift+K+N
Collapse Namespace / Unit
Will effectively collapse the entire file. Not sure if this is particularly useful.
Ctrl+F Ctrl+Q+F
Find
Bring up the Find toolbar on the bottom of the edit window. The text to find will default to any select text or identifier under the cursor.
Ctrl+Shift+F
Find in Files
Brings up the Find in Files dialog, it has Lots of options for searching files in the current project, or files on disc.
Ctrl+R Ctrl+H Ctrl+Q+A
Replace
Shows a dialog that allows you to search for a pattern and replace it with something else.
Ctrl+Shift+Enter
Find References
I love this; it will find all the references within the current project of a given identifier.
F3 or Ctrl+L
Find Next
Find the next occurrence of the previous search.
Ctrl+E
Incremental Search
This will highlight any occurrences of the text you type.
Ctrl+G
Find Original Symbol
For instance, do this on TForm and it will take you to the declaration of TForm.
F6 or Ctrl+.
IDE Insight
Focuses the IDE Insight edit box, allowing you to search the IDE.
Ctrl+Shift+B
Show Buffer List
A list of all the open files in the Editor.
F5
Toggle Breakpoint
Sets or removes a breakpoint.
Alt+F7, Alt+F8
Previous, Next Message
Go to the previous/next error message in the Messages View.
Searching
IDE Commands
023
Help
I still use this occasionally, but usually Google knows more.
Shift+F10
Context Popup
Displays the popup context menu. It’s like right-clicking - or pressing the context menu key - if you have one. If you do, it will likely be beside the right Ctrl key.
F4
Run to cursor
Place the cursor on a line of code, and execution will stop when that line is reached. Allows you to use a breakpoint without creating one.
F7
Step into
When debugging is paused, this will step into the current line of code.
F8
Step over
When debugging is paused, this will step over the current line of code.
Shift+F7
Trace Into
Will trace into the next line of code (very similar to Step into)
Shift+F8
Run until return
Finishes execution of the current method or procedure and breaks again.
F9
Run under debugger
Compile and run the current project, or continue if your application is paused.
Ctrl+F2
Program reset
If your application is currently being debugged, this will terminate it.
Shift+Ctrl+F9
Run Without Debugging
Just as it says on the box.
Alt+F5
Inspect
While debugging brings up the Debug Inspector window.
Ctrl+F7
Evaluate/ Modify
While debugging it allows you to change the value of a variable.
Ctrl+F5
Add Watch
Adds a watch for the current symbol when debugging.
Ctrl+F12
View Units
Shows the Search for Units dialog.
Ctrl+Alt+P
Tool Palette
Navigate directly to the tool pallet search window, this will allow you to add controls to your form, or units to your application without having to reach for the mouse.
Alt+F12
Form / Text
Toggles between the graphical form representation and its text counterpart.
Alt+F11
Use unit
Allows you to add a unit from the current
00011000
F1
024
00011001
application to the interface or implementation uses section of a unit. Ctrl+F11
Open Project
Brings up the “open file” dialog for projects.
Ctrl+Alt+F11
Project Manager Window
Places focus in the Projects window
F11
Object Inspector
Toggle between the currently selected control and the object inspector.
F12
Design / Code View
Toggles between the source code of a form and its GUI
Ctrl+F9
Compile Project
Compiles (Compile units that have changed).
Shift+F9
Build Project
Builds (Compile every unit).
Shift+F11
Add to Project
Shows an open file dialog that allows you to add a file to the current project.
Ctrl+F1
Topic Search
Same as F1, although it should open the Topic Search within Help.
Ctrl+F10
Activate Main Menu
Can also be done by tapping the Alt key.
Alt+0 Alt+[zero]
Window List
If you have multiple edit windows open (by right-clicking in the editor and selecting New Edit Window or View|New Edit Window), this will show you a list of your open windows. Having multiple edit windows is a great way to get the IDE to do “strange” things.
Shift+Alt+F11
Structure Window
Opens and moves focus to the Structure tool window.
Ctrl+Shift+B
Buffer List
Shows the Buffer List dialog, shows all open files.
Ctrl+Alt+B
Breakpoints Window
Opens and moves focus to the Breakpoints tool window.
Ctrl+Alt+S
Call Stack Window
Opens and moves focus to the Call Stack tool window (a Debugging window).
Ctrl+Alt+W
Watch List Window
Opens and moves focus to the Watch List tool window.
Ctrl+Alt+L
Local Variables Window
Opens and moves focus to the Local Variables tool window (a Debugging window).
Tool Windows
025
Threads Window
Opens and moves focus to the Threads tool window (a Debugging window).
Ctrl+Alt+C
CPU Window
Opens and moves focus to the CPU view (only available while debugging).
Ctrl+Alt+F
FPU Window
Opens and moves focus to the FPU tool window (only available while debugging).
Ctrl+Alt+D
Disassembly window
Opens and moves focus to the Disassembly tool window (only available while debugging).
Ctrl+Alt+R
Registers window
Opens and moves focus to the CPU Registers tool window (only available while debugging)
Ctrl+Alt+K
Stack window
Opens and moves focus to the CPU Stack tool window (only available while debugging)
Ctrl+Alt+E, Ctrl+Alt+(2-4)
Memory windows 1-4
Open one of four memory view windows.
Ctrl+Alt+V
Event Window
Opens and moves focus to the Events tool window (for Debugging).
Ctrl+Alt+M
Modules Window
Opens and moves focus to the Modules tool window (for debugging)
Shift+F12
Forms Window
Shows a dialog with all the forms in your application.
Ctrl+Shift+F11
Project Options
Opens the Project Options dialog
00011010
Ctrl+Alt+T
Keyboard shortcuts that are of the form Ctrl+K+X (Ctrl plus two letters) can either be: hold down Ctrl, then hold down K and press X or, hold down Ctrl, press K, then press X or ever Ctrl+K followed by Ctrl+X.
CodeInsight CodeInsight is built into the IDE and can help autocomplete the code you are currently typing. CodeInsight can be extremely helpful in cutting down the amount of typing you need to do. As fast as you can type, not typing is even faster. CodeInsight can be invoked automatically (default), or manually by pressing Ctrl+Space.
026
00011011 In the diagram, you can see CodeInsight recommending a list of identifiers that begin with (or in Delphi 10.4 or later contain) “Roll”. It’s also worth noting that you can resize the code complete window - I make mine quite tall, which allows me to see more suggestions without having to scroll. You can select a suggestion in the list by scrolling with the cursor keys (PageUp and PageDown also work), then pressing Enter to select the one you want, or you can reach for the mouse (which you already know is bad - I’ll let you slap your own wrist if you find yourself doing it). You can configure its options in Tools|Options|User Interface|Editor Options| Code Insight - or you can type “code insight” into the IDE Insight box (Ctrl+. Or F6) and find it there. There are a bunch of things to configure there. I find the defaults just fine, although you might like to enable Auto Invoke if you work with small projects - on more substantial projects it can be quite painful as it can cause long delays as it tries to work out the auto-completion list. In Delphi 10.4 Sydney CodeInsight received a substantial upgrade, it now uses the Language Server Protocol (LSP) and should make the code editor much more responsive.
Code Templates Continuing on the theme of letting the IDE write code for you, Code Templates are macros that let you type a few keystrokes which can expand out into blocks of code. Place the cursor somewhere convenient then type the letter b, and press Ctrl+J.
027
00011100 A list of code templates will popup with the begin...end template highlighted. If you press Enter, the IDE automatically places the begin...end code block with the cursor located within the block.
You can see the full list of code templates by pressing Ctrl+J on a blank line. Additionally, you can go into View|Tool Windows|Templates
The Template tool window allows you to manage the templates. Here is another example. Go to a type section of your unit and type in “classf” then press Ctrl+J, then Enter. The full outline of a class definition replaces the template shortcut within your code. From the public section (you should already be there) type “propgs”, then Space, and it creates an outline for a property with getter and setter. To name the property type “Test”, then Tab, “string” to give it a type, and press Enter (or Tab again). You should get the following (I’ve removed some blank lines):
028
00011101
interface type TMyClass = class(TComponent) private function GetTest: String; procedure SetTest(const Value: String); protected public property Test: String read GetTest write SetTest; published end; implementation { TMyClass } function TMyClass.GetTest: String; begin end; procedure TMyClass.SetTest(const Value: String); begin end; That’s quite a bit of code that you generated with only a few keystrokes. It's certainly worth becoming familiar with the available templates and knowing how to write your own. You can do this from the Templates window (View|Tool windows|Templates) by editing an existing template and saving a modified copy.
029
00011110 The Templates tool window (undocked) Double click a template to use it (or click the Insert/Play button), or use any of the other buttons to Add, Remove, Edit or Filter the templates. You also can find these actions via a Right-click. Here is a listing of a simple template:
variable variable to be freed
FreeAndNil
030
00011111
This script allows you to type “fan”, then space (or Tab or Ctrl+J) and the fan is replaced with FreeAndNil(variable), with the text variable selected, so you can just type something to replace it.
The template name attribute is the shortcut text, and the invoke="auto" indicates that it’s executed by just pressing space (otherwise you need to press Ctrl+J). There can be multiple “points” which you can tab between, here I have shown only one. The code section indicates what code should appear in the editor, with the actual code appearing in the CDATA section.
MultiPaste MultiPaste is a fantastic tool that is now standard in Delphi. It was originally part of Castalia but was integrated directly into the IDE with its acquisition by Embarcadero. It gives much more control over how you paste blocks of text into your Delphi code. For instance, say we have some SQL we want to add to a TFDQuery: select u.* from users u left join groups g on g.Userid = u.Userid where u.LastName = 'Smith' and u.FirstName = 'John' We could paste this directly into the Code Editor, then add qryGroups.SQL.Add(' to the beginning of each line and '); to the end, plus escape the quote marks. Which wouldn’t be tedious for this small example, but what if it was a hundred lines? Then you’d be looking for a better option, right? Fortunately, there is one. It’s MultiPaste, and it’s available from the Edit menu (Edit|MultiPaste).
031
00100000 As you can see in the MultiPaste dialog, we are prefixing and suffixing each line, and any quote marks optionally escaped automatically. Just click OK to paste the adjusted clipboard contents into our code. Because it can escape quotes, it can even be useful if you want to paste a single line. I love this addition, not one I use every day, but when I do use it, it’s a big time saver.
The Editor Toolbar
Stretching along the top of the code editor is the editor Toolbar. The first button (Used Units) provides a list of all the units in the uses list - I’ve never found this particularly helpful. However, the second button (File Sections) provides a mechanism to jump to specific points in your code.
032
00100001
In particular, I find going to the uses clauses quite handy at times. The two combo-boxes in the Toolbar show the Types and Methods (respectively) in the current unit, click the dropdown or type text in it to filter. The final button, Project Symbol Search, allows you to search your entire project based on types, methods, and variables. Here I’m searching for “setting”.
As you can see, it gives you a list of what it has found. Clicking on an item takes you straight to it in the code.
IDE Insight IDE Insight is a search box that lets you search IDE for settings, controls, menu items and various other things. You can find it on the title bar of the IDE window, and its shortcut is Ctrl+[.] or F6. Once active, you can type in things to search for. For instance:
033
00100010 Here I’m currently viewing a form in the editor, and I’m searching for “button”. It has found several controls on the Component Palette, some Components on the current form and many Preferences. IDE Insight is smart enough to be contextaware. If you have the Form Designer open, you get a different list than from the Code Editor. It also includes menu items that have been added by plugins. IDE Insight has also included as a search box in Project|Options and Tools| Options allowing you to quickly find rarely used or obscure options without having to hunt for them.
Structure View The Structure View shows different items depending on whether you are inside the Form Designer or Code Editor. From inside the Code Editor, the Structure View gives you an outline of your code. Double-click on a Structure View item, and it navigates you straight to that section of your code. You are likely aware that it shows you errors in your code as part of Error Insight.
034
00100011 . Did you know that you can also use it to modify your code?
If you Right-click on a class and select New (or press Insert) you can add a new field (by typing something like “name: string”), procedure or function (“function NameValid(name: string): boolean”) or a property (“property name: string”). While this can be very handy, the Structure View does have many limitations. You can declare global variables and functions - but only if you have one already in your unit. While you can declare a property, it doesn’t allow you to specify getters and setters. You can rename things, but it’s not for refactoring (it only renames the declaration, not where it’s used). And while you can create a procedure with parameters, you can’t change the parameters on an existing method. If you make a mistake, Undo won’t help you either.
035
The Class Explorer is a tool window that is not visible by default, but you can show it from View|Tool Windows|Class Explorer
00100100
The Class Explorer
The Class Explorer searches all units listed in your project (DPR) file. It lets you navigate various declarations by double-clicking on them (or Right-click|Go to Declaration), alternatively you can Shift+Click on a method and it takes you to its implementation. The reverse is also true, if the Class Explorer is visible and you right-click on something in the Code Editor and show it in the Class Explorer (Right-click|Find in Class Explorer). You can also easily add fields, properties and methods to classes by right-clicking on them.
036
00100101
Here I’m declaring a function in the public section of a form. You can see it has two parameters (arguments) and returns a TMineSweeperCell. Deletion of a field, property or method is also possible from a Right-click, this can be a quick way of deleting methods and properties as it removes both the interface and implementation sections. Arrangement of the class tree can be in three ways: Container (shown above) where the ordering is by namespace (unit to class and subclasses). Derived to Base where sub-nodes form the ancestors of classes (TAnimal would be a subnode of TDog). And finally Base to Derived where sub-nodes contain descendants (TDog would be a subnode of TAnimal).
Code History In the bottom right corner of the Delphi code editor, you can toggle between the Code and Designer (F12), or a third option History.
In History, you can see different revisions of your source files. Below I’ve selected a form from my Minesweeper clone application.
It shows the history of this file with the most recent at the top. The first entry is what is currently stored in memory, followed by the local file on disk. The
037
00100110
revisions with the blue ticks indicate what is stored in version control (Git in this case) and the green icons are local backup files. Every time you save a file in Delphi, a backup copy is saved into the __history subfolder with an additional extension appended (for instance .~2~, the larger the number, the more recent the file). You can configure how many backups you want to keep in the Editor Options (Tools|Options|User Interface|Editor Options|File backup limit). The default is 10, and the maximum is 90. I generally find that ten is enough, and if I want to go back further, I use version control. I don’t commit every time I save, so this provides an additional level of safety in case I make a major mistake that for which Undo is insufficient. This is especially true in the Form Designer where Undo is limited to the last thing you deleted. Down the bottom right, you have the option to look at the differences between the files.
Information is only handy to be able to see the full commit message, but Differences allow you to compare two revisions and see their differences. I find this very helpful to be able to compare the current file in memory with what is saved to disk as a way of double-checking my changes are really what I intended. If you examine a file that has an associated DFM or FMX file, you can select it to see what changes have happened on the form or datamodule. This can be helpful to see what properties have changed and what components were added (or removed). Below you can see how a comparison looks.
Here, the “not supported” message has been replaced by some useful code. The new code, indicated by the + sign, and removed code, indicated by the - sign, is determined by which revisions you select on the left and right. Here I’ve selected an old revision on the left and the current revision on the right. If I reversed the
038
00100111
selection (old on the right and current on the left), it would look like I had removed the code replacing it with the ShowMessage. You navigate the changes by using the Next (Shift+Ctrl+F8) and Previous (Shift+Ctrl+F7) Difference buttons. If you need to undo the changes from an entire file, Right-click on the file you want to go back to and select Revert or select the file and use the Revert button. If you need to revert a change within a revision, you can either copy and paste the text you want, or use Beyond Compare via the Show in Difference Viewer button. You can configure additional difference viewers via Tools|Options|User Interface|Difference Viewer.
Here I’m configuring Tortoise Merge as an additional external difference viewer. The Parameters are (which are pretty hard to read from the screenshot): /base:$(LeftFileName) /mine:$(RightFileName) /basename:$(LeftDisplay) /minename:$(RightDisplay)
If you want to add it and have TortoiseSVN installed.
Macros The Delphi IDE has rudimentary macro recording ability, which allows you to record a series of keystrokes, then play them back. Despite the lack of ability to save or edit these macros, you can still do some smart things. Usually, this involves doing repetitive keystrokes, for instance, adding semicolons to the end
039
00101000
of every line would be End, [;], Down repeat. You might get quite quick at this series of keystrokes, but why not make things easier on yourself? To record, press Ctrl+Shift+R, type in your keystrokes (like the End, [;], Down from above), press Ctrl+Shift+R again to stop recording, to playback, press Ctrl+Shift+P. If you have a large number of lines to apply this to, you can hold down Ctrl+Shift+P to apply repeatedly. If you can’t remember the shortcut keys, you can also use the buttons down the bottom left of the edit window.
Use the round red Record Macro button to start recording, the square Stop Recording Macro button to stop recording, and the green triangle to Playback Macro.
Surround If you highlight a block of text, you can use the Surround command (Right-click| Surround) to wrap the text in various things. For instance, you can convert a block of code to a comment by highlighting the text, Right-click|Surround|{. Which starts the block with a “{” character and ends it with a “}”. You can achieve a similar thing by highlighting and pressing { (a Smart Surround Key) or Ctrl+/ on the keyboard (and pressing again to uncomment). Say you have a block of code that you want to wrap in an if statement, typing this manually means that you need to type in the if statement, the “begin” at the start of the block and the “end” where you want it to finish, this can involve considerable cursor movement. Alternatively, select the block, Right Click| Surround|ifb, this automatically places the begin and end with the cursor inside the if statement. The surround commands are effectively code templates (it’s also possible to write your own). This means that you can also access them from the code templates window (View|Tool Windows|Templates) where you can double-click a template to execute it.
040
00101001
SyncEdit When you select some code in Delphi, you may have noticed a small icon in the margin, clicking on it enables Sync Edit Mode (Ctrl+Shift+J).
Once enabled, it allows a dynamic search and replace within the selected code.
Sync Edit Mode enabled on a block of code. In the image above, you can see that UpdateCaption is selected, and you may notice that every instance of UpdateCaption has a box around it. If I change one instance of UpdateCaption, each of the others are changed simultaneously. Each of the underlined items is an identifier that can be changed with Sync Edit. You can press the Tab key to move around between each of the identifiers. This can be a swift way of renaming something in code. However, it’s important to note that this is just a text-based search and replace and doesn’t understand the syntax of the code, as you can see from the UpdateCaption comment that will also change - this is often something you want to happen, but not always.
041
00101010
The Delphi Form Designer Keyboard Shortcuts There are a whole new set of keyboard shortcuts that you can use when designing your forms. However, there are many that are already familiar. Additionally, many shortcuts work in both the Code Editor and Form Designer (F9 to Run for example), which are not listed here. Shortcut
Name
Description
Ctrl+C Ctrl+X Ctrl+V
Copy, Cut, and Paste
Use these on a single control (and everything it owns), or selected groups of controls. Be careful when pasting between forms as you can lose events and live bindings.
Cursor Keys
Change selected control
This will change the focus of the selected control to one in the direction pressed.
Tab
Next Control
This will select the next control by tab order.
Shift+Tab
Previous Control
This will select the previous control in the tab order.
Esc
Select Parent Control
This will select the parent of the currently selected control, you can press this repeatedly until you finally reach the form (or frame/datamodule).
Left-Drag+Esc
Shift Parent
If you left-click and hold on a control, then press Esc, this will allow you to select the control’s parent for dragging, allowing you to shift the parent control (even though you might not be able to click on it directly).
Left-Click
Select Control
Shift+Click
Add/Remove control to selection
This will add or remove a control from your current selection. If you do this to a singularly selected control it will select the main form (or topmost parent).
Ctrl+Drag
Select multiple controls
This will select all the controls that touch the box made by dragging across the form or parent control.
Selection
042
00101011
Drag
Move selected control, or select multiple controls
If you start dragging on a control, it will move the control. If you start on the form background it will work like Ctrl+Drag.
Ctrl+A
Select All
Selects all the controls in the Form Designer.
Editing Commands Delete
Delete Component
Deletes the currently selected component(s). This is just about the only thing that Undo works with in the form designer.
Ctrl+Cursor Key
Move selected control(s)
This will shift the selected control(s) by one pixel in the direction.
Shift+Ctrl+Curs or Keys
Move selected control(s) faster
This will shift the selected control(s) by eight pixels.
Shift+Left/ Right
Change Width
This will increase (Right) or decrease (Left) the width of the selected control(s).
Shift+Up/Down
Change Height
This will increase (Down) or decrease (Up) the height of the selected control(s).
Ctrl+Z
Undo
This only really works to undo the last deleted component.
Other Commands
043
Enter
Toggle to Object Inspector or Form
This will switch between the Object Inspector and selected control.
Ctrl+H
Hide non-visual components
This will hide/show any non-visual components on the form.
Double-Click
Go to or create default event
If you double click on a control, you will be taken to its default event (e.g. OnClick for a TButton). If there isn’t one, it will be created.
F12
Swap between Code Editor and Form Designer
Just like it says on the box.
Alt+F12
Toggle between Form and Form Source
Use this if you want to modify the form as text.
Right-Click Shift-F10
Popup Menu
Bring up the context menu for the current selection. If you have a menu key on your keyboard you can also use that (note: keyboard shortcuts don’t currently work on FMX forms).
Hold Shift or Ctrl
Show VCL Guidelines
If you have control(s) selected, holding down Shift or Ctrl will make the VCL Guidelines visible.
00101100
There are additional shortcuts specific to other editors such as the Fields Editor for a dataset which I’ve not listed above.
Fields Editor for a TClientDataSet
Quick Edits Quick Edits are now available for the VCL and FireMonkey via Right-click|Quick Edit… (or even faster with Right-click then [Q], or faster still pressing Shift+F10, [Q] or even Shift+F10+Q, unfortunate the keyboard shortcuts only currently work for VCL forms not FireMonkey). This gives you access to several common properties for the selected control. Here is the dialog for a VCL TEdit control.
044
00101101
Here you can quickly change the name and alignment and layout properties.
Quick Actions If you Right-click on a control, the first few items in the popup menu are the Quick Actions.
As you can see, we can quickly select Left Justify and Right Justify, and Clear Text for this Edit box. The Quick Actions also appear at the bottom of the Object Inspector.
If you have limited screen space, you can Right-click on the Object Inspector, select Properties and untick both Show quick action panel and Show status bar. Hiding these will give you some additional height in the object inspector.
Add Control and Add Component If you Right-click on a container (such as a panel, group box or the form), you get two other menu items. Add Control allows you to add common controls such as buttons and labels. Add Component gives you the ability to add an image list or menu quickly.
045
00101110
Object Inspector
You can access the Object Inspector with the F11 shortcut. This takes you straight to the search box where you can type in the property you are looking for, such as “text” on a TEdit (you should see the properties HelpContext, Text, and TextHint). Press Enter to change focus from the search box to the first property and use the Up and Down keys to select the property you want. Then just type the new value of that property. These shortcuts are much faster than reaching for the mouse (you’ve probably noticed a central theme of “mouse bad, keyboard good” already). You can access the Object Inspectors properties from Right-click|Properties (or Tools|Options|User Interface|Object Inspector).
046
00101111 If you (like myself) want additional screen real estate, unticking the Show quick action panel, Show description panel, and Show status bar is useful. The Bold non-default values option is a fast way of identifying which properties you have adjusted on a control.
Structure View We’ve already seen the structure view when editing code, but there is also much you can do with it when designing a form. There are two really common things I do in the Structure View. The first is to select a control, which selects it in the object inspector, and the second is to re-parent a control. Re-parenting is useful, for instance, you might have a button that is currently on a form, but you want to shift it onto a panel.
047
00110000
You could Cut it from the form and Paste it onto the panel, and this works just fine (unless you have live bindings). But instead, within the Structure View, drag the button onto the panel. This is an easy way to shift controls from one container to another. Things get tricky when the form is complex, and you want to drag a control between two containers that can’t both be visible in the Structure View at the same time. You have several options to accomplish this. You could try to collapse other controls so that you can get both visible, do some clever dragging as the list scrolls when you drag a control near the bottom or top. Or make the structure view larger - which you can do by undocking it from the IDE then maximizing it. By Right-clicking on a control in the Structure View, you can access its context menu, including its Quick Edit and any other special menu items. The Structure View can also be a convenient place to Cut, Copy, Paste and Delete controls.
The Structure View can also provide you access to various other components that are not selectable on the Form Designer (such as FireMonkey effects and animations, or control sub-properties).
048
00110001
The Component Palette
Avoid using the mouse to add controls to your form, unless you want to place them precisely. Instead, use the Ctrl+Alt+P shortcut, then type the name of the control, then press Enter. This adds the new control as a child or sibling to whatever control you have currently selected on the form. If you do use the mouse, you can double-click the control to add it to the form, or drag it onto the form, or select the control and click where you want it placed, or drag across the form to define its size and position. If you want to add many of the same control (5 radio buttons say), you can add one and copy and paste the remainder, or you can Shift+Click the control in the palette, then each time you click on the form it adds a control until you press Escape or select a different control. If the Persist search filter option is enabled, you can type the control you want then keep pressing Enter to add them. There are several properties you can configure for the Tool Palette (Right-click| Properties on the Tool Palette or Tools|Options|User Interface|Palette).
049
00110010 Most of which I pretty much ignore as I use the palette as a way to type text searches. However, you can disable the captions and have larger buttons to make the palette similar to Delphi 7.
Component Palette with large buttons and no captions. You can also adjust the layout of the controls within the palette, just drag the categories or controls to adjust their order in the list. You can also drag controls between categories and create new categories (perhaps a favourites category).
050
00110011
Further, you can hide categories (Right-click|Delete on a category) and individual controls (Right-click|Hide), or bring back either (Right-click|Unhide Button). If you’ve made a mess with your customisations, you can Right-click| Reset Palette… to go back to the original settings.
Editing the Form’s Source You’ll love this extremely powerful technique that allows you to do some pretty useful things. To access the source of the form Right-click on it and select View as text (Alt+F12). This gives you the text representation of the form, which is the “real” representation of the form as it is stored in the DFM file. Something I do fairly regularly is to change a control’s type. For instance, below I’ve changed a TButton to a TSpeedButton:
When I return to Edit as form (Alt+F12), I’m immediately confronted by an error message:
051
00110100
TSpeedButton has no TabOrder property. If I had been diligent, I could have removed it when I was editing the form’s source. However, clicking Ignore achieves the same thing. I’m not done yet as Button2 is still a TButton as far as the PAS file is concerned. If I compile (Ctrl+F9) I get an additional error dialog. Just clicking Yes updates the source file and everything compiles just fine, and Button2 is now completely converted to a TSpeedButton.
Editing the Clipboard When you copy controls to the clipboard, the details of the control are stored in the clipboard as text (like they are in the DFM file). You can paste the control into a text editor, modify it, copy the new text, then paste it back onto the form designer. Or you could even create controls from scratch; a technique I've used to great effect many times. For instance, to define the structure of a TFDMemTable, I could write some code to build up a list of fields based on some other data I had, then paste those fields into the TFDMemTable field list.
Aligning Controls In the Form Designer, there are three tool palettes that you can take advantage of that allow you to change the position and size of controls. They are Position, Spacing and Align. They are not enabled by default so you may need to rightclick on the toolbar to select them.
052
00110101 Position
The first two buttons are Bring to Front and Send to Back. These change the zorder of the controls allowing you to change which control is in front (or behind) when two or more controls overlap. These commands can also be found on a by Right-clicking the control and selecting Control, or from the IDE main menu, Edit|Bring to Front and Edit|Send to Back. The next two buttons (Center vertically and Center horizontally) allow you to centre one or more selected controls either horizontally or vertically on their parent.
Alignment (and Size)
This collection of a dozen commands allow you to line up controls in various ways. You need multiple controls selected for all the alignment commands to be enabled. Alignment occurs based on the anchor control. The anchor control is the one with the black dots rather than grey dots surrounding it.
053
00110110 Here we can see that Button2 is the anchor control, and if I click Align left edges (the first tool button), Button1 shifts right so that its left edge matches Button2 (Button3 and Button4 are already in alignment). If instead, I wanted all the buttons to line up against Button1, I would click Button1 to make it the anchor control. The rest of the alignment commands work similarly (aligning right, vertically centred, top, bottom and horizontally centred). Additionally, you can bring up the alignment dialog from Edit|Align or Rightclick|Position|Align, the commands in this dialog work just the same as the buttons. This dialog gives you access to the alignment commands, the spacing commands (below), and the centre commands (above).
There are three align to grid commands, these all have the hatching pattern in the background, and are all based on the grid spacing, which you can adjust via Tools|Options|User Interface|Grid size/Snap tolerance.
054
00110111 The default grid size is 8 pixels which I find perfectly acceptable. You can toggle the visibility of the grid with the Display grid checkbox and toggle the Snap to grid checkbox or its corresponding button on the alignment palette. Snap to grid takes effect when you position or size a control with the mouse. The other two buttons, Align to grid, adjusts controls so that their top left corner sits on the grid, and Size to grid adjusts the control width, height and position to fit the grid (although this doesn’t seem to work in recent versions of Delphi). The final commands are sizing commands, like the Align commands they work on multiple selected components based on the anchor control. Make same height makes all the selected controls the same height as the anchor control. Make same width does the same for widths. Make same size adjusts both width and height. You can also adjust sizing from Edit|Size or Right-click on the control and select Position|Size
055
00111000
Spacing This set of eight controls are enabled when you have multiple controls selected and allows you to adjust the spacing between controls. Four adjust horizontal spacing, and four adjust vertical spacing. Space equally horizontally shifts controls so that the left sides of each control are equally spaced apart. This works great if all the controls are the same size, but if the controls are of different widths, the gaps between the controls become uneven. Increment horizontal space adds one pixel of additional space between controls. Decrement horizontal space removes one pixel between all the controls. It continues to work even once the controls overlap each other and keeps going until their left sides match. Remove horizontal space removes the space between controls so that they all touch each other. These previous three commands all work based around the anchor control, such that the anchor keeps its position. If you want the space between the controls to be equal, you can first remove the space between the controls, then increment the space until you have the spacing you want. The vertical commands work just the same way as their horizontal counterparts. Another way of setting controls to be the same size is to have multiple controls selected then adjust the appropriate property in the Object Inspector. For instance, if you wanted their left edges aligned, you would set the Left property in the Inspector.
VCL Guidelines These were a fantastic addition to the Form Designer (and it would be great to have an equivalent for FMX). These are the thin blue and pink lines that appear when you move controls around on your VCL form. The blue lines allow you to align the edges of controls, and the pink lines indicate when the base of text elements are aligned.
056
00111001
Here you can see me moving Button1 between Button2 and Button3. The pink line indicates that all the text is aligned, and the blue lines show that all the tops and bottoms of the controls line up. I generally get controls approximately positioned where they should be and use Ctrl+Arrow to fine-tune their position and Shift+Arrow to alter their size, adjusting until the desired lines appear. Another trick to check if a control is aligned is to select it, then hold down Shift or Ctrl which makes the guidelines appear (no lines indicate that controls may not be correctly aligned).
Windows Magnifier If you are feeling short-sighted like Mr Magoo, squinting at your form layout wondering if you need to move that label a pixel to the left, it might be time to pull out the magnifying glass. The Magnifier app allows you to zoom in on your screen by a specified amount. You can start the magnifier by pressing Window+ [+] and turn it off by using Window+Escape. You can configure it in Windows settings (or by clicking the settings button on the app). Once enabled, it makes it much easier to get that single-pixel positioning on controls that otherwise would be impossible to do when viewing at 100% (particularly on high-resolution laptop screens).
057
00111010
Customising the IDE IDE Layout The default IDE layout has been pretty standard for quite some time, it has changed slightly over the years, but for the most part, it has been consistent and quite usable. However, there are times when the default layout is not suitable.
Here is the default layout A. Code/Form Editor B. Structure View C. Object Inspector D. Project Window E. Component Palette F. Message Window There are unlimited ways you can rearrange the IDE, but the focus should be on maximising screen area for things you use most often (in particular the Code Editor) and minimise for the amount of mouse travel so that tools and commands that are commonly used together also have proximity. Before we look at my preferred layout, let’s examine the ways you can customise the IDE.
Unpinning and Undocking One way you can make additional space is by closing windows, you can easily do this by clicking the close button in the top right of each tool window.
058
00111011 The top of the Structure View, showing the pin and close buttons. The windows can easily be re-opened by using the appropriate menu item or shortcut-key. If we close the Structure View, we can open it again from View| Tool Windows|Structure or by its shortcut (Shift+Alt+F11). An alternative to closing windows is to un-pin them by clicking on the pin button (next to the close button). Un-pinning collapses the tool window to a tab to the edge of the IDE. You can pop out the window by moving the mouse over the tab or using its shortcut key. The window pops back once you either move the mouse outside the window or change focus to another window. Un-pinning is an excellent alternative to closing windows, giving you quick access to less frequently used windows while opening up additional screen real estate for the other windows. You can undock a tool window by dragging its title bar until a translucent box appears. Drag the window to where you want it and release. If you move the window near a suitable dock-site, it automatically tries to dock, but you can prevent docking by holding down the Ctrl key.
Object Inspector undocked.
059
00111100
Once undocked and floating, you can shift a tool window around like any other window. For instance, if you have multiple monitors, you can shift it to a secondary screen and maximise it. There are three ways you can dock windows together, side-by-side, top/bottom or together as tabs. These can be combined to group your windows in more useful ways. Here is my preferred layout on my desktop:
A. B. C. D. E. F.
Code/Form Editor Structure View Object Inspector Project Window Component Palette Message Window
As you can see in the image, I group my windows to the left side and have a large area for code on the right side with the message window below the other tool windows to maximise the available height for the Code Editor. The tool windows are on the left to reduce mouse and eye travel across the IDE so that things are faster to access. I will also often have the Multi-Device Preview window docked to the right side if I’m doing any FMX work. My preference is for the IDE to be all docked together on one screen, and use additional screens for other windows (email, web, documentation, etc.). The layout on my laptop (which is pretty old) is different. There isn’t as much screen height, so the message window has to fit below the code. Play around to work out what suits your circumstances best.
060
00111101
Once you have arranged the IDE to how you want it, you may want to save that layout so that should you break it, you can quickly restore it. Here’s how you do that:
Desktop Speedsettings The Speedsettings are accessible from a button and Combobox in the title bar of the IDE.
By default there are four layouts that you can select, they are in order: 1. Classic Undocked. All the windows are undocked, and the component palette on the toolbar is made visible, I’ve never been a fan of this layout, but if you have come straight from Delphi 7 or below, this layout might be more familiar to you. 2. Debug Layout. When you run your application with debugging enabled, the IDE automatically swaps to this layout. It has different tool windows visible that are more suited to debugging your application. 3. Default Layout. This is the IDE layout when you have a project open, and is probably the one you would use most frequently. 4. Startup Layout. When you have no project open, this is the active layout. You can choose a layout from the Desktop Speedsetting dropdown in the title bar to be applied to the IDE. The Speedsetting button shows a popup menu.
061
00111110
Once you have arranged the IDE to how you want it to look, use the first option, Save Desktop. This prompts for a name, enter an existing name to replace that desktop, or create a new one.
Here I have altered the IDE so that only the editor is visible and I’m saving the desktop with the name of “Code Only”. I can quickly go back to the default layout by selecting the Default Layout from the Speedsetting combobox.
Notice how Code Only is now an option. You can create as many layouts as you like, and even overwrite the defaults. The next Speedsetting option is Set Desktop, it allows you to change the default layout for when debugging (Set Debug Desktop...), editing a project (Set Default Desktop...), or when no project is loaded (Set Startup Desktop...). You can choose either the Dark, Light or Custom IDE themes or change the syntax highlighting colour scheme via the Editor menu. These will be discussed further in ‘Welcome
062
00111111
to the Dark Side’. Finally Theme Options... allows you to choose the default colour schemes for the Light and Dark themes.
Changing the ToolBar and ToolButtons The main Delphi ToolBar offers quite a lot in terms of customisation. Firstly you can shift around the tool groups by dragging their tool grips. I recommend you line up all the tool groups in a single line beside the main menu, if your screen real estate permits. This gives you slightly more vertical space for the rest of the IDE. You can Right-click on the toolbar and enable/disable various groups (such as the Align, Position and Spacing tool groups that we enabled in Aligning Controls above). Additionally, you can customise the toolbar at the button level (Right-click| Customize|Commands)
From here you can drag and drop commands to the toolbar. Some of the cursor movement is a bit confusing (i.e. not working correctly at time of writing), but if you drop the button where the cursor is pointing and ignore the little button preview, everything works fine. You can reset a tool group (if you mess things up) on the Toolbars tab.
063
01000000
To remove a button, just drag it off the toolbar, and it will disappear. This allows you to remove all the tool buttons you don’t use to make the IDE less cluttered. Alternatively you can add some additional controls. One I often add to the Debug toolbar is Run|Notify on Language Exceptions, which allows you to disable exceptions in the debugger. This is otherwise hard to get to: Tools|Options| Debugger|Embarcadero Debuggers|Language Exceptions|Notify on language exceptions, if you really must know - although IDE Insight can help find it. Another thing that you might like to do is enable the classic tool palette on the toolbar (Right-click|Component). If you’ve recently shifted from an old version of Delphi, you might feel more at home with this rather than the Component Palette tool window. You will get a bit more horizontal space if you remove the Component Palette tool window. Personally I prefer the Component Palette tool window.
The classic Tool Palette
Welcome to the Dark Side It may seem trivial to customise the font and colour of the IDE, but it can have an impact on your productivity - particularly if you are working in the evening or in a dark room (e.g. basement, cave or dungeon, you know, all the usual coding workspaces). Darker background colours are not only easier on your eyes, causing less eye fatigue; they can also help you sleep better. In previous versions of Delphi, it was quite difficult to swap to a dark theme, now it’s just a couple of clicks, by clicking on the Speedsetting button, then selecting Dark.
064
01000001 Light vs. Dark theme in the IDE There are substantially more ways you can customise the editor. To do so have a look in the Editor properties either by Tools|Properties|User Interface|Editor or within the editor, Right-click|Properties. The default font is Courier New, but as far as pre-installed fonts go, I think Consolas is a better choice for readability. A simple Google of “best programming font” brings up lots of suggestions and debate about which font to choose. You also need to decide what point size you want for the font: too small and you may have difficulty reading the code. Too large, and you limit the amount of code on the screen at any one time. Use the Ctrl+[Num+] and Ctrl+[Num-] to adjust the font size in the Code Editor (or the Current editor font size widget at the bottom of the Code Editor, or Right-click|Font size|Increase/Decrease font size). To change the font, go to the Display properties (Tools|Options|User Interface| Editor|Display) and pick the font you want to use. Only mono-spaced fonts are listed. If you don’t like the syntax highlighting scheme that Delphi uses, you can create a custom version on the Color properties (Tools|Options|User Interface|Editor| Display). I’ve found this to be a bit clunky to edit and maintain, so I’d recommend using Rodrigo Ruz’s3 Delphi IDE Theme Editor4. It gives you significantly more themes and allows you to save them to a file. This makes it
065
3 4
https://theroadtodelphi.com/ https://github.com/RRUZ/delphi-ide-theme-editor
01000010
much easier to shift your scheme to different installations of Delphi (either different versions or on other computers). If you are working with multiple versions of Delphi, it can be helpful to have a different theme for each version to help tell them apart.
Write Your Own IDE Plugin Sometimes you need to make the IDE do something that you can’t find in an existing plugin. No problem. In this example, we are going to disable the Insert key in the Code Editor. Usually, Insert toggles between insert mode and overwrite mode. However, I’ve never had an instance where overwrite mode has been useful; whenever I’ve enabled it, it’s been a mistake and has caused me to type over something useful. So, wouldn’t it be nice if we could disable it? Well, we can. We can build a project that uses the Open Tools API, which allows us to control the IDE. Here we are creating a unit that registers a keybinding with the IDE that disables the Insert key. You can find this example in the DisableInsert\ DisableInsert.dpk project.
066
01000011
unit MyBinding; interface procedure Register; implementation uses Windows, Classes, SysUtils, ToolsAPI, Vcl.Menus; type TLearnDelphiKeyBinding = class(TNotifierObject, IOTAKeyboardBinding) private procedure DoNothing(const Context: IOTAKeyContext; KeyCode: TShortCut; var BindingResult: TKeyBindingResult); public function GetBindingType: TBindingType; function GetDisplayName: string; function GetName: string; procedure BindKeyboard(const BindingServices: IOTAKeyBindingServices); end; var LearnDelphiKeyBindingIndex : integer = 0; procedure Register; begin LearnDelphiKeyBindingIndex := (BorlandIDEServices as IOTAKeyBoardServices).AddKeyboardBinding(TLearnDelphiKeyBinding.Cre ate); end; procedure TLearnDelphiKeyBinding.BindKeyboard(const BindingServices: IOTAKeyBindingServices); begin BindingServices.AddKeyBinding([ShortCut(VK_INSERT, [])], DoNothing, nil);
067
function TLearnDelphiKeyBinding.GetBindingType: TBindingType;
01000100
end;
begin Result := btPartial; end; function TLearnDelphiKeyBinding.GetDisplayName: string; begin Result := 'Disable Insert'; end; function TLearnDelphiKeyBinding.GetName: string; begin Result := 'LearnDelphi.DisableInsert'; end; procedure TLearnDelphiKeyBinding.DoNothing(const Context: IOTAKeyContext; KeyCode: TShortCut; var BindingResult: TKeyBindingResult); begin BindingResult := krHandled; end; initialization finalization if LearnDelphiKeyBindingIndex > 0 then (BorlandIDEServices as IOTAKeyboardServices).RemoveKeyboardBinding(LearnDelphiKeyBindingIn dex); end. We also need to house this within a package that we can install into the IDE which looks something like the following (I’ve removed much of the compiler directives - which get added back if you go into Project|Options).
068
01000101
package DisableInsert; {$R *.res} {$DESCRIPTION 'Disable the Insert Key'} {$DESIGNONLY} requires rtl, designide, vcl; contains MyBinding in 'MyBinding.pas'; end. Once we have loaded the project, we can install it into the IDE (Right Click|Install on the project file in the Projects tool window).
You can now test this by going into the Code Editor and pressing the Insert key no more overwrite mode. If you now have a look in the Key Mappings (Tools|Options|User Interface| Editor|Key Mappings), you now see that Disable Insert can be found in the Enhancement modules list.
069
01000110 You can also confirm that the package is installed by looking in Components| Install Packages, you can see it listed as Disable the Insert Key, which is the package description we used above. I will confess that the time working out this piece of code will probably not pay back the time lost to undoing and retyping from being in overwrite mode. However, it was very satisfying to write, and I don’t end up being angry at my keyboard anymore. It might also lead to other ideas, hmmm, I have no Menu key on my laptop - I wonder if I could repurpose the Insert key…
Further Learning The Open Tools API is rather substantial, and I’m not even scratching the surface of what’s achievable. If you want to learn more, then it is worth reading Dave Hoyle book on the subject available at his website DavidHoyle.co.uk5 or the whitepaper6 by Bruno Fierens.
5 6
https://www.davidghoyle.co.uk/WordPress/?p=1143 https://www.embarcadero.com/images/dm/technical-papers/extending-the-delphi-ide.pdf
070
01000111
Language Features Delphi has advanced quite a bit since version 7, but many developers using the latest version of Delphi still write code like it’s 2002. From my experience working with other developers, I think the three most under-utilized language features are: Interfaces (introduced in Delphi 3), Anonymous methods (Delphi 2009), and Generics (also Delphi 2009). There are lots of other language features that are not used as much as they should be (polymorphism would be one that springs to mind). These language features allow you to write cleaner and more readable code (which is a theme of my next book, ‘Code Better in Delphi’), but they can also allow you to write code faster, so I’m introducing them in this book.
Interfaces Delphi has supported interfaces for most of its life, however, few developers take proper advantage of them. They make possible both the Dependency Injection Pattern and the Dependency Inversion Principle (this is the D in the SOLID principles). Both of these are extremely powerful techniques that can help make your code more maintainable. To take advantage of them, you first need to understand how interfaces work, which I’ll cover briefly. Declare an interface like this: type IAnimal = interface ['{C7982869-9293-41C2-8294-4DCE28623435}'] procedure Speak; procedure MoveSlow; procedure MoveFast; end; The GUID (Globally Unique IDentifier - shortcut: Ctrl+Shift+G) is required to identify an interface uniquely, and every interface requires its own GUID (otherwise it would just be an ID) - strictly you don’t need to add one, but if you want to be able to use as and is operators then you need the GUID. Implementing an interface on a class looks like the following:
071
TAnimal = class(TInterfacedObject, IAnimal)
01001000
type protected procedure Speak; virtual; procedure MoveSlow; virtual; procedure MoveFast; virtual; end; TDog = class(TAnimal) protected procedure Speak; override; procedure MoveSlow; override; procedure MoveFast; override; end; I’ve inherited from TInterfacedObject rather than TObject, which I’ve done to implement the minimum requirements for an interface, which are three special methods (_AddRef, _Release, and QueryInterface). TInterfacedObject implements these for you and will look similar to this: TInterfacedObject = class(TObject, IInterface) Protected FRefCount: Integer; function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall; function _AddRef: Integer; stdcall; function _Release: Integer; stdcall; end; The actual implementation will depend on which version of Delphi you are using. _AddRef increments the reference count (FRefCount), _Release decrements it, and if it reaches zero, _Release also destroys the contents of the interface. TDog inherits from TAnimal, and thus also implements IAnimal. All the rules of polymorphism apply in the natural way to interfaces, and interfaces can also inherit from each other. A given class can implement a whole list of interfaces. This gives you a kind of multiple inheritance without many of the confusing headaches that this brings to a language.
072
01001001
Interfaces give you automatic reference counting (ARC), which is a form of automatic memory management. Each time you assign an interface, the _AddRef method is called, and each time an interface goes out of scope or is assigned something else _Release is called. This means that the following code doesn’t leak memory: var Dog : IAnimal; begin Dog := TDog.Create; Dog.Speak; end; When the Dog variable gets assigned a new TDog, the reference count for the object is one, and when the procedure ends, the reference count drops to zero. This causes the TDog object to be destroyed. Don’t worry, no actual dogs are harmed in this example. The lack of try..finally clauses and manual destruction when using interfaces can greatly reduce the amount of code you need to write, this can make code easier to maintain and faster to write. There is often a tradeoff with regards to the difficulty of debugging however. I’ll mention that you shouldn’t mix code containing the object implementation of the interface and the interface itself as strange things can happen. For instance, if you pass a variable of TDog into a procedure that accepts an IAnimal, when that procedure ends, the IAnimal reference count may become zero, in which case it’s freed - this is also what your Dog object is pointing to - so you now have a reference to a freed dog. Off-leash areas only, in accordance with local bylaws, please.
Further Learning This section is an intentionally grossly inadequate explanation of interfaces. Because Interfaces have been around so long, there are plenty of tutorials and reference material if you Google it. There is also a chapter in Coding in Delphi (by Nick Hodges, 2014) on interfaces, which I recommend you check out. Actually, read the whole book, you’ll be a better coder for the experience.
073
Most developers don’t need to write code that makes use of generics directly. Instead, they’ll take advantage of libraries that use them. In particular, the
01001010
Generics System.Generics.Collections unit has several prebuilt containers that can make your code more type-safe and reduce the amount of code you need to write. So what are generics? Roughly put, “it is a style of programming that allows you to write algorithms whereby you specify types at a later time, while still ensuring type safety”. Another way of thinking of this is ‘parameterised types’, just like a procedure can have a parameter that has a value that you pass in, you can also specify the type of that parameter elsewhere. An example. Say we have a procedure that checks an array to see if a particular integer value is present. function IsPresentInArray(Value : integer; AnArray : array of integer) : boolean; var I : integer; begin for I := low(AnArray) to High(AnArray) do if Value = AnArray[i] then Exit(True); Result := False; end; And we can use this function like this: procedure TForm7.Button1Click(Sender: TObject); begin if IsPresentInArray(3, [1,2,3,4,5]) then ShowMessage('yes'); end; If we click on Button1, it would indeed say “yes”. This is a straightforward function, and you might have something similar in a library. However, what if you now wanted to find a string in an array, or a floating-point number, or a TBitmap? You would need to write many overloaded versions of this same function for each of those types - this is lots of repetitive
074
01001011
code. Instead, we can parameterize the type and this is what generics allow us to do. The code would look something like this: function IsPresentInArray(Value : T; AnArray : array of T) : boolean; var I : integer; begin for I := low(AnArray) to High(AnArray) do if Value = AnArray[i] then Exit(True); Result := False; end; If this code actually worked (spoiler - it doesn’t - see below), you would use it in this way: if IsPresentInArray(3, [1,2,3,4,5]) then ShowMessage('yes'); Or if IsPresentInArray('yellow', ['red', 'blue', 'green', 'yellow']) then ShowMessage('yes'); There are two problems here. First, it’s not possible to make a global function or procedure generic. Second, we can’t simply use ‘=’ for equality. To get around the first problem, we can use a class (or record). The second issue requires us to make use of the IComparer interface in the System.Generics.Defaults unit. type ArrayUtils = class class function IsPresentInArray(Value : T; AnArray : array of T) : boolean; end; class function ArrayUtils.IsPresentInArray(Value : T; AnArray : array of T) : boolean; var I : integer;
075
lComparer := TEqualityComparer.Default;
01001100
lComparer: IEqualityComparer; begin for I := low(AnArray) to High(AnArray) do if lComparer.Equals(Value, AnArray[i]) then Exit(True); Result := False; end; Now we can use our function with whatever type we want. For instance: if ArrayUtils.IsPresentInArray(Button1, [Button1, Button2, Button3, Button4]) then ShowMessage('yes'); And (assuming all the buttons have been dropped onto the form), this happily tells us that Button1 is part of the array. However; if ArrayUtils.IsPresentInArray(Label1, [Button1, Button2, Button3, Button4]) then ShowMessage('yes'); won’t even compile, and we get the compiler error message “E2010 Incompatible types: 'TButton' and 'TLabel'”. Thus we now have compile-time type safety. In Delphi 10.3 onwards, in some instances the type can automatically be determined (type inferencing), thus you can omit the so: ArrayUtils.IsPresentInArray('yellow', ['red', 'blue', 'green', 'yellow']); is also valid.
Generic Collections You probably won’t write too much of your own code that uses generics. However, you may consume much code in the form of libraries and frameworks. In particular (as mentioned above) you should make use of the System.Generics.Collections unit and I cover this in the ‘Knowing the RTL’ section.
076
01001101
Anonymous Methods Anonymous methods are mostly ‘syntactic sugar’, you can achieve much of their benefit through other means (function pointers, or even just procedures on classes), but it does give you an additional level of expressiveness that you shouldn’t ignore. Anonymous methods are, well, just as the name suggests, methods without names. You can assign them to variables and you can pass them as parameters. Let’s analyse a contrived and simplistic example. var Greet : TProc; begin Greet := procedure (value : string) begin ShowMessage('Hello ' + value); end; Greet('Alister'); end; First, let's examine the Greet variable; its TProc type is a useful shorthand defined in the System.SysUtils unit, there are several TProc, TFunc, and a TPredicate types defined for various numbers of parameters. type TProc = reference to procedure (Arg1: T); Or in our case, using a string, the Greet variable could instead be defined as: type TGreet = reference to procedure(value : string); var Greet : TGreet; Greet is a variable that is a reference to a procedure, which takes a string parameter. You can see, the TProc shorthand is more concise than its full definition. Back to our original example, we assign a procedure to Greet, and then call that procedure, passing in a parameter. Let’s take a look at another, more useful example.
077
01001110
A typical pattern when using anonymous methods is injecting one method into another. function ForEveryRecord(DataSet : TDataSet; Func : TPredicate) : boolean; var bm : TBookmark; begin result := True; bm := DataSet.GetBookmark; try DataSet.First; while not DataSet.EOF do begin if not Func(DataSet) then Exit(False); DataSet.Next; end; finally DataSet.GotoBookmark(bm); end; end; This procedure iterates over a dataset. You might find yourself doing something like this quite regularly, and writing the same while loop over and over again. Say we want to sum a field called cost on a dataset (cdsMonthlyEarnings in this case) function TMonthEndReport.GetTotalEarnings: Currency; var TotalCost : Currency; begin TotalCost := 0; ForEveryRecord(cdsMonthlyEarnings, function(DataSet : TDataSet) : boolean begin result := True; TotalCost := TotalCost + DataSet.FieldByName('Cost').AsCurrency; end ); result := TotalCost; end;
078
01001111
If the greeting example looked foreign, this example would look very strange. We pass a function inline into a procedure, and each record is iterated over, and the cost field summed. Why the boolean result on the anonymous method? This can be quite handy if, for example, we are searching for a particular record with specific characteristics. If we find one we might want to do some processing, but not iterate over the remaining records (which would return false). For example: function TMonthEndReport.RecordIsValid(ds: TDataSet): boolean; begin //some processing that returns true for valid, and false otherwise result := ds.FieldByName('IsValid').AsBoolean; end; function TMonthEndReport.IsEveryRecordValid: boolean; begin result := ForEveryRecord(cdsMonthlyEarnings, RecordIsValid); end; Here we are passing in a regular function on a class as an anonymous method. If the function returns false for a particular record, ForEveryRecord would stop processing on that record and return false, making IsEveryRecordValid return false.
079
Something unique to anonymous methods is variable capture. This is something that I’ve yet to make use of, but if you are not aware of how it works, it might catch you out. To explain it I’m going to start with an example:
01010000
Variable Capture
function VariableCapture : TProc; var x : integer; begin x := 5; result := procedure begin x := x * 5; ShowMessage(x.ToString); end; end; This function returns an anonymous method which makes use of a local variable, the variable x should disappear when the VariableCapture function terminates. So you might think if the anonymous procedure executes, the x variable points to something invalid on the stack. It doesn’t. var p1, p2 : TProc; begin p1 := VariableCapture(); p2 := VariableCapture(); p1; p1; p2; end; In the above code, we are getting two references to an anonymous method from our VariableCapture function. We call p1 twice then p2 once, and we get the following messages 25, 125, and 25. This is because the anonymous method ‘captures’ the local variable, and ‘remembers’ it between invocations, so each time the p1 procedure is called, it uses the same instance of x. Internally, anonymous methods are implemented with interfaces, so in the above example, when they go out of scope, they are freed automatically - along with any captured variables.
080
01010001
We are only scratching the surface of anonymous methods, but they are very useful, so get motivated to learn more.
Anonymous Threads A common use of anonymous methods is to execute something in a background thread. This allows for an easy way to execute a task without having to worry about creating a descendant of TThread, which can save quite a bit of coding. In the example below, we zip up some files in an anonymous thread, and while it is executing in the background the GUI is not blocked. When it completes, it logs to a memo indicating that it has finished. This could run as part of a loop, zipping up the contents of multiple directories simultaneously (to different zip files obviously). Include the System.Zip unit in the uses section which contains TZipFile needed below. var ZipFileName : string; DirectoryToZip : string; begin {... setup ZipFileName and DirectoryToZip ...} TThread.CreateAnonymousThread( procedure begin TZipFile.ZipDirectoryContents(ZipFileName, DirectoryToZip); TThread.Synchronize(nil, procedure begin memo1.Lines.Add('Zip Complete:' + ZipFileName); end ); end ).Start; end;
Further Learning See the section on the Parallel Programming Library.
081
These features were introduced in Delphi 10.3 Rio, however, at the time Error Insight didn’t support inline variables so they would look like errors even when you used them correctly. However, in Delphi 10.4 Sydney, Error Insight got a big overhaul with the introduction of Language Server Protocol (LSP) support, which makes them usable.
01010010
Inline Variables and Type Inferencing
Inline variables allow you to declare variables when you need them and not at the top of a procedure. Let's take a look at a short snippet of code: procedure TForm15.Button1Click(Sender: TObject); var Files: TArray; FileName: string; begin Files := TDirectory.GetFiles('c:\temp'); for FileName in Files do ListBox1.Items.Add(FileName); end; Here you can see that we are placing a list of all the files in the ‘c:\temp’ folder into a TListBox. We can change this so that we are declaring the variables inline like so: procedure TForm15.Button2Click(Sender: TObject); begin var Files : TArray := TDirectory.GetFiles('c:\temp'); for var FileName : string in Files do ListBox1.Items.Add(FileName); end; Now the variables are declared where they are used. This makes little difference in this simple example, but when writing a much larger method you can create variables as you need them and don’t need to keep declaring them at the top of a method. They also have local scope, so that the FileName variable is only valid within the context of the for loop. When we add type inferencing (which we encountered briefly in Generics above) we can rewrite the code to the following:
082
01010011
procedure TForm15.Button3Click(Sender: TObject); begin var Files := TDirectory.GetFiles('c:\temp'); for var FileName in Files do ListBox1.Items.Add(FileName); end; Now the compiler ‘infers’ the type from its context, allowing us to write much more concise code. Less code equals less typing equals better productivity - at least that’s the theory. Because both these features are new to Delphi, it might take quite some time to determine if our third example is ‘better’ than the first. It’s certainly more concise and less cluttered to look at, but is it easier to maintain? At this stage, I reserve judgement. I suspect it might be the source of some subtle and hard to find bugs (like the with statement), but this is yet to be determined.
083
01010100
Know the RTL The Delphi Run-Time Library is everything leftover when you remove the GUI code and language features. It includes things like the System unit, System.SysUtils, System.StrUtils, System.Classes, System.Generics.Collections, System.RTTI, System.Threading, and so much more (much of the RTL is in the System namespace). Having a good knowledge of these libraries can save you reinventing the wheel, and hence save you lots of effort and time. This section is a teaser to give you a taste of what a few libraries are capable of. See links to resources as to where you can learn more.
Measuring Time Traditionally I’ve done this with GetTickCount, which gives you the number of milliseconds since the operating system started. var ticks: UInt64; begin ticks := GetTickCount64; DoSomething; ShowMessage('Processing time: ' + (GetTickCount64 ticks).ToString + 'ms'); end; Gives the unexciting message
However, there are some problems with GetTickCount (or in this case, GetTickCount64). Firstly what if you want sub-millisecond accuracy? Secondly, this code only works on Windows. Fortunately, we have a simple solution. In the System.Diagnostics unit there is a type called TStopwatch which solves both these problems.
084
01010101
var sw : TStopwatch; begin sw := TStopwatch.StartNew; DoSomething; sw.stop; ShowMessage('Processing time: ' + sw.Elapsed.TotalMilliseconds.ToString + 'ms'); end; Now we get:
Which is very precise, but how accurate is it? We can determine the precision from the Frequency property of the stopwatch, which is the number of ‘ticks per second’ of the operating system. So we can determine the accuracy in milliseconds from: ShowMessage('Maximum Accuracy: ' + (1000 / sw.Frequency).ToString + 'ms'); Which will give us:
This dialog indicates a maximum accuracy of one ten-thousandth of a millisecond (one ten-millionth of a second or 100 nanoseconds) on my current platform, yours may be better or worse depending on your CPU and operating system.
085
The collections unit contains several classes including TList, TQueue, TStack,
01010110
Generic Collections TDictionary and TThreadedQueue along with their object versions (TObjectList, TObjectQueue, TObjectStack and TObjectDictionary). The difference between the Object versions and the standard versions is that the Object version can optionally own the objects. This means that if you remove an object it will be freed. If you free the collection, every object that it owns is also freed. Let’s take TList, for example. The original TList is a list of pointers and allows you to add and remove pointers. For example procedure TForm11.btnAddCustomerClick(Sender: TObject); var Customer : TCustomer; CustomerList : TList; begin CustomerList := TList.Create; Customer := TCustomer.Create(1, 'Jim'); CustomerList.Add(Customer); CustomerList.Add(btnAddCustomer); // ... Customer := CustomerList[1]; // ... end; If you are observant, you’ll notice that we are adding a button to our customer list, then later assigning it to a customer. Rather disturbingly this works quite well as our Customer class is just an integer and a string like so: TCustomer = class public id : integer; Name : string; constructor Create(id : integer; Name : string); end; When I run this, I get a customer with an id of 52892960 and name ‘btnAddCustomer’, which could be a hard-to-spot source of data corruption. If I
086
01010111
free that customer, it frees the button, and it disappears from the form - all with zero errors at runtime or compile time. Historically to fix this you would need to write your own list class (TCustomerList) that contains a TList internally, but all the methods would be type-safe. For instance: procedure TCustomerList.Add(Customer: TCustomer); begin List.Add(Customer); end; Doing this for all the relevant methods is very tedious, especially when you also need a TInvoiceList, TPaymentList, and so on. At least the compiler now gives you an error when we try to add a button to the CustomerList. If you include the System.Generics.Collections unit, we can rewrite our code to: procedure TForm11.btnAddCustomerClick(Sender: TObject); var Customer : TCustomer; CustomerList : TList; begin CustomerList := TList.Create; Customer := TCustomer.Create(1, 'Jim'); CustomerList.Add(Customer); CustomerList.Add(btnAddCustomer); // ... Customer := CustomerList[1]; // ... end; And this time we get an error on compilation: [dcc32 Error] Unit11.pas(56): E2010 Incompatible types: 'TCustomer' and 'TButton'
Using TList gives us the compile-time safety that we desire without all the hard work. This not only enables you to write code more quickly and concisely, but also allows you to spot bugs that can be hard to track down
087
01011000
(especially if the button was called bCustomer, and you wanted to add a CustomerB variable).
TDictionary I’m going to make a special mention of TDictionary because of how useful it is. Before the collections unit, there wasn’t an easy-to-use hash table in Delphi. Hash tables are a fantastic way of storing values based on a key. They are optimised for retrieval speed, although adding or removing an item can be expensive. Generally, hash tables require twice as much memory as a plain list. You especially want to avoid looking up by value as this requires a scan of all the items. I’ve used TDictionary as a replacement or cache for a dataset when I needed the additional performance. I had a calculated field that needed to take a list of codes and expand them out into a list of descriptions. This caused a vast number of locate calls on the dataset that contained the codes and descriptions. Replacing that dataset with a TDictionary resulted in approximately a thousandfold increase in performance, making data grids much more responsive. Dictionaries store key/value pairs, and the key is ‘hashed’ to a 32-bit integer, which is used to determine where to store the value in the dictionary. Below is code for an application to take the symbol for a chemical element and return its name. TfrmElements = class(TForm) // ... private Elements : TDictionary; end; procedure TfrmElements.FormCreate(Sender: TObject); begin Elements := TDictionary.Create; Elements.Add('H', 'Hydrogen'); Elements.Add('He', 'Helium'); Elements.Add('Li', 'Lithium'); Elements.Add('Be', 'Beryllium'); Elements.Add('B', 'Boron'); Elements.Add('C', 'Carbon');
088
01011001
//... end; procedure TfrmElements.FormDestroy(Sender: TObject); begin Elements.Free; end; procedure TfrmElements.btnLookupSymbolClick(Sender: TObject); var Name : string; Symbol : string; begin Symbol := edtSymbol.Text; if Elements.TryGetValue(Symbol, Name) then ShowMessage(Name + ' has the symbol ' + Symbol) else ShowMessage('Could not find an element with the symbol ' + Symbol); end; Below we are confirming that Helium has the symbol He
The performance difference in this example between TDictionary and using a TStringList is probably not going to be noticeable as there are only just over 100 elements on the Periodic Table. However, on a dataset with a million items using a TStringList would be painful, yet a TDictionary with 1,000,000 items would be just as fast as with 100 items.
089
01011010
The other advantage is that you can use any type for the key and value that you like; for instance simple types such as integers, strings or doubles, or more complex types such as classes, records, arrays or interfaces. Be aware not all keys are created equal. For instance, two records with the same contents would not be considered equal; for this, you need to write a custom comparer to compare their fields.
Further Learning The documentation for generic collections is pretty good, so I haven’t felt the need to reproduce it here. You can watch more extensive coverage of the collections unit in one of my videos on LearnDelphi.tv.
Parallel Programming The System.Threading unit makes writing multi-threaded code much more straightforward. It’s still challenging. I don’t think it’s ever going to be easy, but we’ll take whatever simplification we can get. I’m not going to use the boring old parallel programming example of calculating primes, we’re going to calculate twin primes. So much more interesting. Twin primes are primes that are separated by two, for example, 3 and 5 are twin primes (as are 5, 7 and 11, 13). We are going to calculate every twin prime under ten million. Our user interface is going to be a memo, some buttons and an ActivityIndicator (set its animate property to True). You can find this example in the PrimeExample\PrimeExample.dpr project.
The ActivityIndicator shows us when the GUI is unresponsive (the indicator will stop spinning).
090
01011011
We need a function that determines if a number is prime: function TfrmPrimes.IsPrime(const x: integer): boolean; var I: Integer; MaxValue : integer; begin MaxValue := Round(Sqrt(x)); for I := 2 to MaxValue do if (x mod i) = 0 then exit(False); result := True; end; We can define a procedure to calculate all the twin primes in a given range procedure TfrmPrimes.TwinPrimes(LowBound, HighBound: integer; Output: TStrings); var I : integer; begin for I := LowBound to HighBound do begin if IsPrime(i) and IsPrime(i+2) then OutPut.Add(i.ToString + ', ' + (i+2).ToString); end; end; This is certainly not the fastest algorithm (I could skip even numbers, cache prime results, take advantage of the properties of twin primes, etc.), but suits our purposes making the CPU busy. Now that we are armed with these sophisticated prime calculating algorithms (cough), we can look at some threading examples, starting with just using the main thread.
No Parallel Example For our example where we do nothing in parallel, I’ve written:
091
var sl : TStringList;
01011100
procedure TfrmPrimes.btnNoParallelClick(Sender: TObject);
sw : TStopwatch; begin sw := TStopWatch.StartNew; sl := TStringList.Create; TwinPrimes(2, MaxPrime, sl); sl.Add(sw.Elapsed.TotalMilliseconds.ToString); ShowResult(sl); sl.Free; end; If I execute this code, it freezes the application for about 10 seconds (9,709.0126ms for my last run), but it does successfully calculate the 58,980 twin primes under ten million (MaxPrimes is a constant that’s set to 10,000,000). To display the primes in the memo, we use the ShowResult procedure: procedure TfrmPrimes.ShowResult(sl : TStringList); begin TThread.Synchronize(nil, procedure begin if cbFinalTwinPrimeOnly.Checked then begin mmoTwins.Clear; if sl.Count MineSweeperEngine MineSweeperFMForm -> MineSweeperSettings MineSweeperFMForm -> MinesweeperSoundInterface MineSweeperFMForm -> MinesweeperAudioManagerSound MineSweeperFMForm -> MineSweeperFMSettings MineSweeperFMXSound -> MinesweeperSoundInterface MinesweeperAudioManagerSound -> AudioManager MinesweeperAudioManagerSound -> MinesweeperSoundInterface MineSweeperFMSettings -> MineSweeperSettings } Once rendered in Graphviz, it looks like the following:
This uses graph is from my MineSweeper application (FireMonkey version). The above graph is quite instructive for this small application, and it clearly shows the unit dependencies within the project. However, for any significant application, the graph can be large, confusing and difficult to see anything. If you find Graphviz too limiting try Gephi16. Writing an application to parse the uses of a project is a very non-trivial exercise, but if you take advantage of Delphi AST, it becomes much easier.
DFM Parser I’ve used Robert Love’s17 DFM parser several times (uDFMParser.pas). It allows you to manipulate a DFM (or compatible) file. In the past I’ve used it to change components and properties, for example, you might want to convert fonts from using MS Sans Serif to Tahoma, but also increase the point size by 1 - which is pretty hard to do with search and replace. I’ve also used it to change components types, with adding and removing properties, the PAS file can then be updated with a simple ‘search and replace’ if required. What the code allows us to do is to convert the contents of a text-based DFM file into a tree (using the ObjectTextToTree procedure). We can then manipulate
141
16 17
https://gephi.org/ http://robstechcorner.blogspot.com/
ObjectTreeToText).
10001110
that tree using its various properties and save it back to a DFM file (using
The example procedure below is from a project that tidied up after a BDE to FireDAC migration. The original BDE application used a BDE Alias on all the TQuery and TTable components, these were converted to TFDQuery and TFDTable, and the Alias was converted to a ConnectionName property (which is a string). However instead I wanted to use a centralised TFDConnection, so the code searches through all the components to find the ones I want to be changed and update the Connection (a string) to a ConnectionName (an identifier - which refers to a specific control). You’ll find this sample as part of the DFMParser\DFMParser.dpr project, note that this application is exactly what I used to migrate all the forms in a project to work better with FireDAC. It was also executed as the final stage of a much larger BDE replacement script (you can use the /DFM command-line parameter to execute automatically). You won't find it useful “as is” but feel free to customise it to suit. You can also see that I also remove unsupported properties, and then recurse into any children objects that DFMObj owns.
procedure TfrmMigrateDFMs.ProcessDFMObject(DFMObj: TDfmObject); var I: Integer; p : TDfmProperty; begin if MatchText(DFMObj.DfmClassName, ['TFDQuery', 'TFDTable']) then begin for I := 0 to DFMObj.DfmPropertyCount-1 do begin p := DFMObj.DfmProperty[i]; if SameText(p.PropertyName, 'ConnectionName') then begin //we want to change all ConnectionNames to a Connection p.PropertyName := 'Connection'; p.PropertyType := ptIdent; p.StringValue := 'dmFireDACStandard.Connection'; end;
142
10001111
end; for I := DFMObj.DfmPropertyCount-1 downto 0 do begin //remove properties that don't exist on TFDQuery or TFDTable p := DFMObj.DfmProperty[i]; if MatchText(p.PropertyName, ['FieldDefs', 'StoreDefs', 'IndexDefs']) then DFMObj.RemoveDfmProperty(p); end; end else begin //recurse for all children for I := 0 to DFMObj.OwnedObjectCount-1 do ProcessDFMObject(DFMObj.OwnedObject[i]); end; end;
reFind This is a command-line tool that allows you to make certain changes to Delphi source files based on a script file and/or command-line options. A script file allows you to add and remove units from the uses clauses and add, remove and replace properties on classes. I found that reFind was pretty slow, so if you are going to be running it a lot, you should only run it across files that need to be changed (rather than every file in a project). I did this by running it on every file in the project, then using SubVersion to tell me which files had changed, then produced a batch file to migrate only those files. The reFind documentation18 is pretty good, so I probably don’t need to reproduce it here - but a simple example might be instructive. Let’s say that we want to migrate every TButton in an application to TSpeedButton. We need to make a script file with some rules in it. Firstly we want to convert the classes across and add the unit that TSpeedButton is in (Vcl.Buttons), so we need a rule: #migrate TButton -> TSpeedButton, Vcl.Buttons
18
143
http://docwiki.embarcadero.com/RADStudio/en/ ReFind.exe,_the_Search_and_Replace_Utility_Using_Perl_RegEx_Expressions
run into problems. Firstly TButton has a TabOrder property that TSpeedButton does not, so we need to remove this:
10010000
This converts across all the controls in the DFM and PAS files, however, we might
#remove TabOrder However, lots of controls have a tab order property, and unfortunately, there is no way to constrain the remove command to a single class, so a bit of hackery is required. Instead, we could use the rule: #migrate TButton:TabOrder -> TButtonTabOrder This line converts the TabOrder property to the nonexistent TButtonTabOrder property, which we can subsequently remove. We can save our script in the same directory as our project file calling it migrate.txt, which will look like the following #migrate TButton:TabOrder -> TButtonTabOrder #remove TButtonTabOrder #migrate TButton -> TSpeedButton, VCL.Buttons Notice that I’m changing the TabOrder property first as there won’t be any TButtons if we do it the other way around. We can upgrade our project on the command line using: refind *.dfm *.pas /X:migrate.txt The more astute of you may have noticed that TButton has several other properties, such as ButtonKind and ModalResult, which we could also remove in a similar way. The conversion scripts can be quite long and powerful. There are some samples, for example, the BDE to FireDAC script which you can find in: C:\Users\Public\Documents\Embarcadero\Studio\21.0\Samples\Object Pascal\Database\FireDAC\Tool\reFind\BDE2FDMigration Or whatever the equivalent directory is for your version of Delphi. I have one other example. If you use the MessageDlg function, but don’t include the System.UITypes unit, you will get a compiler hint:
144
10010001
[dcc32 Hint] ButtonForm.pas(31): H2443 Inline function 'MessageDlg' has not been expanded because unit 'System.UITypes' is not specified in USES list If you are updating a legacy application, you might get this message over a great many units. A quick solution I found was to run the script: #migrate MessageDlg-> MessageDlg, System.UITypes Which adds the System.UITypes to every unit that uses the MessageDlg function.
Mida Converter You can think of Mida Converter19 as a much more powerful version of reFind. It’s a commercial product which I’ve used to convert a VCL application to FireMonkey mobile. This is perhaps a bit of an overstatement, Mida Converter allows you to convert VCL code over to its FMX equivalent. I’ve found this to work reasonably well; however, a considerable amount of effort is still required to make each form mobile friendly - I found that the conversion saved me a significant amount of time over building the application from scratch.
To use Mida Converter, you specify a directory containing the VCL source code you want to migrate and a destination directory where you want the FireMonkey conversion to be placed. Many VCL and third party components are supported, and you can add your own custom conversions.
145
19
http://midaconverter.com/
The property corrector allows you conditionally alter properties on the current form, project or project group. Let’s say you want to convert labels to use Segoe UI font rather than Tahoma, and if the font size is less than 10, increase it to 10. We would open the property corrector dialog (cnPack|Property Corrector), go into the options and add two new rules (I’ve removed the default rules that were present).
10010010
cnWizards Property Corrector
We can then click OK to go back to the wizard and click Forms in Current Project then Search. This searches your entire project and gives you a list of results that it found.
You can then click Confirm All if you are happy to make all the changes. If there are a few that you didn’t want to be modified, you can Right-click and select Undo. Alternatively, you can Right-click and confirm individual changes or navigate to the control.
146
10010011
GExperts Replace Components If you have GExperts installed, you can use the Replace Components dialog (GExperts|Replace Components). This dialog allows you to convert all the controls of a particular type into something else. Let’s, for instance, convert a TLabel to a TEdit. First, we need to define some rules in the Settings for Replace Components (GExperts|Replace Components|Settings). The first rule we want to add is to convert the Caption property of the TLabel, to the Text property of the TEdit.
You can add subsequent rules by pressing the green + button. For the next two properties, we want to Assign constant value, firstly set color to -16777211 (which is clWindow) and height to 25.
147
10010100 We can then right-click on a label, select Replace Component and change our TLabel with a TEdit.
It automatically converts over the caption property and updates the height and colour.
This has made for a rather short edit box, so we might also want to do something about the width.
148
10010101
Your Physical Environment You are a squishy meat-sack existing in this physical world, but you need to interact effectively with that non-corporeal world where we create software which is a great excuse to purchase several cool and shiny tools, as well as think of what your body needs.
Hardware Hardware is the stuff you touch, which can significantly influence your productivity.
Keyboard Mechanical keyboards are generally preferred for typing, and it is what’s allowing me to bang out this text super quickly. Bang is probably not quite the right word - but they can be a bit noisy, if you are working in an open-plan office you need to consider co-workers, perhaps opting for a less ‘clicky’ keyboard. Most mechanical keyboards allow you to replace the keycaps. If you want to be super cool, you can replace all the keys with blank keycaps - effectively defeating non-touch typists from using your computer. Nothing spells 313373 like a completely jet black keyboard. This won’t make you faster if you can’t touch type, but you’ll look cool! All else being equal, mechanical keyboards feel good and are fast to type on.
Mouse I like to use a gaming mouse as they have adjustable sensitivity, are pretty high precision (yup, justification!), although generally, I’m happy using anything that has two buttons and a wheel (honestly - we are trying to reduce mouse usage after all). I also like to have a soft gaming mousepad, which makes the mouse slide better and more quietly. It also stops it from picking up whatever that goo is from a hard surface. I don’t think I’d ever want to go back to cleaning a ball mouse (ahh, the good old days). Now it’s lasers all the way, baby!
Computer You want something that you don’t have to wait for, preferably something with excellent single-threaded performance (i.e. lots of GHz / IPC) as lots of programming tasks can’t be parallelised, and good multicore performance (lots of cores) for those tasks that can. I like a desktop machine to do most of my work, and a laptop for when I’m ‘out and about’ or lazing on the couch. A fast
149
10010110
hard drive is also essential. I remember when I first moved from a ‘spinny’ mechanical hard disc drive to an SSD - it was a significant improvement in compile times due to the large number of small files involved (a bit of a killer for a mechanical drive).
Screens I’m a big fan of multiple monitors, and I’ll usually use three where I can. I find a sizeable primary monitor and two cheaper monitors in landscape mode either side to be optimal. I’ll do all the development on the primary screen and have various web pages and documentation on the others. These days you can get good monitors at a reasonable price. I’ll show my vintage recalling the days when a 23 inch Sony Trinitron tube was king (with its price and weight proving it!).
Chair Make sure you have something comfortable that you can sit in for long periods. I have an old Aeron chair that I quite like - but there is a lot of personal preference here. I think mesh chairs are better for long term work than foam padded chairs as they ‘breathe’ better. Chairs come with an ‘hour rating’ for how long sitting in them is comfortable. The longer the better, although you really shouldn’t do 8 hours sitting at a time, get a chair that makes this bearable when you do. You can also try using a swiss ball to sit on, for the ultimate challenge try kneeling on one while coding.
Desk Generally, I don’t need a huge amount of room to write code, but I like to have enough width for my expanse of screens, and enough depth for mouse & keyboard, plus some room for doodling diagrams when I need to do so to understand things better, or drafting a solution to some problem. Desks should be the right height for you, your chairs and your screens, and appropriately sturdy. Consider a standing desk as they are currently considered healthier, but understand you need to build up your tolerance to them if you haven't used these before, starting with short stints and building up over weeks.
150
10010111
Other Considerations Environmental Having some peace and quiet can certainly help. Music has been proven to be distracting and reduce productivity. Music without lyrics is generally less distracting, but sometimes I like to listen to music while I code - particularly if it’s something I don’t particularly want to write. This might be a problem if you are in an open-plan office. So a good pair of noise-cancelling headphones can go a long way to remove any background sounds and allow you to concentrate (while allowing a proper appreciation for the music of your choice). Consider ‘white noise’ as your background track for coding - it is very effective at minimising distracting sounds, and you can download many different Apps to try these. The shush of constant rain sounds might even be relaxing for you.
Interruptions These can hurt productivity, particularly if you are ‘in the zone’. It can be quite hard sometimes to get into the rhythm of solving a complicated problem, and the last thing you need is someone asking you a bunch of questions that they can answer for themselves with five minutes of Googling. Arrange with those around you what signals you all should use for ‘leave me alone, I’m in the zone, only let me know if the building is burning down’. Refrain from using it all day every day - people around you likely need your input to increase their productivity too, so set up fair rules and schedules so everyone benefits. It’s also good from time to time to get out of your zone for a bathroom break and to refuel the body, and the subconscious mind often keeps turning over a problem even when you are not actively ‘on task’, so chatting with a colleague about their problem might be just what the doctor ordered to make a breakthrough. A phone placed on do not disturb, or even in another room, can be very helpful. Set up your voicemail to state when you will be available to take calls again, or who they can contact if an immediate answer really, and I mean really, is needed. Turning off pop up notifications on your email or social media will help reduce distractions, however fleeting they may be. Productivity experts recommend scheduling time to deal with emails all at once, say every 2 hours, rather than as each one comes in. This can be hard to do if it is counter to your team culture, but worth considering.
151
While multicore CPUs are great at this, your mind can only focus on a single task at a time. Sure, in the background, your unconscious can be solving something, but your conscious attention can only be focused on one task at a time. So if you think you’re multitasking, you’re not, what you are doing is task switching. And each time you switch between tasks, it takes your brain a certain amount of time to load all the relevant information, and this process can be quite time expensive.
10011000
Multitasking
Quit pretending you can multitask and deal with just one thing at a time. Start by implementing the suggestions above and see what kind of difference it makes to your concentration and output.
152
10011001
Sharpening the Saw This is the seventh habit of highly effective people (according to Stephen Covey) and is a metaphor for self-improvement or getting better at something. Sometimes it’s better to stop and sharpen the saw, rather than to continue grinding away with a dull saw blade. Sometimes even a small insight can make a complicated task much easier, but where can these insights come from?
Where to go when you are Stuck Programming is a process of continuous problem solving, most of these problems you solve without even thinking, others drive you insane as you keep going round and round in circles as you try to find a solution. Here are some suggestions for you that will hopefully prevent you from wasting time on a problem that someone else has already solved (or knows how to solve).
Google is Your Friend Google is the first place I go when I’m stuck. However, working out what to search for is quite an art. You want to include enough keywords so that you get relevant search results, but not too specific that you miss out. For instance, if there is a part of your application that is not fast enough, and you need some way of working out what to improve. You might start with “Delphi Faster” as your first search, however, this might be too general and “Delphi Execution Performance” might get you more specific results. Often one search leads to another, in the results you see something about profilers, so you might then search for “Delphi Profiler”. Not all search results are created equal. You want to zero in on the ones that solve your problem and ignore results that have no relevance. I’ll prioritise search results from the Delphi DocWiki, Stack Overflow, and many other sites that I’ve found quality information from previously. It is worth noting the date of the result - older results might not be taking advantage of the latest features of Delphi. There are many ways you can do more powerful searches. You can place a hyphen before a word to exclude it, or quote a word (or phrase) to indicate that it must be present. There also binary operators (AND which is the default, OR or |), and ways of searching within specific file types (for example, you knew what you wanted
153
10011010
was contained within a PDF), and plenty more - check out the advanced search page20.
Asking Questions Sometimes you can’t find an answer to your question. If you are part of a development team, then asking a team member for help would be the next step. This is something you need to be very considerate of, as interruptions can harm productivity. An email is less intrusive than an instant message which is less intrusive than face to face. However, being more intrusive will likely get you an answer quicker. So you are going to need to do a mental calculation regarding your productivity vs your co-workers. See ‘Interruptions’ above about setting up some signals with your team as to when is a good time to ask for help. If you are a member of an online community (I belong to both the New Zealand and Australian Delphi Users Groups), you can ask there. It’s often helpful to be able to ask people that you already know and that are in the same or similar time zones to you.
Stack Overflow If your searches turn up fruitless, then asking in a forum is your final step. My favourite place to ask is stackoverflow.com. Stack Overflow is a huge question and answer site, with millions of members and I would recommend that you become one of them if you are not already. While you have full access to the site without being a member if you want to ask a question, you need to join. I’ve found Stack Overflow to become a pretty harsh place, and you need to read the rules, or they get pointed out to you rather severely. That aside, there are a large number of Delphi developers willing to answer questions that belong to the site. It is worth taking the time to formulate your question so that it is clear and unambiguous (and is in the form of a question). The people answering are volunteers, so you don’t want to waste their time, and conversely, if it is not a good question, they may ignore it (or downvote it). Sometimes you can get an answer in minutes, other times it might take days - or never be answered.
Recommended Reading Throughout this book, you have seen me recommend many books, here is a complete list of books that I recommend you read, it will probably develop over time https://learndelphi.tv/books
20
https://www.google.com/advanced_search
154
10011011
Social Networks There are many Delphi groups that you can join on various social networks. Posting on these is a great way to build your reputation within the Delphi community. Improving your reputation can lead to additional career opportunities or access to people and resources that are not available to your “average” developer.
Facebook There are several groups that you could potentially join and people you could follow. A good starting point would be to join the Delphi Developer21 group and follow Embarcadero Technologies22. There are many other great developers you can follow. I primarily use Facebook for following people that I actually know and have met.
LinkedIn LinkedIn is far more business-focused, and I'm connected to significantly more Delphi developers on it than Facebook. This is an excellent way of building a network and gaining lots of industry-specific knowledge. You can join my network (linkedin.com/in/alisterchristie), and let me know that you’ve read this book. There are also three English Delphi groups (and others for different languages) Delphi and Pascal Developers Group23, Delphi Professionals24, and Embarcadero Technologies25. I also belong to NZDUG and ADUG groups, which you should join if you are in the Australia / New Zealand area - there might be other groups relevant to your area.
Twitter I’m not a big twitter user but occasionally dip into the firehose. It can be a great source of up to the second, unfiltered information, and there a large number of Delphi developers that you can follow, me included on twitter.com/AlisterChristie. #CodeFasterInDelphi #CodeBetterInDelphi #LearnDelphiTV
Meetup Meetup is a way of organising in-person events. You can create or join a group of other like-minded individuals in your area. The meetup can be as simple as a
155
21 22 23 24 25
https://www.facebook.com/groups/137012246341854/ https://www.facebook.com/EmbtDelphi/ https://www.linkedin.com/groups/1290947/ https://www.linkedin.com/groups/101829/ https://www.linkedin.com/groups/2551723/
10011100
group meeting at a bar or restaurant and just ‘shooting the breeze’, or a full-on presentation. It is often possible to find a sponsor for the event to provide or cover the venue and perhaps pizza and beer. In the past, I’ve run Delphi meetups. You can too, and it’s a great way to meet other Delphi developers.
YouTube While not a social networking site, it does allow you to subscribe to many YouTubers so that you can be notified of videos that may have relevance to you. You can follow me (youtube.com/user/codegearguru) or many others. In particular, Embarcadero26 and Quark Cube27 are good places to start.
StackOverflow I’ve already discussed this as a site to get answers to questions. The site is based on a reputation system, where it’s members can up or down vote questions and answers. If you provide a good question or answer, it may get upvoted, increasing your reputation, with the reverse also being true. As your reputation increases, you get additional privileges and badges for certain achievements. Because Stack Overflow is so well known, this reputation can be used on your résumé / CV.
Delphi-PRAXiS For many years this was (and still is) a great German Delphi forum - for which Google Translate was my friend. In the last few years, the English version of the site has taken off: en.delphipraxis.net.
Becoming Known as an Expert This is certainly not for everyone, and many fantastic developers don’t participate in the community and quietly work on projects churning out lots of great code. However, I would encourage you to become engaged in the community. This might be as simple as commenting on a blog post or YouTube video, or as extreme as you writing a book or producing a video course. There are lots of levels in between others might include Creating a blog Answering a question on Stack Overflow Joining a community Posting on Facebook or LinkedIn 26 27
https://www.youtube.com/c/EmbarcaderoTechnologies https://www.youtube.com/user/QuarkCube
156
10011101
Tweeting Reviewing a book on Amazon Attending or hosting a local meetup Liking or Commenting on a post Creating a YouTube tutorial Make a start, however small, and make a regular contribution.
What’s Improved Productivity Worth Something that I find amazing at many of the organisations that I’ve worked at has been the lack of resources put into developing their developers. This is something that should not only be encouraged but mandated.
As an Employer If you can raise a developer’s productivity by 10%, what might this be worth? Let’s run some numbers. Let’s say a developer has a salary of $100k, and costs a further $50k in expenses, so the total cost of that developer is $150k. Let’s say that the developer brings in $200k worth of value, therefore $50k profit. If we increase that developer’s productivity by 10%, how much is that worth? Well, they now bring in $220k, their salary and fixed costs remain the same, so their profitability has gone from $50k to $70k - a 40% increase in profit! Suppose by investing in a developers productivity you could double their productivity to $400k, at even double their fixed costs ($50k becomes $100k), that’s increasing the profit from that developer by 400%, which is a truly impressive amount, and a great return on investment in anyone’s book. Can you increase a developer's productivity by 10% or more? Well given how much I see the average company invests in professional development and tools for their developers I would say this could be achieved with trivial, and low cost, adjustments. Having a good computer and screens, having administrative access to their local machine, and allowing them to choose their tools are all low cost and any one of them could easily achieve this. Even buying a few books might be enough to have a significant breakthrough, along with the time to read and implement their learning. Several innovative companies have ‘20% time’ in which the team can work on their own projects (usually something in the companies interest) or professional development. In our hypothetical example here, this would cost the company $20k per annum, or 40% of the current profit. If the return on that was more productive team members who stay longer and have more job satisfaction, what could that add to the profit line? Doubling productivity is challenging, but given these numbers, an organisation can spend vast amounts of time and money on improving their developers and
157
10011110
still be well ahead, but I think regular investment, week on week on skills and infrastructural improvement will do the trick.
As an Employee On the flip side, if you are paid a salary, a company is generally not willing to pay twice as much for a developer who is twice as good - but is usually willing to pay more. It is also great to work somewhere where your skills are both valued and cultivated. If you don’t feel valued, then this might be as simple as discussing your needs with your manager or as hard as changing jobs. Another option is to start a side project or business. You don’t necessarily need to make money directly from the project, for instance, working on an open-source project can add to your experience and profile, and you might be able to monetise it somehow (via advertising revenue, support, or a Pro version). I’ve had several side projects over the years, for instance, the LearnDelphi.tv website and making commercial videos. They’ve all been worth doing. Even using paid work or side projects to fund other investments, for example, stock markets or investing in real estate is worth doing - nothing gives you the ability to pick and choose your fate as financial freedom. Your day job probably won’t make you rich, but that killer app might!
Self Employed If you work for yourself, then any improvement in productivity can go straight into your pocket. This is particularly true if you are selling a product. This could just be improving your existing product faster, or that extra productivity could mean that you have time to develop additional products. Being able to code once and sell it time and again is an excellent way to leverage your time and expertise for profit. Whether that is a product you sell, or licence, or just code you can utilise for different clients (for example, a booking and lead generation system for a hairdresser can be adapted for other hairdressers, or even for dentists and so on). Your options are only limited by your ability to think of them.
Diminishing returns on investment Getting your first 10% improvement in productivity is usually pretty easy, the next is slightly harder, and the next harder still. The ‘harder’ may be a financial cost, time cost, bureaucratic cost, willingness/resistance, or limits of your imagination. It isn’t always possible to fully optimise something, or worth it to
158
10011111
do so. Generally speaking, pick the low hanging fruit first - that is, do what is easiest for the most return on investment.
Further Learning This kind of what this whole section is about, but I’d recommend The Complete Software Developer's Career Guide by John Sonmez (2017), I listened to the audiobook version and found it excellent.
159
10100000
Final Words and Conclusion Congratulations you’ve reached the end of this book. I hope that you’ve enjoyed reading it and learnt many great ideas for being more productive in Delphi. This doesn’t need to be the end of our journey together. Check out my book - Code Better in Delphi to improve the code you produce which you can order via www.learndelphi.tv Additionally, I’ve prepared additional material for you to get even more out of your journey to being a faster Delphi developer. You can get the ‘super-secret’ bonus material from: LearnDelphi.tv/faster
Have any coding or productivity tips you think I’ve missed? Have you spotted a mistake in this book? Continue the conversation by contacting / following me on... Email: [email protected] YouTube: https://www.youtube.com/user/codegearguru Web: https://LearnDelphi.tv LinkedIn: https://linkedin.com/in/alisterchristie/ Twitter: https://twitter.com/AlisterChristie Facebook: https://www.facebook.com/LearnDelphitv/ Instagram: https://www.instagram.com/christiealister/ I look forward to hearing from you as you continue your Delphi journey.
end.
160