TListView detecting ESC or unchanged editing
TListView detecting ESC or unchanged editing
I am trying to subclass the WindowProc
of TListView
to detect ESC key press after editing TListView
caption (if user cancels editing). ListViewWndProc is getting called clearly, but the code parameter which is supposed to detect that never gets LVN_ENDLABELEDIT value. Why the commented part never gets called? I cannot see the error, it should be happening.
WindowProc
TListView
TListView
TWndMethod OldWndProc;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner)
{
OldWndProc = ListView1->WindowProc;
ListView1->WindowProc = ListViewWndProc;
}
//---------------------------------------------------------------------------
void __fastcall TForm1::ListViewWndProc(TMessage &Message)
{
if (Message.Msg == CN_NOTIFY)
{
LPNMHDR pnmh = reinterpret_cast<LPNMHDR>(Message.LParam);
if (pnmh->code == LVN_ENDLABELEDIT) // UPDATE: if LVN_ENDLABELEDIT is replaced with 4294967120 it works
{
// !!! THE FOLLOWING NEVER HAPPENS !!!
// UPDATE: Looks like LVN_ENDLABELEDIT is incorrectly defined in C++ Builder 2010
// if LVN_ENDLABELEDIT is replaced with 4294967120 the code works
LV_DISPINFO *pdi = reinterpret_cast<LV_DISPINFO*>(Message.LParam);
if (pdi->item.pszText == NULL)
{
Edit1->Text = "Cancelled";
return;
}
}
}
OldWndProc(Message);
}
//---------------------------------------------------------------------------
void __fastcall TForm1::ListView1Editing(TObject *Sender, TListItem *Item, bool &AllowEdit)
{
Edit1->Text = "Editing";
}
//---------------------------------------------------------------------------
void __fastcall TForm1::ListView1Edited(TObject *Sender, TListItem *Item, UnicodeString &S)
{
Edit1->Text = "Done";
}
@KeremD I still use C++ Builder 2010, but how is that related as this is a regular WinAPI call. You haven't specified what works exactly and how you tested.
– Coder12345
May 28 at 13:27
I built an app and copied your code... When I cancel editing by pressing ESC the commented part of the code is called and the Edit is filled with the text "Cancelled" . So it works as it should I think. Perhaps you should remove the C++Builder related tags if it's only about winapi?
– Kerem D
May 28 at 14:08
@KeremD Can you please do
Edit1->Text = LVN_ENDLABELEDIT;
and see if it reports value 4294967190
? That is the value defined by C++ Builder 2010, so perhaps in Seattle it is different, that is what might be the cause of this.– Coder12345
May 28 at 14:31
Edit1->Text = LVN_ENDLABELEDIT;
4294967190
The shown value is
4294967120
.– Kerem D
May 28 at 14:43
4294967120
2 Answers
2
In C++, the value of LVN_ENDLABELEDEDIT
depends on the project's TCHAR_Mapping, which can be changed in the Project Settings via the "_TCHAR maps to"
configuration item. By default, _TCHAR
is set to wchar_t
in C++Builder 2009 and later, unless you migrate a project from an earlier version, in which case it is char
by default instead.
LVN_ENDLABELEDEDIT
"_TCHAR maps to"
_TCHAR
wchar_t
char
LVN_ENDLABELEDIT
is a macro that maps to LVN_ENDLABELEDITA
(4294967190) when _TCHAR
is char
, and to LVN_ENDLABELEDITW
(4294967120) when _TCHAR
is wchar_t
.
LVN_ENDLABELEDIT
LVN_ENDLABELEDITA
_TCHAR
char
LVN_ENDLABELEDITW
_TCHAR
wchar_t
Checking for both constants LVN_ENDLABELEDEDITA
and LVN_ENDLABELEDEDITW
, like it is done in the Delphi source code, should be OK.
LVN_ENDLABELEDEDITA
LVN_ENDLABELEDEDITW
void __fastcall TForm1::ListViewWndProc(TMessage &Message)
{
if (Message.Msg == CN_NOTIFY)
{
LPNMHDR pnmh = reinterpret_cast<LPNMHDR>(Message.LParam);
if ((pnmh->code == LVN_ENDLABELEDITA) || (pnmh->code == LVN_ENDLABELEDITW))
{
LV_DISPINFO *pdi = reinterpret_cast<LV_DISPINFO*>(Message.LParam);
if (pdi->item.pszText == NULL)
{
Edit1->Text = "Cancelled";
return;
}
}
}
OldWndProc(Message);
}
Thanks for added research, well done, an elegant solution!
– Coder12345
May 28 at 23:12
Note that for
LVN_ENDLABELEDITA
, pdi->item
is a LVITEMA
, and thus item.pszText
is a char*
, and for LVN_ENDLABELEDITW
, pdi->item
is a LVITEMW
, and thus item.pszText
is a wchar_t*
. You have to watch out for that if you ever intend to use pszText
for anything other than comparing it to NULL.– Remy Lebeau
May 29 at 19:58
LVN_ENDLABELEDITA
pdi->item
LVITEMA
item.pszText
char*
LVN_ENDLABELEDITW
pdi->item
LVITEMW
item.pszText
wchar_t*
pszText
Also, note that the TCHAR mapping only affects C++, not Delphi. The VCL's source code is written in Delphi, and uses only Win32 Unicode APIs in C++Builder/Delphi 2009+, so the
TListView
should only be sending LVN_ENDLABELEDITW
regardless of the TCHAR mapping, which explains why the code started working when LVN_ENDLABELEDIT
was substituted with 4294967120
(LVN_ENDLABELEDITW
). You don't need to check for both notifications unless you plan on compiling your code for both pre-Unicode and Unicode versions of C++Builder/Delphi.– Remy Lebeau
May 29 at 20:07
TListView
LVN_ENDLABELEDITW
LVN_ENDLABELEDIT
4294967120
LVN_ENDLABELEDITW
In case anybody else gets here looking for a solution in Delphi, I have just translated Kerem D's code:
constructor TForm1.Create(_Owner: TComponent);
begin
inherited;
OldWndProc = ListView1.WindowProc;
ListView1.WindowProc = ListViewWndProc;
end;
procedure TForm1.ListViewWndProc(var _Message: TMessage);
var
LvDispInfo: PLVDispInfo; // declared in CommCtrl
Code: Integer;
begin
if _Message.Msg = CN_NOTIFY then begin
Code := PNMHdr(_Message.lParam).Code;
if (Code = LVN_ENDLABELEDITA) or (Code = LVN_ENDLABELEDITW) then begin
LvDispInfo := PLVDispInfo(_Message.lParam);
if LvDispInfo.Item.pszText = nil then begin
Edit1.Text := 'Cancelled';
Exit; //==>
end;
end;
end;
OldWndProc(_Message);
end;
It works for me in Delphi 6 to 10.2 (used in GExperts)
By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.
I compiled this with C++ Builder Seattle and it works as it should. Which version do you use?
– Kerem D
May 28 at 7:11