Coder12345
Coder12345

Reputation: 3753

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.

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

Upvotes: 2

Views: 334

Answers (2)

dummzeuch
dummzeuch

Reputation: 11252

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)

Upvotes: 2

Kerem
Kerem

Reputation: 449

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_ENDLABELEDIT is a macro that maps to LVN_ENDLABELEDITA (4294967190) when _TCHAR is char, and to LVN_ENDLABELEDITW (4294967120) when _TCHAR is wchar_t.

Checking for both constants LVN_ENDLABELEDEDITA and LVN_ENDLABELEDEDITW, like it is done in the Delphi source code, should be OK.

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

Upvotes: 3

Related Questions