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
CodePudding user response:
There are a lot of issues with your code.
TCustomListView1is not a good name for your component. Use something more meaningful.Your component doesn't need the
cancelmember at all, so get rid of it. You could just hard-code thecancelledparameter of theOnEditCancelevent instead. However, that event really should not have acancelledparameter 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 asTLVNotifyEvent.WM_CHARis a window message identifier, it is not a valid flag that you can add to the return value of theWM_GETDLGCODEmessage. You meant to use theDLGC_WANTCHARSflag instead, which will enable your control to receiveWM_CHARmessages.LVN_ENDLABELEDITis a window message identifier, it is not a valid flag that you can add to the return value of theCN_NOTIFYmessage. You need to check if theMessage.NMHdr->codefield matchesLVN_ENDLABELEDITand then process theMessage.NMHdrfield accordingly.you are handling the wrong message in your
MESSAGE_MAP. You are catchingWM_NOTIFYmessages that are sent from the ListView's header control to the ListView. You need to instead catchCN_NOTIFYmessages, which areWM_NOTIFYmessages that the ListView sends to its parent window and are then reflected back to the ListView asCN_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
