SetFocus is broken in Delphi

SetFocus is broken in Delphi. Has been since v1 and there are no signs that it will be fixed soon. What does it mean, broken? Do I really have to explain it? We all know. Try to set focus on a visual control that is disabled or is invisible or simply is not focusable, and the program will crash. Of course, we can try to call CanFocus before we actually set focus, that that function is flawed also.
And because recently I worked on a legacy project where SetFocus was really abused (in 4200 places) I create a safe alternative for Delphi’s SetFocus and a tool to do the batch replace.

The safe alternative looks like this:

function CanFocus(Control: TWinControl): Boolean;
Result:= Control.CanFocus AND Control.Enabled AND Control.Visible;
if Result
AND NOT Control.InheritsFrom(TForm)
then Result:= CanFocus(Control.Parent); { Recursive call: This control might be hosted by a panel, which could be also invisible/disabled. So, we need to check all the parents down the road. We stop when we encounter the parent Form.
Also see: GetParentForm }
procedure SetFocus(Control: TWinControl);
if CanFocus(Control)
then Control.SetFocus;

As we can see, the work is actually done in CanFocus. Here we check for all possible causes of “accidents”. One important thing to know is that we should not try to focus a control if it is hosted by a disabled control. Therefore, we need to do a recursive check on all parents of our control, down the tree until we hit the parent form. Here we stop.

The program that is doing the batch replace can be found here:
It looks up for all lines of code that contain something like


If a line is found, it extracts the name of the control and put it inside the new SetFocus function:


Update: I updated the program to automatically add the name of the unit where the new SetFocus is implemented to the “uses” clause. With this change, we can truly do a full batch replace and compile the program, like nothing happen.


The code


The code in the main form is like this (simplified code):

procedure TfrmMain.btnSearchClick(Sender: TObject);
var SearchResult: TSearchResult;
FileList:= ListFilesOf(edtPath.Text, edtFilter.Text, True, True);
for TextFile in FileList do
SearchResult:= FindSetFocusInFile(TextFile, bReplace);

if SearchResult.Found
lbxResults.AddItem(SearchResult.FileName + ' Found at: '+ SearchResult.PositionsAsString, SearchResult);

Caption:= 'Searched '+ IntToStr(FileList.Count)+ ' files. Found: '+ IntToStr(Replaces)+ ' times in '+ IntToStr(FoundFilesCount)+ ' files.';

The heavy listing is done by FindSetFocusInFile in cFindInFile.pas. It is looking for the SetFocus keyword and method it with the SetFocus() function.

ListFilesOf is a function from ccIO.pas that returns all PAS files in the specified folder and SearchResult is an object that stores how many files contained the SetFocus methods and on which line.


Note: I provided an already compiled EXE file. However, if you want to compile the program yourself, you need the Delphi LightSaber library.

Leave a Comment

Scroll to Top