Cannot edit labels in a CListCtrl - c++

I'm building a project with MFC Feature Pack. Is this project I have a window which includes a CView, which includes a CListCtrl-derived object. The object includes the LVS_EDITLABELS flag.
Somehow I cannot edit the CListCtrl icon labels by two-time clicking (not double-clicking) on the icon label. After I select the item with a single click, a second click just flashes the item (button down turns text background to white, button up turns it back to blue) and the edit control never appears.
I reduced this problem to the simplest form, and even with a plain CListCtrl object I cannot edit the labels.
I also found that:
This problem occurs in VS2008. It doesn't occur in a similar project built in VS2003.
I am able to edit the labels if I build a CListView instead of a CView+CListCtrl.
I am also able to edit the labels if I build a CFormView and put the CListCtrl inside the resource dialog.
Here's some code in the simplest form: the .h file:
// vwTerminaisTeste.h
//
#pragma once
// vwTerminaisTeste view
class vwTerminaisTeste : public CView
{
DECLARE_DYNCREATE(vwTerminaisTeste)
protected:
vwTerminaisTeste(); // protected constructor used by dynamic creation
virtual ~vwTerminaisTeste();
CListCtrl m_lstTerminais;
protected:
DECLARE_MESSAGE_MAP()
virtual void OnDraw(CDC* /*pDC*/);
public:
afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
afx_msg void OnSize(UINT nType, int cx, int cy);
};
and the .cpp file:
// vwTerminaisTeste.cpp : implementation file
//
#include "stdafx.h"
#include "vwTerminaisTeste.h"
// vwTerminaisTeste
IMPLEMENT_DYNCREATE(vwTerminaisTeste, CView)
vwTerminaisTeste::vwTerminaisTeste()
{
}
vwTerminaisTeste::~vwTerminaisTeste()
{
}
BEGIN_MESSAGE_MAP(vwTerminaisTeste, CView)
ON_WM_CREATE()
ON_WM_SIZE()
END_MESSAGE_MAP()
// vwTerminaisTeste message handlers
void vwTerminaisTeste::OnDraw(CDC* /*pDC*/)
{
CDocument* pDoc = GetDocument();
}
int vwTerminaisTeste::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CView::OnCreate(lpCreateStruct) == -1)
return -1;
m_lstTerminais.Create(WS_CHILD | WS_VISIBLE | LVS_EDITLABELS, CRect(0,0,1,1), this, 0);
m_lstTerminais.InsertItem(0, "Teste", 0);
return 0;
}
void vwTerminaisTeste::OnSize(UINT nType, int cx, int cy)
{
CView::OnSize(nType, cx, cy);
if (IsWindow(m_lstTerminais.GetSafeHwnd()))
m_lstTerminais.MoveWindow(0, 0, cx, cy);
}
This way I cannot edit labels.
To change it to a CListView I simply replaced CView by CListView and m_lstTerminais by GetListCtrl(), and removed the OnCreate and OnSize implementations. That way it worked.
Note: the vwTerminaisTeste is created from a CSplitterWndEx within a CMDIChildWndEx-derived class.

Well nobody solved this problem but I managed to go around it by changing the CView to a CFormView and building a resource dialog with a ListView control, attaching it to the CListCtrl-derived class.
If anyone still has any suggestions on how could I solve this problem entirely, I'd appreciate them.

This sounds like it may be a focus or command routing issue, although that doesn't explain why it works OK in VS2003. You might try routing the command and/or focus messages from the splitter ctrl to vwTerminaisTeste, and/or from the MDIChild to the splitter. If you haven't already, you may need to derive your own splitter window. The command/focus forwarding would be something like...
BEGIN_MESSAGE_MAP(MySplitter, CSplitterWnd)
ON_WM_SETFOCUS()
END_MESSAGE_MAP(...)
void MySplitter::OnSetFocus(CWnd* pOldWnd)
{
// forward focus to the view window
m_vwTerminaisTeste.SetFocus();
}
BOOL MySplitter::OnCmdMsg(UINT nID, int nCode, void* pExtra, AFX_CMDHANDLERINFO* pHandlerInfo)
{
// let the view have first crack at the command
if (m_vwTerminaisTeste.OnCmdMsg(nID, nCode, pExtra, pHandlerInfo))
return TRUE;
// otherwise, do default handling
return MySplitter::OnCmdMsg(nID, nCode, pExtra, pHandlerInfo);
}

