TListView 检测 ESC 或不变的编辑

TListView detecting ESC or unchanged editing

我正在尝试子类化 TListViewWindowProc 以在编辑 TListView 标题后检测 ESC 键按下(如果用户取消编辑)。 ListViewWndProc 被清楚地调用,但应该检测的代码参数永远不会获得 LVN_ENDLABELEDIT 值。为什么评论部分永远不会被调用?我看不到错误,它应该正在发生。

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

在 C++ 中,LVN_ENDLABELEDEDIT 的值取决于项目的 TCHAR_Mapping,可以通过 "_TCHAR maps to" 配置项在项目设置中更改。默认情况下,_TCHAR 在 C++Builder 2009 及更高版本中设置为 wchar_t,除非您从早期版本迁移项目,在这种情况下,它默认设置为 char

LVN_ENDLABELEDIT 是一个宏,当 _TCHARchar 时映射到 LVN_ENDLABELEDITA (4294967190),当 [=] 时映射到 LVN_ENDLABELEDITW (4294967120) 13=] 是 wchar_t.

检查常量 LVN_ENDLABELEDEDITALVN_ENDLABELEDEDITW,就像在 Delphi 源代码中所做的那样,应该没问题。

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

以防其他人到这里来寻找 Delphi 中的解决方案,我刚刚翻译了 Kerem D 的代码:

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;

它适用于 Delphi 6 到 10.2(在 GExperts 中使用)