子类化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
相关产品推荐
相关产品推荐