Related

I am extending CTabCtrl but but cant insert any tabs

I am extending CTabCtrl but when I call InsertItem on my extended object none tab gets inserted. Who knows why is that. What do I do wrong?
class MyTabControl : public CTabCtrl
{
public:
MyListControl m_listCtrl;
void switchInterface(IDataProvider *provider);
public:
MyTabControl();
~MyTabControl();
afx_msg void OnGetDispInfo(NMHDR *pNMHDR, LRESULT *pResult);
protected:
afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
DECLARE_MESSAGE_MAP()
};
If I remove ON_WM_CREATE() macro from message map then I can add tabs. Implementation of OnCreate function contains m_listCtrl.Create() function call and return 0 if list control is created successfully. What is wrong with this?
The CTabCtrl class is terribly old and poorly functional; you will have to do all the showing/hiding logic of controls when user is switching from one tab to another by your own hand. I recommend you to extend from CMFCTabCtrl instead.

MFC click and move/drag dialog window

I am currently working on finishing some code handed off to me. It was written in MFC in Visual Studio 2005 years ago, was put on hold, and now is brought to me.
While I know C++, I have spent the last ~2 months studying the code and learning MFC and it's starting to come together.
The GUI for the code is an SWF flash file embedded in an invisible dialog window. I do not have the source code for the SWF file so will probably, in the future, redo it in WPF or something. I have the WMMode set to "Window" because in Transparent/Opaque mode it doesn't display properly, where it flashes/blinks everytime a mouse event is captured.
Anyhow, in Win XP/Vista, clicking and dragging the flash control works. In windows 7/8.1, it won't move.
Happy to provide any and all info needed. I'm still a little overwhelmed by MFC dialogs so unsure what you'd all like to see.
I found this question: Moving window by click-drag on a control
Which looks like it solves a lot of the issue. However, I don't want the whole control to be clickable like this, only the top part. Unfortunately, in the MS Resource view, the ActiveX control is blank as the SWF is only loaded at runtime; I've tried to find resources for this kind of thing but it's very difficult as I am unsure of the technical terms to use.
EDIT
I have attempted this by creating a very simple MFC app that has a Static Text control and nothing else. I am trying to get it to work by clicking on the static text (though I may be painting myself into a corner as it does not have a built-in lButtonDown event).
Here is the relevant code:
class MyDialog : public CDialog
{
public:
MyDialog(CWnd* pParent = NULL) : CDialog(MyDialog::IDD, pParent)
{ }
// Dialog Data, name of dialog form
enum{ IDD = INTERFACE1 };
protected:
virtual void DoDataExchange(CDataExchange* pDX) { CDialog::DoDataExchange(pDX); }
//Called right after constructor. Initialize things here.
virtual BOOL OnInitDialog()
{
CDialog::OnInitDialog();
pText = (CStatic *)GetDlgItem(ID_TEXT);
pText->SetWindowTextW(_T("Hello World!"));
return true;
}
afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
private:
CStatic * pText;
public:
DECLARE_MESSAGE_MAP()
};
BEGIN_MESSAGE_MAP(MyDialog, CDialog)
ON_WM_LBUTTONDOWN()
END_MESSAGE_MAP()
Overridden Method:
afx_msg void MyDialog::OnLButtonDown(UINT nFlags, CPoint point)
{
CWnd::OnNcLButtonDown(HTCAPTION, point);
}
I have also tried setting nFlags to 0x2, calling OnLButtonDown (as opposed to onNcLButtonDown), various other things. The message fires but the window does not move (it does move from the title bar, as normal). What am I missing?
Actually lets try this code instead with ON_WM_NCHITTEST(). This will drag the dialog if you click the mouse anywhere in client area (client area acts as caption). There is a line rc.bottom = rc.top + 100 if you uncomment it then it will only drag if you click the top section (I picked the number 100 at random).
//declare:
afx_msg LRESULT OnNcHitTest(CPoint point);
BEGIN_MESSAGE_MAP(MyDialog, CDialog)
ON_WM_NCHITTEST()
END_MESSAGE_MAP()
LRESULT MyDialog::OnNcHitTest(CPoint point)
{
ScreenToClient(&point);
CRect rc;
GetClientRect(&rc);
//rc.bottom = rc.top + 100;
if (rc.PtInRect(point))
return HTCAPTION;
return CDialog::OnNcHitTest(point);
}
Second option:
If we want to move the dialog by clicking on a child control, and if that control captures the mouse, then try this method instead. ***Note, test to make sure the control works properly after it is moved.
BOOL MyDialog::PreTranslateMessage(MSG *msg)
{
if (msg->message == WM_MOUSEMOVE && (msg->wParam & MK_LBUTTON))
{
CPoint p;
GetCursorPos(&p);
CRect r;
ActiveX->GetWindowRect(&r);
if (r.PtInRect(p))
{
ReleaseCapture();
SendMessage(WM_NCLBUTTONDOWN, HTCAPTION, 0);
SendMessage(WM_NCLBUTTONUP, HTCAPTION, 0);
return 1;
}
}
return CDialogEx::PreTranslateMessage(msg);
}

