Numeric edit control with flat button inside and no calculator - delphi

I'm writing a C++ project with RAD Studio, but this also applies to Delphi.
I need an edit control where user can only enter floats (2 decimal places) and can restore the original value (taken from a variable, not important here) clilcking on a button (actullay an icon) inside the edit control itself.
This is what I've done, using a TJvCalcEdit from JEDI library.
Control definition:
object Sconto1: TJvCalcEdit
[non-important attributes...]
ButtonFlat = True
Glyph.Data = {
D6020000424DD6020000000000003600000028000000100000000E0000000100
180000000000A0020000130B0000130B00000000000000000000FFFFFFFFFFFF
FFFFFFFFFFFFFFFFFF999EC29396C3FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9198C48694CBA7BAFE8493CA72
75B9FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8B96C5
8695CBA7BAFEA7BAFEA7BAFEA7BAFE747EB66D71B5FFFFFFFFFFFFFFFFFFFFFF
FFFFFFFFFFFFFFFFFFFF8493CAA7BAFEA7BAFEA7BAFEA7BAFEA7BAFEA7BAFE84
93CA7E83CE6D71B4FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8F94C3A7BAFE
A7BAFEA7BAFEA7BAFEA7BAFEA7BAFE8492CA8288D27B7FCA6D71B4FFFFFFFFFF
FFFFFFFFFFFFFFFFFFFFFFFFFF8492CAA7BAFEA7BAFEA7BAFE828ECA7B82C993
96FA6D6FB67B7FCA7B7FCA6D6FB4FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9299C2
A5B7FE7E88CA787DC99396FA9396FA9396FA9396FA6D6FB67B7FCA7B7FCA6D6F
B4FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7678C89396FA9396FA9396FA9396FA93
96FA9396FA9396FA6D6FB67B7FCA7B7FCA6C6FB3FFFFFFFFFFFFFFFFFFFFFFFF
FFFFFF7678C89396FA9396FA9396FA9396FA9396FA9396FA9396FA6D6FB67B7F
CA7B7FCA7576B0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7678C89396FA9396FA93
96FA9396FA9396FA9396FA9396FA6D6FB67B7FCA6266A2D6D0E2FFFFFFFFFFFF
FFFFFFFFFFFFFFFFFF7678C89396FA9396FA9396FA9396FA9396FA9396FA9396
FA6D6FB67B7FCA7C7EB0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7678C893
96FA9396FA9396FA9396FA9396FA9396FA9396FA7679C66B6DACFFFFFFFFFFFF
FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7678C89396FA9396FA9093F58B8EEC7678
C87C7FC6ACABE5FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
FFFF7678C88283C5A4A4E7C4C1EBFFFFFFFFFFFFFFFFFFFFFFFF}
ImageKind = ikCustom
DecimalPlacesAlwaysShown = False
OnButtonClick = EScontoButtonClick
end
Method called on button click:
void __fastcall TFRigOrd::EScontoButtonClick(TObject *Sender)
{
TJvCalcEdit* edit = dynamic_cast<TJvCalcEdit*>(Sender);
edit->Value = oldSconto1;
}
The problem: at the end of this method a calculator popup appears below the control, requiring an action by the user. I don't want this to happen because I'm changing the value programmatically. I guess it's a default value due to the fact that such button is made for triggering the calculator. Moreover the value you see (255) appears without decimal point, with will be shown only once the calculator is closed.
So, can I disable this behaviour? Or can someone suggest me a solution with another control (standard, open source or free anyway)?

