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.
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 thecancelled
parameter of theOnEditCancel
event instead. However, that event really should not have acancelled
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 asTLVNotifyEvent
.WM_CHAR
is a window message identifier, it is not a valid flag that you can add to the return value of theWM_GETDLGCODE
message. You meant to use theDLGC_WANTCHARS
flag instead, which will enable your control to receiveWM_CHAR
messages.LVN_ENDLABELEDIT
is a window message identifier, it is not a valid flag that you can add to the return value of theCN_NOTIFY
message. You need to check if theMessage.NMHdr->code
field matchesLVN_ENDLABELEDIT
and then process theMessage.NMHdr
field accordingly.you are handling the wrong message in your
MESSAGE_MAP
. You are catchingWM_NOTIFY
messages that are sent from the ListView's header control to the ListView. You need to instead catchCN_NOTIFY
messages, which areWM_NOTIFY
messages 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