Why child control doesn't appear in MFC derived CWnd

I want to add simple Cedit to my derived GUI class from CWnd. This class is simple container and treat the same as Panel in MFC. in constructor of class I add simple CEdit instance in the class.
but the when I instantiated the panel in client dialog, the panel shows but the button does not show.why it doesn't show.
The panel code
Panel header file
#pragma once
#include "afxwin.h"
class CPanel :
public CWnd
{
public:
CPanel(void);
~CPanel(void);
virtual void PreSubclassWindow();
virtual void DoDataExchange(CDataExchange* pDX);
CEdit *txt;
DECLARE_MESSAGE_MAP()
};
panel source file
#include "stdafx.h"
#include "Panel.h"
CPanel::CPanel(void)
{
WNDCLASS wndcls;
HINSTANCE hins=AfxGetInstanceHandle();
if(!(::GetClassInfo(hins,_T("CPanelCtrl"),&wndcls))){
wndcls.style=CS_DBLCLKS|CS_HREDRAW|CS_VREDRAW;
wndcls.lpfnWndProc=::DefWindowProc;
wndcls.cbClsExtra=wndcls.cbWndExtra=0;
wndcls.hInstance=hins;
wndcls.hIcon=NULL;
wndcls.hCursor=AfxGetApp()->LoadStandardCursor(IDC_CROSS );
wndcls.hbrBackground=(HBRUSH)(COLOR_3DFACE+13);
wndcls.lpszMenuName=NULL;
wndcls.lpszClassName=_T("CPanelCtrl");
txt=new CEdit();
txt->Create(ES_PASSWORD,CRect(10,10,25,35),this,1);
if (!AfxRegisterClass(&wndcls))
{
AfxThrowResourceException();
return;
}
else{
return;
}
}
}
CPanel::~CPanel(void)
{
}
void CPanel::PreSubclassWindow()
{
// TODO: Add your specialized code here and/or call the base class
CWnd::PreSubclassWindow();
}
void CPanel::DoDataExchange(CDataExchange* pDX)
{
// TODO: Add your specialized code here and/or call the base class
CWnd::DoDataExchange(pDX);
}
BEGIN_MESSAGE_MAP(CPanel, CWnd)
END_MESSAGE_MAP()
In dialog box in OninitDialog method I do like this
panel=new CPanel();
panel->Create(L"CPanelCtrl",L"Hello ", WS_VISIBLE , CRect(70, 70, 400, 200), this, 1);
Two hints:
first: you assign to your CEdit the same ID as to the CPanel: 1.
Second: you create CEdit inside CPanel constructor, I would try creating it inside CPanel WM_CREATE handler, because inside CPanel constructor HWND of CPanel is not yet assigned.
Your code to create the edit controls only runs once, because after the window class is registered the edit control is never be created.
You can't create a child window, when the parent window is created.
Create child windows on in the WM_CREATE handler of the parent.
If you always have an edit control, why do you use a pointer to it. Just create a simple member.
You should use WS_CHILD if the edit control is located inside the panel.

Graying out a wizard's Close button in WTL

