You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

子类化TListView后ListView_SetView失效,求解决办法

子类化TListView扩展功能实现记录

已完成功能

  • 实现ESC键捕获,处理编辑取消场景
  • 实现编辑状态下Del键捕获,解决菜单快捷键导致编辑控件中Del键失效的问题

历史问题及修复

调用ListView_SetView(ListViewEx1->Handle, LV_VIEW_TILE);在原生TListView中可正常切换视图,但在自定义TListViewEx中无效,该问题已修复

TListViewEx组件实现代码

.cpp实现文件

//---------------------------------------------------------------------------

#include <vcl.h>

#pragma hdrstop

#include "TListViewEx.h"
#pragma package(smart_init)
//---------------------------------------------------------------------------
// ValidCtrCheck 用于确保创建的组件没有纯虚函数
//

static inline void ValidCtrCheck(TListViewEx *)
{
    new TListViewEx(NULL);
}
//---------------------------------------------------------------------------
__fastcall TListViewEx::TListViewEx(TComponent* Owner)
    : TListView(Owner)
{

}
//---------------------------------------------------------------------------
void __fastcall TListViewEx::Click()
{
    TListView::Click();
}
//---------------------------------------------------------------------------
void __fastcall TListViewEx::WMGetDlgCode(TMessage &msg)
{
    TCustomListView::Dispatch(&msg);
    msg.Result |= DLGC_WANTCHARS;
}
//---------------------------------------------------------------------------
void __fastcall TListViewEx::CNKeyDown(TMessage &Message)
{
    if (Message.WParam == VK_DELETE)
    {
        if(FOnWantEditEvent)
        {
            FEdit = ListView_GetEditControl(Handle);
            OnWantEditEvent(this, FKey);
        }
    }
    TListView::Dispatch(&Message);
}
//---------------------------------------------------------------------------
void __fastcall TListViewEx::SetView(int view)
{
    ListView_SetView(Handle, view);
}
//---------------------------------------------------------------------------
void __fastcall TListViewEx::CNNotify(Winapi::Messages::TWMNotify &Message)
{
    TListView::Dispatch(&Message);

    if (Message.NMHdr->code == LVN_ENDLABELEDITA || Message.NMHdr->code == LVN_ENDLABELEDITW)
    {
        NMLVDISPINFO *plvdi = reinterpret_cast<NMLVDISPINFO*>(Message.NMHdr);

        if ((plvdi->item.pszText == NULL) && (plvdi->item.iItem != -1) && (FOnEditCancel != NULL))
        {
            // 理想情况下应使用TCustomListView::GetItem(LVITEM)获取对应的TListItem
            // 但该方法是私有方法,子类无法访问,这也是Embarcadero需要在原生TListView中修复的点...

            TListItem *item;
            if (plvdi->item.mask & LVIF_PARAM)
                item = reinterpret_cast<TListItem*>(plvdi->item.lParam);
            else // TODO: 处理OwnerData=true的情况...
                item = this->Items->Item[plvdi->item.iItem];

            FOnEditCancel(this, item);
        }
    }
}
//---------------------------------------------------------------------------
namespace Tlistviewex
{
    void __fastcall PACKAGE Register()
    {
        TComponentClass classes[1] = {__classid(TListViewEx)};
        RegisterComponents(L"Samples", classes, 0);
    }
}
//---------------------------------------------------------------------------

.h头文件

//---------------------------------------------------------------------------

#ifndef TListViewExH
#define TListViewExH
//---------------------------------------------------------------------------
#include <System.SysUtils.hpp>
#include <System.Classes.hpp>
#include <Vcl.ComCtrls.hpp>
#include <Vcl.Controls.hpp>

//---------------------------------------------------------------------------
typedef void __fastcall (__closure *TOnEditCancel)(TObject* Sender, TListItem* item);
typedef void __fastcall (__closure *TOnWantEditEvent)(TObject* Sender, WORD &Key);


class PACKAGE TListViewEx : public TListView
{
private:
    TOnEditCancel FOnEditCancel;
    TOnWantEditEvent FOnWantEditEvent;
    HWND FEdit;

    MESSAGE void __fastcall WMGetDlgCode(TMessage &msg);
    MESSAGE void __fastcall CNNotify(Winapi::Messages::TWMNotify &Message);
    MESSAGE void __fastcall CNKeyDown(TMessage &Message);

    BEGIN_MESSAGE_MAP
        VCL_MESSAGE_HANDLER(WM_GETDLGCODE, TMessage, WMGetDlgCode)
        VCL_MESSAGE_HANDLER(CN_NOTIFY, TWMNotify, CNNotify);
        VCL_MESSAGE_HANDLER(CN_KEYDOWN, TMessage, CNKeyDown);
    END_MESSAGE_MAP(TListView);


protected:
    DYNAMIC void __fastcall Click();

public:
    __property HWND Edit = {read = FEdit};
    __fastcall TListViewEx(TComponent* Owner);
    void __fastcall SetView(int view);

__published:
    __property TOnEditCancel OnEditCancel = {read = FOnEditCancel, write = FOnEditCancel};
    __property TOnWantEditEvent OnWantEditEvent = {read = FOnWantEditEvent, write = FOnWantEditEvent};
};
//---------------------------------------------------------------------------
#endif

窗体层事件处理逻辑

基于Martin Nijhoff的代码实现,在窗体中处理OnWantEditEvent及删除按钮点击事件:

void __fastcall TForm3::ListViewEx1WantEditEvent(TObject *Sender, Word &Key)
{
    switch(Key)
    {
    case VK_DELETE:
        {
        if(ListViewEx1->Edit)
            {
            UINT Scan = MapVirtualKey(VK_DELETE, MAPVK_VK_TO_VSC);  // 获取Del键的扫描码

            SendMessage(ListViewEx1->Edit, WM_KEYDOWN, VK_DELETE, 0x00000001 | Scan << 16);  // 在编辑窗口模拟Del键按下
            SendMessage(ListViewEx1->Edit, WM_KEYUP,   VK_DELETE, 0xC0000001 | Scan << 16);
            }
        }
        break;
    }
}
//-------------------------------------------------------------------------    
void __fastcall TForm3::Delete1Click(TObject *Sender)
{
    if(!ListViewEx1->IsEditing())
        ListViewEx1->DeleteSelected();

    ViewTile();
}

内容的提问来源于stack exchange,提问作者user16458899

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.19 04:15:40