I am extending CTabCtrl but but cant insert any tabs - c++

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.

Related

What's wrong with my attempt at subclassing CButton?

I tried to create a subclassed control for the first time, but I feel like I did something wrong. The control is a Button, which I placed in the designer. This is its class:
class TTTField : public CButton
{
public:
BEGIN_MSG_MAP_EX(TTTField)
MSG_WM_INITDIALOG(OnInitDialog);
END_MSG_MAP()
TTTField operator=(const CWindow& btn);
private:
const BOOL OnInitDialog(const CWindow wndFocus, const LPARAM lInitParam);
};
Nothing fancy so far.
However, I can't really achieve to receive windows messages in this control. This is bad, considering the main reason for trying to subclass a control was the fact that this should be a reusable class with reusable, custom Paint behaviour. I want to overwrite certain message handlers, while keeping those I didn't explicitely ask for to the usual CButton routine.
As you can see, I implemented a message map, but the messages are just not coming in.
This is how I tried to setup the instance of this class:
TTTField fld;
is a member variable of my main dialog class. In this class I added the following DDX_MAP:
BEGIN_DDX_MAP(TTTMainDialog)
DDX_CONTROL_HANDLE(IDC_BTN, fld)
END_DDX_MAP()
with IDC_BTN being the id of the button on the designer.
In the assignment operator overload for TTTField I have the following:
TTTField TTTField::operator=(const CWindow& btn)
{
Attach(btn);
return *this;
}
I feel like this operator overload might be the source of my problems, but I just can't manage to find a website which is properly explaining the whole topic without using code which seems outdated for like 20 years.
What am I doing wrong here? I am really lost right now.
The button class should be defined as follows:
class TTTField : public CWindowImpl<TTTField, CButton>
{
protected:
BEGIN_MSG_MAP_EX(TTTField)
MSG_WM_LBUTTONDOWN(OnLButtonDown)
END_MSG_MAP()
protected:
LRESULT OnLButtonDown(UINT, CPoint)
{
//Edit: this override is meant for testing the subclass only
//it's insufficient for handling button clicks
MessageBox(L"Testing override...");
return 0;
}
};
Override dialog box's OnInitDialog, call SubclassWindow to subclass the button:
class TTTMainDialog: public CDialogImpl<CMainDialog>
{
public:
enum { IDD = IDD_MYDIALOG };
BEGIN_MSG_MAP(TTTMainDialog)
MESSAGE_HANDLER(WM_INITDIALOG, OnInitDialog)
END_MSG_MAP()
TTTField fld;
LRESULT OnInitDialog(UINT nMessage, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
fld.SubclassWindow(GetDlgItem(IDC_BTN));
return 0;
}
};
Edit, for initialization
class TTTField : public CWindowImpl<TTTField , CButton>
{
public:
void Create(CWindow *wnd, int id)
{
SubclassWindow(wnd->GetDlgItem(id));
//add initialization here
}
...
}
Then to create the button:
//fld.SubclassWindow(GetDlgItem(IDC_BTN));
fld.Create(this, IDC_BTN); //<== use this instead
Perhaps the best example, or at least one of, of subclassing a button is right in the sources of WTL, at the top of atlctrlx.h:
template <class T, class TBase = CButton, class TWinTraits = ATL::CControlWinTraits>
class ATL_NO_VTABLE CBitmapButtonImpl : public ATL::CWindowImpl< T, TBase, TWinTraits >
{
public:
DECLARE_WND_SUPERCLASS(NULL, TBase::GetWndClassName())
...
You will also file external resources on this class: Using WTL's CBitmapButton.
That's not to mention WTL's comment on doing the controls:
// These are wrapper classes for Windows standard and common controls.
// To implement a window based on a control, use following:
// Example: Implementing a window based on a list box
//
// class CMyListBox : CWindowImpl<CMyListBox, CListBox>
// {
// public:
// BEGIN_MSG_MAP(CMyListBox)
// // put your message handler entries here
// END_MSG_MAP()
// };
More examples of simple and sophisticated custom WTL controls can be found at viksoe.dk.
A confusing thing about WTL control extension is that basic classes like CButton, CComboBox are thin wrappers over standard controls. They mostly translate methods into messages to be sent. You can often easily cast instances of such classes to HWND and back.
Standard controls themselves offer a level of customization through support of notification messages.
When you subclass a control, you are adding functionality on your side which somehow needs to interoperate with stock implementation, and control classes are no longer thin wrappers. Hence, you inherit from CWindowImpl and not CButton directly. Next challenge is to specifically subclass: you need to have original window created and after that, having a HWND handle, you modify it to route the messages through your message map. This is where you need SubclassWindow method. That is, you have the control created, you look it up its handle, e.g. with GetDlgItem and then you subclass the window using your class instance SubclassWindow call. Or, alternatively you can create the control using your new class Create method in which case CreateWindow and association with your message map will be done for you.
Some, more complicated, implementations of custom controls will also want you to reflect notification messages from parent window to the controls, so that they could handle them within the same custom control class. This will typically require that you add a line REFLECT_NOTIFICATIONS in your dialog class message map (see this related question on this).

Changing static text in dialog box at runtime

I have created a dialog box and linked it to the menu item. In this case the menu item is Help -> Statistics. It all works. So when I run the program, click on the menu Help, then Statistics, a dialog box pops up.
I also have a static text box in the dialog box. How do you change the text of this static text box at runtime?
P.S: Though I have a dialog box up and running, I do not have the handle for the dialog box. If any of your solutions involve knowing the handle to the dialog box, please tell me how to retrieve it. Thanks.
EDIT:
class CStatisticsDlg : public CDialogEx
{
public:
CStatisticsDlg();
// Dialog Data
enum { IDD = IDD_STATISTICS };
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
// Implementation
protected:
DECLARE_MESSAGE_MAP()
public:
};
CStatisticsDlg::CStatisticsDlg() : CDialogEx(CStatisticsDlg::IDD)
{
}
void CStatisticsDlg::DoDataExchange(CDataExchange* pDX)
{
CDialogEx::DoDataExchange(pDX);
}
BEGIN_MESSAGE_MAP(CStatisticsDlg, CDialogEx)
END_MESSAGE_MAP()
In Class Wizard, create a CString member variable for the label. Note: by default, labels don't have a custom id so you have to give it one like IDC_MY_LABEL.
Somewhere before showing the dialog call m_strMyLabel.SetWindowText("blah");
If you need to do it while the dialog is open you have to call UpdateData(FALSE)
Edit: if you don't want to create a member variable you can
**corrected - typing from memory....
// Find the label
// if called from within CStatusDlg class
CWnd *label = GetDlgItem(IDC_MY_LABEL);
label->SetWindowText("blah");
// If called from elsewhere
CStatusDlg dlg..... // create the dialog
CWnd *label = dlg.GetDlgItem(IDC_MY_LABEL);
label->SetWindowText("blah");

C++/MFC Error accessing control's variable

I created a control's variable for CEdit:
class CGateDlg : public CDialog
{
...
public:
// here is my control's variable
CEdit m_edit_a;
// here I map variable to control
virtual void DoDataExchange(CDataExchange* pDX);
}
And this is how I map my variable to the control:
void CGateDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
DDX_Control(pDX, IDC_EDIT_A, m_edit_a);
}
This is how it works: user types some text into the edit box. Then he presses the "Reset" button which clears the edit box. This is a piece of code responsible for clearing edit box after clicking Reset button:
void CGateDlg::OnBnClickedReset()
{
// clear edit box
m_edit_a.SetWindowTextW(L"");
}
Application starts without any errors. I type some text into EditBox and hit "Reset" button. Then I get an error which leads me to winocc.cpp, line 245 (ENSURE(this)):
void CWnd::SetWindowText(LPCTSTR lpszString)
{
ENSURE(this);
ENSURE(::IsWindow(m_hWnd) || (m_pCtrlSite != NULL));
if (m_pCtrlSite == NULL)
::SetWindowText(m_hWnd, lpszString);
else
m_pCtrlSite->SetWindowText(lpszString);
}
I think the problem is with the hWnd:
this 0x0030fa54 {CEdit hWnd=0x00000000} CWnd * const
but how to fix it ?
Everything works fine when I access my control's value using this:
CEdit *m_edit_a;
m_edit_a = reinterpret_cast<CEdit *>(GetDlgItem(IDC_EDIT_A));
m_edit_a->SetWindowTextW(L"");
What am I doing wrong ?
I can see two possibilities:
The control does not exist when the dialog starts. The first thing that CDialog::OnInitDialog will do is call DoDataExchange, so if you're creating the control later in the initialization process it's too late.
Your own OnInitDialog is not calling CDialog::OnInitDialog so DoDataExchange is not being called.
I think you should no use directly the meber of your control (in this case m_edit_a). Instead you should use a memeber variable, let's say CStrimg m_edit_data, and you should link it to the control:
DDX_Text(pDX, IDC_EDIT_A, m_edit_data); // as you did it in DDC_Cotrol
Now you can use directy the variable, but in order the control to be updated you should use the following code before using it:
UpdateData(true); // unlocks the control in a sense
m_edit_data = "this is my test";
UpdateData(false); // locks the control again (in a sense)
This is normal procedure in MFC :), hope I helped...
ohh... you should also add the control to String Table ... (let me know if you do not know)
I can not find something wrong with you. I Create a new project using VC6.0,and associate a variable to the Edit,just link you do. the exe operates normally.
class CEditTestDlg : public CDialog
{
// Construction
public:
CEditTestDlg(CWnd* pParent = NULL); // standard constructor
// Dialog Data
//{{AFX_DATA(CEditTestDlg)
enum { IDD = IDD_EDITTEST_DIALOG };
CEdit m_Edit;
//}}AFX_DATA
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CEditTestDlg)
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
//}}AFX_VIRTUAL
......
.cpp
void CEditTestDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CEditTestDlg)
DDX_Control(pDX, IDC_EDIT1, m_Edit);
//}}AFX_DATA_MAP
}
void CEditTestDlg::OnBnClickedReset()
{
// TODO: Add your control notification handler code here
m_Edit.SetWindowText("tttt");
}
so,I think it is not a code problem.You had better try again.
If your dialog starts off calling CDialog::OnInitDialog() and your DoDataExchange starts off calling CDialog::DoDataExchange but still you have null hWnd pointers and get CNotSupportedException, make sure your resource (rc) file's dialog template includes all the controls (IDC_) and such you have in DoDataExchange.
Check for overriding definitions if using a DLL that also provides resources.