I'm writing a WTL Aero wizard and I'd like to gray out the window's Close button (its first step will require no user interaction and can not be canceled, so disabling the button is perfectly appropriate).
Putting the following code:
CMenuHandle pMenu = GetSystemMenu(FALSE);
pMenu.EnableMenuItem(SC_CLOSE, FALSE);
in OnInitDialog does not work, as the procedure is called before the window itself is displayed on the screen (the ATLASSERT(::IsMenu(m_hMenu)); assertion in EnableMenuItem is tripped at runtime).
Is there an elegant way to disable the Close button? (I'm a WTL beginner, and I'd like the solution to be as clean as possible).
This is a minimal version of the wizard's page code:
#include "stdafx.h"
class MainPage : public CAeroWizardPageImpl<MainPage> {
public:
BEGIN_MSG_MAP(MainPage)
MESSAGE_HANDLER_EX(WM_INITDIALOG, OnInitDialog)
CHAIN_MSG_MAP(__super)
END_MSG_MAP()
enum {
IDD = IDR_MAINFRAME
};
MainPage() : CAeroWizardPageImpl<MainPage>(IDR_MAINFRAME) {
/* Set the wizard's title */
m_headerTitle.LoadString(IDS_INSTALLHEADER);
SetHeaderTitle(m_headerTitle);
}
private:
CString m_headerTitle;
LRESULT OnInitDialog(UINT message, WPARAM wParam, LPARAM lParam) {
UNREFERENCED_PARAMETER(message);
UNREFERENCED_PARAMETER(wParam);
UNREFERENCED_PARAMETER(lParam);
/* Disable the wizard buttons and center the window */
ShowWizardButtons(0, 0);
EnableWizardButtons(PSWIZB_BACK, 0);
CenterWindow();
return TRUE;
}
};
The close [X] button is a part of Common Controls wizard property sheet class. You are not supposed to alter its presentation and behavior. What you can do is to handle PSN_QUERYCANCEL notification and prevent wizard from closing. With WTL it is easy, however you need to know that that there are two versions of notifications handlers available.
If _WTL_NEW_PAGE_NOTIFY_HANDLERS is defined, typically in stdafx.h, then you do it like this:
class MainPage :
public CAeroWizardPageImpl<MainPage>
{
// ...
INT OnQueryCancel()
{
return 1; // Zero to Allow Wizard Close
}
};
Otherwise, older syntax is in use:
class MainPage :
public CAeroWizardPageImpl<MainPage>
{
// ...
BOOL OnQueryCancel()
{
return FALSE; // Allow Wizard Close?
}
};
Along with preventing cancellation/close you are free to indicate this by showing a message box suggesting use to wait until the pending operation is completed, or otherwise display a notification (e.g. flash a static control etc.)

Handling WM_PAINT in a Subclassed CStatic Control

I created a custom control whose class has CStatic as base class. Currently I handle the drawing using WM_PAINT event. But there is a strange behavior. When I re-enable the window after disabling it using CWnd::EnableWindow function, it refuses to draw what I written in OnPaint function. It draws the static control instead.
I agree that there is this standard method of overriding DrawItem and using SS_OWNERDRAW style. But what's wrong with WM_PAINT?
void XXControl::OnPaint()
{
CPaintDC PaintDC( this );
// ** draw the control to PaintDC**
}
Here is exactly what I have written:
class CMyStatic : public CStatic
{
DECLARE_MESSAGE_MAP()
public:
void OnPaint(void);
};
BEGIN_MESSAGE_MAP(CMyStatic, CStatic)
ON_WM_PAINT()
END_MESSAGE_MAP()
void CMyStatic::OnPaint(void)
{
CPaintDC dc(this);
CRect rect;
GetClientRect(&rect);
dc.FillSolidRect(&rect, RGB(120,255,0));
}
And subclassed:
class CMyDlg : public CDialog
{
// Construction
CMyStatic my_static;
...
};
BOOL CCMyDlg::OnInitDialog()
{
CDialog::OnInitDialog();
my_static.SubclassDlgItem(IDC_DRAW, this);
return true;
}
Where IDC_DRAW is static control on resource for this dialog. I wrote two button handlers:
void CMyDlg::OnBnClickedOk()
{
my_static.EnableWindow(FALSE);
my_static.Invalidate();
}
void CMyDlg::OnBnClickedOk2()
{
my_static.EnableWindow();
my_static.Invalidate();
}
And it works flawlessly! Remove Invalidate call and it would fail.
Try turning off Aero. I'm having a similiar problem where I'm drawing a static control and when it goes from disabled to enabled the WM_PAINT message is never received, but if I turn Aero off it works fine.