I'd use a TButtonedEdit to get the button, and to enforce floating-point input with a maximum of two decimals after the point, I'd do
TButtonedEdit = class(ExtCtrls.TButtonedEdit)
protected
procedure KeyPress(var Key: Char); override;
procedure WMPaste(var Message: TWMPaste); message WM_PASTE;
end;
...
procedure TButtonedEdit.KeyPress(var Key: Char);
function InvalidInput: boolean;
var
dc: integer;
begin
result := false;
if Character.IsControl(Key) then Exit;
dc := Pos(DecimalSeparator, Text);
if not (Key in ['0'..'9', DecimalSeparator]) then Exit(true);
if Pos(DecimalSeparator, Text) > 0 then
begin
if Key = DecimalSeparator then Exit(true);
if (Length(Text) - dc > 1)
and (Pos(DecimalSeparator, Text) < SelStart + 1) and
(SelLength = 0) then Exit(true);
end;
end;
begin
inherited;
if InvalidInput then
begin
Key := #0;
beep;
end;
end;
procedure TButtonedEdit.WMPaste(var Message: TWMPaste);
var
s: string;
i: integer;
hasdc: boolean;
NewText: string;
NewSelStart: integer;
begin
if Clipboard.HasFormat(CF_TEXT) then
begin
s := Clipboard.AsText;
NewText := Text;
Delete(NewText, SelStart + 1, SelLength);
Insert(s, NewText, SelStart + 1);
// Validate
hasdc := false;
for i := 1 to Length(NewText) do
begin
if NewText[i] = DecimalSeparator then
if hasdc then
begin
beep;
Exit;
end
else
hasdc := true
else if not (NewText[i] in ['0'..'9']) then
begin
beep;
Exit;
end;
end;
// Trim
if hasdc then
NewText := Copy(NewText, 1, Pos(DecimalSeparator, NewText) + 2);
NewSelStart := SelStart + Length(s);
Text := NewText;
SelStart := NewSelStart;
SelLength := 0;
end
else
inherited;
end;
Sample demo EXE

Use stock VCL buttoned editor
http://docwiki.embarcadero.com/Libraries/en/Vcl.ExtCtrls.TButtonedEdit
Use OnChange to filter out wrong input (or use JvValidators)
Another approach, JediVCL-based one, would be to use base button-enabled editor
http://wiki.delphi-jedi.org/wiki/JVCL_Help:TJvComboEdit
This has EditMask property, just like TMaskEdit has, so you can tweak it to accept only digits.
And at very least OnChange event would allow u to filter non-numeric text input as well.

Related

Determine whether a focused window has an active caret

