In my Delphi 2009 application, I have this window:
It has a TPageControl that has a TTabSheet on it as well as buttons at the bottom that operate on all sheets.
On the left of the TTabSheet is a TElXTree (a tree/grid component by LMD) and on the right of the TTabSheet is a TPanel containing buttons specific to just this sheet.
When I have a row selected in the TElXTree, and I click on any button in either set of buttons, the buttons all work fine.
Now within the TElXTree, the column labelled "Text" is editable with an Inplace-Editor supplied with TElXtree. When I click on the Text, it goes into edit mode.
When in edit mode, when I click anywhere in the TElXTree (e.g. on the checkbox), it will exit the editor AND process the command (i.e. check or uncheck the checkbox). However, when in edit mode, when I click on any button in either set of buttons, it will simply exit the inplace-editor and NOT process the button. I then have to click on the button again to process that button.
Is there something simple that I am not doing or not understanding here that would allow me to click on one of those buttons and allow it to both exit my inplace editor AND process the button?
Followup:
Thanks to @NGLN's answer, I got my workaround. I used his Application.OnMessage method, which I was previously using anyway for some Drag and Drop code. I had to make some changes though, and this is what I came up with:
procedure TMainForm.AppMessageHandler(var Msg: TMsg; var Handled: Boolean);
var
P: TPoint;
begin
if Msg.message = WM_LBUTTONDOWN then
if Screen.ActiveControl <> nil then
if Screen.ActiveControl.ClassNameIs('TElInpEdit') then
begin
GetCursorPos(P);
{ When in the inplace editor, I need to go to its parent ElXTree }
{ because the ElXTree does not have the problem. }
{ Only components outside the ElXTree do }
with Screen.ActiveControl.Parent do
if not PtInRect(ClientRect, ScreenToClient(P)) then begin
{ The WM_Killfocus didn't work for me, but it gave me this idea: }
{ 1. Complete the operation, and 2. Simulate the mouse click }
InplaceEdit.CompleteOperation(true);
Mouse_Event(MOUSEEVENTF_ABSOLUTE or MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0);
Mouse_Event(MOUSEEVENTF_ABSOLUTE or MOUSEEVENTF_LEFTUP, 0, 0, 0, 0);
{ Then skip the regular handling of this WM_LBUTTONDOWN }
Handled := true;
end;
end;
end;
It indeed looks like a bug. Two possible (nasty) workarounds:
Via Application.OnMessage:
procedure TMainForm.ApplicationEventsMessage(var Msg: tagMSG;
var Handled: Boolean);
var
P: TPoint;
begin
if Msg.message = WM_LBUTTONDOWN then
if Screen.ActiveControl <> nil then
if Screen.ActiveControl.ClassNameIs('TElInpEdit') then
begin
GetCursorPos(P);
with Screen.ActiveControl do
if not PtInRect(ClientRect, ScreenToClient(P)) then
Perform(WM_KILLFOCUS, 0, 0);
end;
end;
Or subclass the component:
type
TElXTree = class(ElXTree.TElXTree)
private
procedure CMMouseLeave(var Message: TMessage); message CM_MOUSELEAVE;
end;
TForm1 = class(TForm)
ElXTree1: TElXTree;
...
procedure TElXTree.CMMouseLeave(var Message: TMessage);
var
P: TPoint;
begin
GetCursorPos(P);
if not PtInRect(ClientRect, ScreenToClient(P)) then
if Screen.ActiveControl <> nil then
if Screen.ActiveControl.ClassNameIs('TElInpEdit') then
Screen.ActiveControl.Perform(WM_KILLFOCUS, 0, 0);
inherited;
end;
Note: this one is nót preferred since it alters the behaviour of the component: just hovering the mouse outside of the grid closes the inplace editor. But I added it because it might bring others to other solutions.