I connot reproduce this. For me simple text inplace editing (TElTreeInplaceEdit) works fine. Can you specify your setup and steps to reproduce more precisely.
I have a TElXTree on a form with 7 sections. The 2nd section is a Text field that is marked Editable. I have a TElTreeInplaceEdit on the form associated with the TElXTree.
I use 3 events for the InplaceEdit:
- InplaceEditBeforeOperation
- InplaceEditAfterOperation
- InplaceEditEditorOnKeyUp
The first two are harmless. I don't know if the 3rd is causing the problem, but I'll give you my code::
procedure TOrganizePages.TagsDisplayInplaceEditEditorOnKeyUp(Sender: TObject; var Key: Word; Shift: TShiftState); begin DoEditorOnKeyUp(TagsList, TDisplayAsCol, TCustomElEdit(Sender), Key, Shift); end;
I do this to allow processing of some special keys on your form:
procedure DoEditorOnKeyUp(ElXTree: TElXTree; const EditColumn: integer; var ElEdit: TCustomElEdit; var Key: word; Shift: TShiftState); var CurItem: TElXTreeItem; INext: Integer; begin
case Key of VK_F2: ElXTree.EndEdit(false);
$41: { A part of Ctrl-A } if ssCtrl in Shift then { Ctrl part of Ctrl-A } ElEdit.SelectAll;
$5a: { Z part of Ctrl-Z } if ssCtrl in Shift then { Ctrl part of Ctrl-Z } ElEdit.Undo;
VK_PRIOR: begin ElXTree.Items.BeginUpdate; { Note: this isn't perfect from near the bottom of the tree, but it's close enough } INext := ElXTree.TopIndex -(ElXTree.BottomIndex - ElXTree.ItemFocused.AbsoluteIndex); if ElXTree.BottomIndex >= (ElXTree.Items.Count - 1) then INext := INext - 1;
if INext < 0 then INext := 0; ElXTree.EndEdit(false); ElXTree.Perform(WM_VSCROLL, SB_PAGEUP, 0); ElXTree.ItemFocused := ElXTree.Items[INext]; ElXTree.Items.EndUpdate; end;
VK_NEXT: begin ElXTree.Items.BeginUpdate; INext := ElXTree.BottomIndex + ElXTree.ItemFocused.AbsoluteIndex - ElXTree.TopIndex; if INext > ElXTree.Items.Count then INext := ElXTree.Items.Count - 1; ElXTree.EndEdit(false); ElXTree.Perform(WM_VSCROLL, SB_PAGEDOWN, 0); ElXTree.ItemFocused := ElXTree.Items[INext]; ElXTree.Items.EndUpdate; end;
VK_HOME: begin if ssCtrl in Shift then { Ctrl part of Ctrl-Home } begin ElXTree.Items.BeginUpdate; ElXTree.EndEdit(false); ElXTree.ItemFocused := ElXTree.Items[0]; ElXTree.Items.EndUpdate; ElXTree.ItemFocused.MakeVisible; end; end;
VK_END: begin if ssCtrl in Shift then { Ctrl part of Ctrl-End } begin ElXTree.Items.BeginUpdate; ElXTree.EndEdit(false); ElXTree.ItemFocused := ElXTree.Items[ElXTree.Items.Count-1]; ElXTree.Items.EndUpdate; ElXTree.ItemFocused.MakeVisible; end; end;
VK_DOWN, VK_RETURN: begin ElXTree.Items.BeginUpdate; CurItem := ElXTree.ItemFocused; CurItem := CurItem.GetNext; while CurItem <> nil do begin ElXTree.ItemFocused := CurItem; if CurItem.IsVisible and CurItem.AllowEdit then begin ElXTree.EndEdit(false); ElXTree.EditItem(CurItem, EditColumn); break; end; CurItem := CurItem.GetNext; end; if CurItem = nil then { at the bottom } ElXTree.EditItem(ElXTree.ItemFocused, EditColumn); ElXTree.Items.EndUpdate; ElXTree.ItemFocused.MakeVisible; Key := 0; end;
VK_UP: begin ElXTree.Items.BeginUpdate; CurItem := ElXTree.ItemFocused; CurItem := CurItem.GetPrev; while CurItem <> nil do begin ElXTree.ItemFocused := CurItem; if CurItem.IsVisible and CurItem.AllowEdit then begin ElXTree.EndEdit(false); ElXTree.EditItem(CurItem, EditColumn); break; end; CurItem := CurItem.GetPrev; end; if CurItem = nil then { at the top } ElXTree.EditItem(ElXTree.ItemFocused, EditColumn); ElXTree.Items.EndUpdate; ElXTree.ItemFocused.MakeVisible; Key := 0; end; end; end;
If I change text in the field and just hit Enter, or any of the keys in the above routine (e.g. arrow up, Home, etc) , there is no problem and it works as it should.
But if I change text in the field and then use the mouse to click on another field or any other window, when I'm in the Delphi debugger, it brings up a Debugger Exception Notification window that says: "Project raised exception class EInvalidOperation with message 'Control '' has no parent window' leading to the Windows exception "Control '' has no parent window."
If I break the debugger a that exception, the line in ElTreeStdEditors it was at was the last line of EditorWndProc: SaveWndProc(Message);
And now that you've got me to look, I see that I had to modify that routine so that I could catch the Return key myself.
This is the routine I use:
procedure TElTreeInplaceEdit.EditorWndProc(var Message : TMessage); var InputValid : boolean; begin if Message.Msg = WM_GETDLGCODE then begin inherited; Message.Result := Message.Result or DLGC_WANTCHARS or DLGC_WANTARROWS or DLGC_WANTALLKEYS; end else if Message.Msg = WM_KEYDOWN then begin TriggerKeyDown(TWMKey(Message)); with TWMKey(Message) do begin if KeyDataToShiftState(KeyData) = [] then begin
(* LK 31 May 2011 - Removed VK_RETURN so I can catch it myself if CharCode = VK_RETURN then begin InputValid := true; FEditing := false; TriggerValidateResult(InputValid); FEditing := true; if InputValid then begin CompleteOperation(true); CharCode := 0; exit; end else Editor.SetFocus; CharCode := 0; end else // LK 31 May 2011 - End of Removed VK_RETURN so I can catch it myself *)
if CharCode = VK_ESCAPE then begin CompleteOperation(false); CharCode := 0; exit; end; end; end; end else if (Message.Msg = WM_CANCELMODE) or ((Message.Msg = CM_CANCELMODE) and (TObject(Pointer(Message.lParam)) <> Editor)) or (Message.Msg = WM_KILLFOCUS) then if FEditing then begin if THackElTree(Tree).FExplorerEditMode then begin EndEditWithInputChecked; end else CompleteOperation(false);
if Message.Msg <> CM_CANCELMODE then SendMessage(THackElTree(Tree).View.Handle, CM_EXIT, 0, 0); end; SaveWndProc(Message); end;
This worked without problem until recently, which I blame on Delphi 10.2.2. When I saw this other post with the exact same problem, I thought there must be a simple solution. Maybe 10.2.3 which just came out will fix the issue, but it's not nice when they make updates that breaks your (or my) code.
If you can see what might be causing this, or a possible patch for this, please let me know.
Also feel free to also give me any suggestions to capture keys like I'm doing if you think there's a better way.
If you need more screenshots or code than I can supply here, please email me: lkessler at lkessler dot com
I removed the handler, and yes, the issue still happens when editing the text and (without pressing Enter) clicking anywhere outside what was edited.
And just to make sure, I added back the code that I removed, and the issue still happens. So it does not appear to have anything to do with the handler I added or the changes I made.
Everything was working fine less than a year ago with my handler and code changes. I am fairly certain it is a similar problem to DateTimePicker from the original post on this thread that you found a fix for, likely caused by Delphi 10.2.x.
The debug trace includes the following Elpack routines:
ElXTree.TElXTreeView.WndProc - the last line: inherited which calls a few vcl routines and then
ElXTree.TElXTreeView.WMLButtonDown, and then
ElXTree.TElXTreeView.IntLButtonDown - the SetFocus at the 3rd line from the bottom, and then
ElXTree.TElXTreeView.SetFocus - the "inherited SelfFocus" line
I tried, as you suggestied to Louis Kriel above, to put "if HandleAllocated then" before any "inherited" lines in any of these routines. That did not fix the issue.
I also went to ElXTree and tried the same, putting "if HandleAllocated then" before any "inherited" lines in:
At the bottom of TElTreeInplaceEdit.EditorWndProc is:
if (Message.Msg = WM_CANCELMODE) or ((Message.Msg = CM_CANCELMODE) and (TObject(Pointer(Message.lParam)) <> Editor)) or (Message.Msg = WM_KILLFOCUS) then if FEditing then begin if THackElTree(Tree).FExplorerEditMode then begin EndEditWithInputChecked; end else CompleteOperation(false); if Message.Msg <> CM_CANCELMODE then SendMessage(THackElTree(Tree).View.Handle, CM_EXIT, 0, 0); end; SaveWndProc(Message);
I noticed similar code in:
TElTreeInplaceMemo.EditorWndProc and
TElTreeInplaceCheckBox.EditorWndProc
except the EditorWndProc procedure had a bit different code.
CompleteOperation(false);
It had:
begin CompleteOperation(false); exit; end;
which exits without executing the SaveWndProc(Message) line.
I did that in the EditorWndProc procedure, and my issue vanished.
Please compare the 3 routines I list above and see if you agree that this is the correct fix.
I found another case where I still got the error. So I changed my fix above, to this:
if (Message.Msg = WM_CANCELMODE) or ((Message.Msg = CM_CANCELMODE) and (TObject(Pointer(Message.lParam)) <> Editor)) or (Message.Msg = WM_KILLFOCUS) then if FEditing then begin if THackElTree(Tree).FExplorerEditMode then begin EndEditWithInputChecked; end else CompleteOperation(false); if Message.Msg <> CM_CANCELMODE then SendMessage(THackElTree(Tree).View.Handle, CM_EXIT, 0, 0);
exit: end; SaveWndProc(Message);
I added that "exit" line near the bottom. It now prevents the SaveWndProc line from being called whenever Canceling or Killing the focus.
Comments
<Removed>
I have a TElXTree on a form with 7 sections. The 2nd section is a Text field that is marked Editable. I have a TElTreeInplaceEdit on the form associated with the TElXTree.
I use 3 events for the InplaceEdit:
procedure TOrganizePages.TagsDisplayInplaceEditEditorOnKeyUp(Sender: TObject;
var Key: Word; Shift: TShiftState);
begin
DoEditorOnKeyUp(TagsList, TDisplayAsCol, TCustomElEdit(Sender), Key, Shift);
end;
procedure DoEditorOnKeyUp(ElXTree: TElXTree; const EditColumn: integer; var ElEdit: TCustomElEdit;
var Key: word; Shift: TShiftState);
var
CurItem: TElXTreeItem;
INext: Integer;
begin
VK_F2:
ElXTree.EndEdit(false);
if ssCtrl in Shift then { Ctrl part of Ctrl-A }
ElEdit.SelectAll;
if ssCtrl in Shift then { Ctrl part of Ctrl-Z }
ElEdit.Undo;
ElXTree.Items.BeginUpdate;
{ Note: this isn't perfect from near the bottom of the tree, but it's close enough }
INext := ElXTree.TopIndex -(ElXTree.BottomIndex - ElXTree.ItemFocused.AbsoluteIndex);
if ElXTree.BottomIndex >= (ElXTree.Items.Count - 1) then
INext := INext - 1;
ElXTree.EndEdit(false);
ElXTree.Perform(WM_VSCROLL, SB_PAGEUP, 0);
ElXTree.ItemFocused := ElXTree.Items[INext];
ElXTree.Items.EndUpdate;
end;
ElXTree.Items.BeginUpdate;
INext := ElXTree.BottomIndex + ElXTree.ItemFocused.AbsoluteIndex - ElXTree.TopIndex;
if INext > ElXTree.Items.Count then INext := ElXTree.Items.Count - 1;
ElXTree.EndEdit(false);
ElXTree.Perform(WM_VSCROLL, SB_PAGEDOWN, 0);
ElXTree.ItemFocused := ElXTree.Items[INext];
ElXTree.Items.EndUpdate;
end;
if ssCtrl in Shift then { Ctrl part of Ctrl-Home } begin
ElXTree.Items.BeginUpdate;
ElXTree.EndEdit(false);
ElXTree.ItemFocused := ElXTree.Items[0];
ElXTree.Items.EndUpdate;
ElXTree.ItemFocused.MakeVisible;
end;
end;
if ssCtrl in Shift then { Ctrl part of Ctrl-End } begin
ElXTree.Items.BeginUpdate;
ElXTree.EndEdit(false);
ElXTree.ItemFocused := ElXTree.Items[ElXTree.Items.Count-1];
ElXTree.Items.EndUpdate;
ElXTree.ItemFocused.MakeVisible;
end;
end;
begin
ElXTree.Items.BeginUpdate;
CurItem := ElXTree.ItemFocused;
CurItem := CurItem.GetNext;
while CurItem <> nil do begin
ElXTree.ItemFocused := CurItem;
if CurItem.IsVisible and CurItem.AllowEdit then begin
ElXTree.EndEdit(false);
ElXTree.EditItem(CurItem, EditColumn);
break;
end;
CurItem := CurItem.GetNext;
end;
if CurItem = nil then { at the bottom }
ElXTree.EditItem(ElXTree.ItemFocused, EditColumn);
ElXTree.Items.EndUpdate;
ElXTree.ItemFocused.MakeVisible;
Key := 0;
end;
begin
ElXTree.Items.BeginUpdate;
CurItem := ElXTree.ItemFocused;
CurItem := CurItem.GetPrev;
while CurItem <> nil do begin
ElXTree.ItemFocused := CurItem;
if CurItem.IsVisible and CurItem.AllowEdit then begin
ElXTree.EndEdit(false);
ElXTree.EditItem(CurItem, EditColumn);
break;
end;
CurItem := CurItem.GetPrev;
end;
if CurItem = nil then { at the top }
ElXTree.EditItem(ElXTree.ItemFocused, EditColumn);
ElXTree.Items.EndUpdate;
ElXTree.ItemFocused.MakeVisible;
Key := 0;
end;
end;
end;
If I change text in the field and just hit Enter, or any of the keys in the above routine (e.g. arrow up, Home, etc) , there is no problem and it works as it should.
But if I change text in the field and then use the mouse to click on another field or any other window, when I'm in the Delphi debugger, it brings up a Debugger Exception Notification window that says: "Project raised exception class EInvalidOperation with message 'Control '' has no parent window' leading to the Windows exception "Control '' has no parent window."
If I break the debugger a that exception, the line in ElTreeStdEditors it was at was the last line of EditorWndProc:
SaveWndProc(Message);
This is the routine I use:
procedure TElTreeInplaceEdit.EditorWndProc(var Message : TMessage);
var
InputValid : boolean;
begin
if Message.Msg = WM_GETDLGCODE then
begin
inherited;
Message.Result := Message.Result or DLGC_WANTCHARS or DLGC_WANTARROWS or DLGC_WANTALLKEYS;
end
else
if Message.Msg = WM_KEYDOWN then
begin
TriggerKeyDown(TWMKey(Message));
with TWMKey(Message) do
begin
if KeyDataToShiftState(KeyData) = [] then
begin
if CharCode = VK_RETURN then
begin
InputValid := true;
FEditing := false;
TriggerValidateResult(InputValid);
FEditing := true;
if InputValid then
begin
CompleteOperation(true);
CharCode := 0;
exit;
end
else
Editor.SetFocus;
CharCode := 0;
end
else
// LK 31 May 2011 - End of Removed VK_RETURN so I can catch it myself *)
begin
CompleteOperation(false);
CharCode := 0;
exit;
end;
end;
end;
end
else
if (Message.Msg = WM_CANCELMODE) or
((Message.Msg = CM_CANCELMODE) and
(TObject(Pointer(Message.lParam)) <> Editor)) or
(Message.Msg = WM_KILLFOCUS) then
if FEditing then
begin
if THackElTree(Tree).FExplorerEditMode then
begin
EndEditWithInputChecked;
end
else
CompleteOperation(false);
SendMessage(THackElTree(Tree).View.Handle, CM_EXIT, 0, 0);
end;
SaveWndProc(Message);
end;
If you can see what might be causing this, or a possible patch for this, please let me know.
Also feel free to also give me any suggestions to capture keys like I'm doing if you think there's a better way.
If you need more screenshots or code than I can supply here, please email me: lkessler at lkessler dot com
Thanks.
Louis Kessler
Winnipeg, Manitoba, Canada
And just to make sure, I added back the code that I removed, and the issue still happens. So it does not appear to have anything to do with the handler I added or the changes I made.
ElXTree.TElXTreeView.WndProc - the last line: inherited
which calls a few vcl routines and then
((Message.Msg = CM_CANCELMODE) and
(TObject(Pointer(Message.lParam)) <> Editor)) or
(Message.Msg = WM_KILLFOCUS) then
if FEditing then
begin
if THackElTree(Tree).FExplorerEditMode then
begin
EndEditWithInputChecked;
end
else
CompleteOperation(false);
if Message.Msg <> CM_CANCELMODE then
SendMessage(THackElTree(Tree).View.Handle, CM_EXIT, 0, 0);
end;
SaveWndProc(Message);
CompleteOperation(false);
exit;
end;
So in
((Message.Msg = CM_CANCELMODE) and
(TObject(Pointer(Message.lParam)) <> Editor)) or
(Message.Msg = WM_KILLFOCUS) then
if FEditing then
begin
if THackElTree(Tree).FExplorerEditMode then
begin
EndEditWithInputChecked;
end
else
CompleteOperation(false);
if Message.Msg <> CM_CANCELMODE then
SendMessage(THackElTree(Tree).View.Handle, CM_EXIT, 0, 0);
end;
SaveWndProc(Message);