Cannot edit labels in a CListCtrl

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);
}

Catching when user selects an item from a CComboBox

This is as basic as it gets.
I want to catch when the user selects an item from a CComboBox (actually, a subclass of CComboBox).
Tried lots of combinations of OnCblSelChange, OnCommand. Guess I haven't hit the right combo yet (no pun intended).
OS is Vista but I'm forcing an XP-style dialog (That shouldn't matter, should it?)
I'm able to catch events for classes derived from CEdit and CFileDialog.
I am at my wits end here. Any assistance would be ever-so appreciated.
Any source code would, of course, be more than ever-so appreciated.
Unfortunately, it seems that all messages (even SELEND_OK) for combo box changing are sent before the text has actually changed, so DoDataExchange will give you the previous text in the CComboBox. I have used the following method, as suggested by MSDN:
void MyDialog::DoDataExchange(CDataExchange* pDX)
{
DDX_Text(pDX, IDC_COMBO_LOCATION, m_sLocation);
CDialog::DoDataExchange(pDX);
}
BEGIN_MESSAGE_MAP(MyDialog, CDialog)
ON_CBN_SELENDOK(IDC_COMBO1, &MyDialog::OnComboChanged)
ON_CBN_EDITUPDATE(IDC_COMBO1, &MyDialog::OnComboEdited) // This one updates immediately
END_MESSAGE_MAP()
...
void MyDialog::OnComboChanged()
{
m_myCombo.GetLBText(m_myCombo.GetCurSel(), m_sSomeString);
}
void MyDialog::OnComboEdited()
{
UpdateData();
}
It seems to work quite nicely.
CBN_SELENDOK should be the message that you're looking for. It's sent after the user selection is finalized but before the combo box closes up (if it does). CBN_SELCHANGE is sent before the selection is actually saved to the combo box control.
This MSDN link has more information (you've likely seen it already...)
Here is the code I promised you. One thing I noticed when gathering this up is that it is possible to have this message suppressed if you are using an ON_CONTROL_REFLECT handler within the class derived from CComboBox. This would cause the control itself to handle the message and not pass it on to the parent. You can get around that problem by using ON_CONTROL_REFLECT_EX with the proper return code, which will make both the box itself and the parent receive the message.
Anyway, here's the code snippet:
class SPC_DOCK_CLASS ProcessingExceptionDockDlg : public CSPCDockDialog
{
SPC_DOCK_DECLARE_SERIAL(ProcessingExceptionDockDlg);
public:
// ... redacted ...
//{{AFX_DATA(ProcessingExceptionDockDlg)
CComboBox m_comboFilter;
//}}AFX_DATA
//{{AFX_VIRTUAL(ProcessingExceptionDockDlg)
protected:
virtual void DoDataExchange(CDataExchange* pDX);
//}}AFX_VIRTUAL
protected:
//{{AFX_MSG(ProcessingExceptionDockDlg)
afx_msg void OnSelendokComboTreeFilter();
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};
/****************/
// ProcessingExceptionDockDlg.cpp : implementation file
//
#include "stdafx.h"
#include "resource.h"
#include "ProcessingExceptionDockDlg.h"
// ... much code redacted ...
void ProcessingExceptionDockDlg::DoDataExchange(CDataExchange* pDX)
{
CSPCDockDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(ProcessingExceptionDockDlg)
DDX_Control(pDX, IDC_COMBO_TREE_FILTER, m_comboFilter);
//}}AFX_DATA_MAP
}
BEGIN_MESSAGE_MAP(ProcessingExceptionDockDlg, CSPCDockDialog)
//{{AFX_MSG_MAP(ProcessingExceptionDockDlg)
ON_CBN_SELENDOK(IDC_COMBO_TREE_FILTER, OnSelendokComboTreeFilter)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
void ProcessingExceptionDockDlg::OnSelendokComboTreeFilter()
{
// ... code redacted ...
}