Relating to Prevent action in TListView's context menu when in edit mode, I'm having an issue reading the value of plvdi->item.pszText
in a CNNotify()
event. This value should be nil
if the edit is cancelled. I tried a few conversions, but no luck. I must be doing something wrong. See the code below.
Everything works, except for comparing the pszText
value.
.cpp
//---------------------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop
#include "TCustomListView.h"
#pragma package(smart_init)
//---------------------------------------------------------------------------
// ValidCtrCheck is used to assure that the components created do not have
// any pure virtual functions.
//
static inline void ValidCtrCheck(TCustomListView1 *)
{
new TCustomListView1(NULL);
}
//---------------------------------------------------------------------------
__fastcall TCustomListView1::TCustomListView1(TComponent* Owner)
: TListView(Owner)
{
cancel = false;
}
//---------------------------------------------------------------------------
void __fastcall TCustomListView1::WMGetDlgCode(TMessage &msg)
{
TCustomListView::Dispatch(&msg);
msg.Result |= WM_CHAR;
//To Do
}
//---------------------------------------------------------------------------
void __fastcall TCustomListView1::CNNotify(Winapi::Messages::TWMNotify &Message)
{
TListView::Dispatch(&Message);
Message.Result |= LVN_ENDLABELEDIT;
NMLVDISPINFO* plvdi = (NMLVDISPINFO*)Message.NMHdr;
if(plvdi->item.pszText == NULL ) ----->> ??? what am i doing wrong here
{
if(FOnEditCancel && IsEditing())
{
cancel = true;
FOnEditCancel(this, this->Selected, cancel);
cancel = false;
}
}
}
//---------------------------------------------------------------------------
namespace Tcustomlistview
{
void __fastcall PACKAGE Register()
{
TComponentClass classes[1] = {__classid(TCustomListView1)};
RegisterComponents(L"Samples", classes, 0);
}
}
//---------------------------------------------------------------------------
.h
//---------------------------------------------------------------------------
#ifndef TCustomListViewH
#define TCustomListViewH
//---------------------------------------------------------------------------
#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, bool cancelled);
class PACKAGE TCustomListView1 : public TListView
{
private:
TOnEditCancel FOnEditCancel;
bool cancel;
MESSAGE void __fastcall WMGetDlgCode(TMessage &msg);
MESSAGE void __fastcall CNNotify(Winapi::Messages::TWMNotify &Message);
BEGIN_MESSAGE_MAP
VCL_MESSAGE_HANDLER(WM_GETDLGCODE, TMessage, WMGetDlgCode)
VCL_MESSAGE_HANDLER(WM_NOTIFY, TWMNotify, CNNotify);
END_MESSAGE_MAP(inherited);
protected:
public:
__fastcall TCustomListView1(TComponent* Owner);
__published:
__property TOnEditCancel OnEditCancel = {read = FOnEditCancel, write = FOnEditCancel};
};
//---------------------------------------------------------------------------
#endif
There are a lot of issues with your code.
TCustomListView1
is not a good name for your component. Use something more meaningful.
Your component doesn't need the cancel
member at all, so get rid of it. You could just hard-code the cancelled
parameter of the OnEditCancel
event instead. However, that event really should not have a cancelled
parameter to begin with. In which case, removing that parameter, you can then make use of one of the existing ListView event types that matches your remaining parameters, such as TLVNotifyEvent
.
WM_CHAR
is a window message identifier, it is not a valid flag that you can add to the return value of the WM_GETDLGCODE
message. You meant to use the DLGC_WANTCHARS
flag instead, which will enable your control to receive WM_CHAR
messages.
LVN_ENDLABELEDIT
is a window message identifier, it is not a valid flag that you can add to the return value of the CN_NOTIFY
message. You need to check if the Message.NMHdr->code
field matches LVN_ENDLABELEDIT
and then process the Message.NMHdr
field accordingly.
you are handling the wrong message in your MESSAGE_MAP
. You are catching WM_NOTIFY
messages that are sent from the ListView's header control to the ListView. You need to instead catch CN_NOTIFY
messages, which are WM_NOTIFY
messages that the ListView sends to its parent window and are then reflected back to the ListView as CN_NOTIFY
. The VCL does this reflection to allow components to be self-contained and process their own notifications.
With that said, try something more like this instead:
TMyListViewEx.cpp
//---------------------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop
#include "MyListViewEx.h"
#pragma package(smart_init)
//---------------------------------------------------------------------------
// ValidCtrCheck is used to assure that the components created do not have
// any pure virtual functions.
//
static inline void ValidCtrCheck(TMyListViewEx *)
{
new TMyListViewEx(NULL);
}
//---------------------------------------------------------------------------
__fastcall TMyListViewEx::TMyListViewEx(TComponent* Owner)
: TListView(Owner)
{
}
//---------------------------------------------------------------------------
void __fastcall TMyListViewEx::WMGetDlgCode(TMessage &msg)
{
TListView::Dispatch(&msg);
msg.Result |= DLGC_WANTCHARS; // TODO
}
//---------------------------------------------------------------------------
void __fastcall TMyListViewEx::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) &&
(FOnCancelEdit != NULL))
{
// ideally, you should be using TCustomListView::GetItem(LVITEM)
// to determine the TListItem affected, but that method is private
// and not accessible to descendants, which is all the more reason
// why Embarcadero needs to fix this in the native TListView instead...
TListItem *item;
if (plvdi->item.mask & LVIF_PARAM)
item = reinterpret_cast<TListItem*>(plvdi->item.lParam);
else // TODO: handle OwnerData=true ...
item = this->Items->Item[plvdi->item.iItem];
FOnCancelEdit(this, item);
}
}
}
//---------------------------------------------------------------------------
namespace Tmylistviewex
{
void __fastcall PACKAGE Register()
{
TComponentClass classes[1] = {__classid(TMyListViewEx)};
RegisterComponents(L"Samples", classes, 0);
}
}
//---------------------------------------------------------------------------
TMyListViewEx.h
//---------------------------------------------------------------------------
#ifndef TMyListViewExH
#define TMyListViewExH
//---------------------------------------------------------------------------
#include <System.SysUtils.hpp>
#include <System.Classes.hpp>
#include <Vcl.ComCtrls.hpp>
#include <Vcl.Controls.hpp>
//---------------------------------------------------------------------------
class PACKAGE TMyListViewEx : public TListView
{
private:
TLVNotifyEvent FOnCancelEdit;
MESSAGE void __fastcall WMGetDlgCode(TMessage &msg);
MESSAGE void __fastcall CNNotify(Winapi::Messages::TWMNotify &Message);
BEGIN_MESSAGE_MAP
VCL_MESSAGE_HANDLER(WM_GETDLGCODE, TMessage, WMGetDlgCode)
VCL_MESSAGE_HANDLER(CN_NOTIFY, TWMNotify, CNNotify)
END_MESSAGE_MAP(TListView);
protected:
public:
__fastcall TMyListViewEx(TComponent* Owner);
__published:
__property TLVNotifyEvent OnCancelEdit = {read = FOnCancelEdit, write = FOnCancelEdit};
};
//---------------------------------------------------------------------------
#endif