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";
}





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





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

Popular posts from this blog

List of Kim Possible characters

Audio Livestreaming with Python & Flask

NSwag: Generate C# Client from multiple Versions of an API