Following _isEdit function detects whether input could be applied to the currently focused control:
class function TSpeedInput._getFocusedControlClassName(): WideString;
var
lpClassName: array[0..1000] of WideChar;
begin
FillChar(lpClassName, SizeOf(lpClassName), 0);
Windows.GetClassNameW(GetFocus(), PWideChar(#lpClassName), 999);
Result := lpClassName;
end;
class function TSpeedInput._isEdit(): Boolean;
const
CNAMES: array[0..3] of string = ('TEdit', 'TMemo', 'TTntMemo.UnicodeClass',
'TTntEdit.UnicodeClass');
var
cn: WideString;
i: Integer;
begin
Result := False;
cn := _getFocusedControlClassName();
for i := Low(CNAMES) to High(CNAMES) do
if cn = CNAMES[i] then begin
Result := True;
Exit;
end;
//MessageBoxW(0, PWideChar(cn), nil, 0);
end;
What I don't like about it is the hard coding of the class name list. Could it be detected that a currently focused window belongs to the editors family or, better to say, that it has an active caret? (in order that _isEdit returns False for a WhateverItIsControl that is in read-only mode).
If the Handle of the control is allocated, you can use this hack:
function IsEdit(AControl: TWinControl): boolean;
begin
if AControl.HandleAllocated then
begin
Result := SendMessage(AControl.Handle, EM_SETREADONLY,
WPARAM(Ord(AControl.Enabled)), 0) <> 0;
end
else
begin
Result := AControl is TCustomEdit;
end;
end;
If the controls you are interested in are on a specific form and are owned by that form (and are standard Delphi controls) you could use the following:
function TFormML2.FocusIsEdit: boolean;
var
i : integer;
begin
Result := FALSE;
for i := 0 to ComponentCount - 1 do
begin
if Components[ i ] is TCustomEdit then
begin
if (Components[ i ] as TCustomEdit).Focused and not (Components[ i ] as TCustomEdit).ReadOnly then
begin
Result := TRUE;
break;
end;
end;
end;
end;
If you know the form and can pass it as a parameter, you could do something similar.
TCustomEdit is the ancestor of all edit boxes, memos, etc.

How to make the same button run different code everytime it is clicked?

I am currently doing a school project, I am making a Credit Card machine. I need the 'Enter Button' to
run different code when it is clicked. The first click must get the card number from an edit ps... (I clear the edit once the card number has been retrieved), and the second click must get the pin from the same edit.
How would I do this?
procedure TfrmMainMenu.btbtnEnterClick(Sender: TObject);
var
sCvv,sPin:string;
begin
iCount2:=0;
sCardNumber:=lbledtCardInfo.Text;
if (Length(sCardNumber)<>16) AND (iCount2=0) then
begin
ShowMessage('Card number has to 16 digits,please try again!!');
end
else
begin
Inc(iCount2);
lbledtCardInfo.clear;
lbledtCardInfo.EditLabel.Caption:='Enter Pin' ;
btbtnEnter.Enabled:=false;
end; //if
if iCount2=2 then
begin
btbtnEnter.Enabled:=true;
sPin:=lbledtCardInfo.Text;
ShowMessage(sPin);//returns a blank
end;
You could try to do everything in a single event handler. There are several different ways to handle that. However, a different solution would be to use separate event handlers for each task, and then each task can assign a new handler for the next click to perform, eg:
procedure TfrmMainMenu.FormCreate(Sender: TObject);
begin
// you can set this at design-time if desired...
btbtnEnter.OnClick := GetCCNumber;
end;
procedure TfrmMainMenu.GetCCNumber(Sender: TObject);
begin
sCardNumber := lbledtCardInfo.Text;
if Length(sCardNumber) <> 16 then
begin
ShowMessage('Card number has to 16 digits,please try again!!');
Exit;
end;
lbledtCardInfo.Clear;
lbledtCardInfo.EditLabel.Caption := 'Enter Pin' ;
btbtnEnter.OnClick := GetCCPin;
end;
procedure TfrmMainMenu.GetCCPin(Sender: TObject);
var
sPin: string;
begin
sPin := lbledtCardInfo.Text;
if Length(sPin) <> 4 then
begin
ShowMessage('Card Pin has to 4 digits,please try again!!');
Exit;
end;
ShowMessage(sPin);
...
lbledtCardInfo.Clear;
lbledtCardInfo.EditLabel.Caption := 'Enter Number' ;
btbtnEnter.OnClick := GetCCNumber;
end;
A variation of this would be to create multiple buttons that overlap each other in the UI, and then you can toggle their Visible property back and forth as needed, eg:
procedure TfrmMainMenu.FormCreate(Sender: TObject);
begin
// you can set this at design-time if desired...
btbtnCCPinEnter.Visible := False;
btbtnCCNumEnter.Visible := True;
end;
procedure TfrmMainMenu.btbtnCCNumEnterClick(Sender: TObject);
begin
sCardNumber := lbledtCardInfo.Text;
if Length(sCardNumber) <> 16 then
begin
ShowMessage('Card number has to 16 digits,please try again!!');
Exit;
end;
lbledtCardInfo.Clear;
lbledtCardInfo.EditLabel.Caption := 'Enter Pin' ;
btbtnCCNumEnter.Visible := False;
btbtnCCPinEnter.Visible := True;
end;
procedure TfrmMainMenu.btbtnCCPinEnterClick(Sender: TObject);
var
sPin: string;
begin
sPin := lbledtCardInfo.Text;
if Length(sPin) <> 4 then
begin
ShowMessage('Card Pin has to 4 digits,please try again!!');
Exit;
end;
ShowMessage(sPin);
...
lbledtCardInfo.Clear;
lbledtCardInfo.EditLabel.Caption := 'Enter Number' ;
btbtnCCPinEnter.Visible := False;
btbtnCCNumEnter.Visible := True;
end;
Notice that you test iCount2 = 0 immediately after setting iCount2 := 0. Thus, that test will always be True. Furthermore, the later test iCount2 = 2 will always be False because the value starts at 0 and you only have one Inc in between.
Instead try the following.
Add two string fields FCardNumber and FPin to your form class:
private
FCardNumber: string;
FPin: string;
Also create an enumerated type TEntryStage = (esCardNumber, esPin) and add a field of this type. This will make your code look like this:
private
type
TEntryStage = (esCardNumber, esPin);
var
FCardNumber: string;
FPin: string;
FEntryStage: TEntryStage;
In Delphi, class fields (class member variables) are always initialized, so FEntryStage will be esCardNumber (=TEntryStage(0)) when the form is newly created.
Add a TLabeledEdit (I see you use those) and a TButton; name them eInput and btnNext, respectively. Let the labeled edit's caption be Card number: and the caption of the button be Next.
Now add the following OnClick handler to the button:
procedure TForm1.btnNextClick(Sender: TObject);
begin
case FEntryStage of
esCardNumber:
begin
// Save card number
FCardNumber := eInput.Text;
// Prepare for the next stage
eInput.Clear;
eInput.EditLabel.Caption := 'Pin:';
FEntryStage := esPin;
end;
esPin:
begin
// Save pin
FPin := eInput.Text;
// Just do something with the data
ShowMessageFmt('Card number: %s'#13#10'Pin: %s', [FCardNumber, FPin]);
end;
end;
end;
You might notice that you cannot trigger the Next button using Enter, which is very annoying. To fix this, do
procedure TForm1.eInputEnter(Sender: TObject);
begin
btnNext.Default := True;
end;
procedure TForm1.eInputExit(Sender: TObject);
begin
btnNext.Default := False;
end;
Much better!

Delphi - using selstart and sellength, but with cursor in the left (selstart position) instead end (sellength position)

I'm using Delphi to develop a DBLookupComboBox component with your own SQL (using Devart UniDac), without external ListSource, ListField, KeyField. Everything is working perfectly fine, but for a better user interface, I need one small detail.
I always leave the selected text according to the user's typing. When characters are typed, all right; but, when movement keys are typed (VK_LEFT, VK_RIGHT, combinations and etc.), the process is not cool, because the SelStart/SelLength places the cursor at the end of the text (sellength) and I want the cursor in the left (at SelStart), next to the last letter typed.
The component (using TFrame, TEdit and etc).
User typed BIAN, my component find the first person and use SelStart/SelLength to highlight.
User typed VK_LEFT, my component should show this:
But show this:
Unfortunately, the SelStart/SelLength properties to not support what you are asking for. Despite what MSDN documentation claims, the caret is always placed on the right side of the selection.
However, there is a simple trick you can use to place the caret on the left side of the selection instead:
procedure SelectText(Edit: TCustomEdit; iFirst, iLast: Integer);
var
bState: TKeyboardState;
bNewState: TKeyboardState;
i: Integer;
begin
if iFirst <= iLast then begin
{
Edit.SelStart := iFirst;
Edit.SelLength := iLast - iFirst;
}
SendMessage(Edit.Handle, EM_SETSEL, iFirst, iLast);
end else
begin
//Edit.SelStart := iFirst;
SendMessage(Edit.Handle, EM_SETSEL, iFirst, iFirst);
if GetKeyboardState(bState) then
begin
bNewState := bState;
bNewState[VK_SHIFT] := bNewState[VK_SHIFT] or 128;
if SetKeyboardState(bNewState) then
begin
repeat
SendMessage(Edit.Handle, WM_KEYDOWN, VK_LEFT, 0);
Dec(iFirst);
until iFirst = iLast;
SendMessage(Edit.Handle, WM_KEYUP, VK_LEFT, 0);
SetKeyboardState(bState);
end;
end;
end;
end;
Alternatively:
procedure SelectText(Edit: TEdit; iFirst, iLength: Integer);
var
bState: TKeyboardState;
bNewState: TKeyboardState;
i: Integer;
begin
if iLength >= 0 then begin
{
Edit.SelStart := iFirst;
Edit.SelLength := iLength;
}
SendMessage(Edit.Handle, EM_SETSEL, iFirst, iFirst + iLength);
end else
begin
//Edit.SelStart := iFirst;
SendMessage(Edit.Handle, EM_SETSEL, iFirst, iFirst);
if GetKeyboardState(bState) then
begin
bNewState := bState;
bNewState[VK_SHIFT] := bNewState[VK_SHIFT] or 128;
if SetKeyboardState(bNewState) then
begin
repeat
SendMessage(Edit.Handle, WM_KEYDOWN, VK_LEFT, 0);
Inc(iLength);
until iLength = 0;
SendMessage(Edit.Handle, WM_KEYUP, VK_LEFT, 0);
SetKeyboardState(bState);
end;
end;
end;
end;
Depending on whether you want to define the selection using absolute start/end positions, or a start position and a length.
Basically, what this code is doing is if the ending position is lower than the starting position, the code places the caret at the starting right side position and then simulates Shift+Left key presses until the caret reaches the desired left side position.

TScrollBox with dynamically created Memos issue [duplicate]

I have a TScrollBox that has a RichEdit that is bigger than the scrollbox, so both side scrollbars appear in the scrollbox. Then I have a function DoTask that calls RichEdit.SetFocus.
When I scroll down to where I want to see part of the text control, and then call DoTask, the ScrollBox will automatically scroll to the top of the RichEdit. How can I avoid that?
As you wish, here are some suggestions:
Override SetFocusedControl in the form:
function TForm1.SetFocusedControl(Control: TWinControl): Boolean;
begin
if Control = RichEdit then
Result := True
else
Result := inherited SetFocusedControl(Control);
end;
Or:
type
TCustomMemoAccess = class(TCustomMemo);
function TForm1.SetFocusedControl(Control: TWinControl): Boolean;
var
Memo: TCustomMemoAccess;
Scroller: TScrollingWinControl;
Pt: TPoint;
begin
Result := inherited SetFocusedControl(Control);
if (Control is TCustomMemo) and (Control.Parent <> nil) and
(Control.Parent is TScrollingWinControl) then
begin
Memo := TCustomMemoAccess(Control);
Scroller := TScrollingWinControl(Memo.Parent);
SendMessage(Memo.Handle, EM_POSFROMCHAR, Integer(#Pt), Memo.SelStart);
Scroller.VertScrollBar.Position := Scroller.VertScrollBar.Position +
Memo.Top + Pt.Y;
end;
end;
Interpose TScrollBox:
type
TScrollBox = class(Forms.TScrollBox)
protected
procedure AutoScrollInView(AControl: TControl); override;
end;
procedure TScrollBox.AutoScrollInView(AControl: TControl);
begin
if not (AControl is TCustomMemo) then
inherited AutoScrollInView(AControl);
end;
Or:
procedure TScrollBox.AutoScrollInView(AControl: TControl);
begin
if (AControl.Top > VertScrollBar.Position + ClientHeight) xor
(AControl.Top + AControl.Height < VertScrollBar.Position) then
inherited AutoScrollInView(AControl);
end;
Or use any creative combination of all of the above. How and when you like it to be scrolled only you know.
the simpliest solution would be
var a, b : Integer;
begin
a := ScrollBox1.VertScrollBar.Position;
b := ScrollBox1.HorzScrollBar.Position;
richEdit1.SetFocus;
ScrollBox1.VertScrollBar.Position:=a ;
ScrollBox1.HorzScrollBar.Position:=b ;
end;
Without hacking into VCL/deriving custom components there's only one solution - TForm.SetFocusedControl override + re-setting the positions of scrollbars as said above. One thing I added is disabling/enabling window redraw to avoid ugly jumps.
Here's my final snippet:
sbContainer is TScrollBox and NoScrCtrl is a control laying inside it which gets focus but we don't want it to be scrolled-in-view.
function TForm1.SetFocusedControl(Control: TWinControl): Boolean;
var hpos, vpos: integer;
begin
if Control = NoScrCtrl then
begin
sbContainer.Perform(WM_SETREDRAW, WPARAM(False), 0);
hpos := sbContainer.HorzScrollBar.Position;
vpos := sbContainer.VertScrollBar.Position;
Result := inherited SetFocusedControl(Control);
sbContainer.HorzScrollBar.Position := hpos;
sbContainer.VertScrollBar.Position := vpos;
sbContainer.Perform(WM_SETREDRAW, WPARAM(True), 0);
sbContainer.Refresh;
end
else
Result := inherited SetFocusedControl(Control);
end;
To disable scroll-into-view behavior from my main form, I used this solution: (C++Builder)
bool __fastcall TMainForm::SetFocusedControl(TWinControl *Control) {
LockWindowUpdate(Handle);
int vpos = VertScrollBar->Position;
int hpos = HorzScrollBar->Position;
bool result = TForm::SetFocusedControl(Control);
if (VertScrollBar->Position != vpos) {
VertScrollBar->Position = vpos;
}
if (HorzScrollBar->Position != hpos) {
HorzScrollBar->Position = hpos;
}
LockWindowUpdate(0);
return result;
}

How do I get the Control that is under the cursor in Delphi?

I need the opposite information that the question "How to get cursor position on a control?" asks.
Given the current cursor position, how can I find the form (in my application) and the control that the cursor is currently over? I need the handle to it so that I can use Windows.SetFocus(Handle).
For reference, I'm using Delphi 2009.
I experienced some problems with suggested solutions (Delphi XE6/Windows 8.1/x64):
FindVCLWindow doesn't search disabled controls (Enabled=False).
TWinControl.ControlAtPos doesn't search controls if they are disabled
indirectly (for example if Button.Enabled=True, but Button.Parent.Enabled=False).
In my case it was a problem, because i need to find any visible control under the mouse cursor, so i have to use my own implementation of function FindControlAtPos:
function FindSubcontrolAtPos(AControl: TControl; AScreenPos, AClientPos: TPoint): TControl;
var
i: Integer;
C: TControl;
begin
Result := nil;
C := AControl;
if (C=nil) or not C.Visible or not TRect.Create(C.Left, C.Top, C.Left+C.Width, C.Top+C.Height).Contains(AClientPos) then
Exit;
Result := AControl;
if AControl is TWinControl then
for i := 0 to TWinControl(AControl).ControlCount-1 do
begin
C := FindSubcontrolAtPos(TWinControl(AControl).Controls[i], AScreenPos, AControl.ScreenToClient(AScreenPos));
if C<>nil then
Result := C;
end;
end;
function FindControlAtPos(AScreenPos: TPoint): TControl;
var
i: Integer;
f,m: TForm;
p: TPoint;
r: TRect;
begin
Result := nil;
for i := Screen.FormCount-1 downto 0 do
begin
f := Screen.Forms[i];
if f.Visible and (f.Parent=nil) and (f.FormStyle<>fsMDIChild) and
TRect.Create(f.Left, f.Top, f.Left+f.Width, f.Top+f.Height).Contains(AScreenPos)
then
Result := f;
end;
Result := FindSubcontrolAtPos(Result, AScreenPos, AScreenPos);
if (Result is TForm) and (TForm(Result).ClientHandle<>0) then
begin
WinAPI.Windows.GetWindowRect(TForm(Result).ClientHandle, r);
p := TPoint.Create(AScreenPos.X-r.Left, AScreenPos.Y-r.Top);
m := nil;
for i := TForm(Result).MDIChildCount-1 downto 0 do
begin
f := TForm(Result).MDIChildren[i];
if TRect.Create(f.Left, f.Top, f.Left+f.Width, f.Top+f.Height).Contains(p) then
m := f;
end;
if m<>nil then
Result := FindSubcontrolAtPos(m, AScreenPos, p);
end;
end;
I think FindVCLWindow will meet your needs. Once you have the windowed control under the cursor you can walk the parent chain to find the form on which the window lives.
If you want to know the control inside a form that is at a certain x,y coordinate
Use
function TWinControl.ControlAtPos(const Pos: TPoint; AllowDisabled: Boolean;
AllowWinControls: Boolean = False; AllLevels: Boolean = False): TControl;
Given the fact that you seem only interested in forms inside your application, you can just query all forms.
Once you get a non-nil result, you can query the control for its Handle, with code like the following
Pseudo code
function HandleOfControlAtCursor: THandle;
const
AllowDisabled = true;
AllowWinControls = true;
AllLevels = true;
var
CursorPos: TPoint
FormPos: TPoint;
TestForm: TForm;
ControlAtCursor: TControl;
begin
Result:= THandle(0);
GetCursorPos(CursorPos);
for each form in my application do begin
TestForm:= Form_to_test;
FormPos:= TestForm.ScreenToClient(CursorPos);
ControlAtCursor:= TestForm.ControlAtPos(FormPos, AllowDisabled,
AllowWinControls, AllLevels);
if Assigned(ControlAtCursor) then break;
end; {for each}
//Break re-enters here
if Assigned(ControlAtCursor) then begin
while not(ControlAtCursor is TWinControl) do
ControlAtCursor:= ControlAtCursor.Parent;
Result:= ControlAtCursor.Handle;
end; {if}
end;
This also allows you to exclude certain forms from consideration should you so desire. If you're looking for simplicity I'd go with David and use FindVCLWindow.
P.S. Personally I'd use a goto rather than a break, because with a goto it's instantly clear where the break re-enters, but in this case it's not a big issue because there are no statements in between the break and the re-entry point.

Resources