Search code examples
delphibuttoninplace-editing

How Can I Exit My Inplace-Editor AND Process the Button in Delphi?


In my Delphi 2009 application, I have this window:

enter image description here

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;

Solution

  • 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.