create a control programmatically using MFC - c++

I just wonder how to do it.
I write :
CEdit m_wndEdit;
and in the button event handler (dialog app),
I write :
m_wndEdit.Create(//with params);
but I still don't see the control appear in the UI.
I actually wrote this in the button handler :
CWnd* pWnd = GetDlgItem(IDC_LIST1);
CRect rect;
pWnd->GetClientRect(&rect);
//pWnd->CalcWindowRect(rect,CWnd::adjustBorder);
wnd_Edit.Create(ES_MULTILINE | ES_NOHIDESEL | ES_READONLY,rect,this,105);
wnd_Edit.ShowWindow(SW_SHOW);
this->Invalidate();
id 105 doesn't exist. (I used it in the Create member function of CEdit). I just put it in there. isn't it supposed to be the id you want to give to the new control ? Should it already exist ?

Check with the following set of flags as the example mentioned in MSDN:
pEdit->Create(ES_MULTILINE | WS_CHILD | WS_VISIBLE | WS_TABSTOP | WS_BORDER | ES_NOHIDESEL | ES_READONLY,
rect, this, 105);

The Invalidate() is not necessary
Add the WS_VISIBLE flag to your create flags, you don't need the ShowWindow
You are creating the button on the location where IDC_LIST1 is - you probably want to do pWdn->Destroy() after the GetClientRect()
The id you pass to Create() can be anything, of course if you want to handle messages from this button later you'll need to use the correct id. In that case it's easiest to manually add an entry to resource.h.
What do you mean with 'I put this code in the button event handler' - which button? A different one from the one you're trying to create, I may hope? Does your code get called at all, does it stop when you put a breakpoint in? What's the value of wnd_Edit->m_hWnd after the call to Create()?
wnd_Edit is a member of your dialog, right, and not a a function local variable?

What is wnd_Edit exactly? If it's a local variable in that function, that is likely the problem. The CWnd destructor destroys the window associated with the CWnd. So when wnd_Edit goes out of scope, the edit box is destroyed too.
If that's not it, check the return value of Create(). Is it NULL? If it is, check the value of GetLastError().

Related

Create Button and Reference in message map

Im creating a button on my oncreate using message map.
I am unable to get a callback message from ON_BN_CLICKED when passing a reference to ICL_OK.
I don't believe its a parenting issue. The window is an CFrameWnd and parent is a CMainFrame.
Even getting all the messages and I can swtich between what I want to do as I have list boxes and input boxes to add also and edit / get response.
Thanks
Cant go into the gui main thread loop. Message map is the way I need to achieve this.
okBtn.Create(_T("Ok Button"), WS_TABSTOP | WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON,
CRect(10, 10, BUTTON_WIDTH, HEIGHT), this, ICL_OK);
To click the button and get a response. Instead in using OnCmdMsg and getting a reference to its nID which I don't like. I want BN_CLICKED to work.
Refering to this answer
Message map macros
I can again confirm oncmdmsg works but wm_command event does not fire.
Message map macros
UPDATE: Still not working, alternative is to use ON_COMMAND_RANGE and still fires the WM_COMMAND so just have to restrict the amount of messages it handles. Hope it helps someone. If you want to generate a button the solution below might help you.
You are writing that the button is not showing in the window. There is a reason for that, and I would guess this: You define the button in a subroutine/method/function instead of defining it in its parent class.
Instead, in its parent class, whether that is the CMainFrame or some other Window, define a button like:
class CMainFrame : public CFrameWnd
{
/// bunch of stuff, including OnCreate() or OnCreateClient()
CButton m_button;
};
In the class that houses the button, assuming CMainFrame for now, create the button... ideally in OnCreate() or OnCreateClient()
call the baseclass version then your button create....
int CMainFrame::OnCreate(LPCREATESTRUCT lpcs)
{
int ret = __super::OnCreate(lpcs);
if (ret != -1) {
m_button.Create(_T("Ok Button"), WS_TABSTOP | WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON, CRect(10, 10, BUTTON_WIDTH, HEIGHT), this, ICL_OK);
}
return ret;
}
If your constructor is in a method, then its destructor will be called at the end of the method. MFC CWnd derived windows classes usually call DestroyWindow() in their destructor and what this means is that the window is destroyed by the end of the call and that is the reason it is not visible.

MFC: How can I initialise CTabbedPane tabs (dialogs) using information from the Document in a SDI?

I'm fairly new to MFC and would like to create an SDI application that has a pane of tabs always embedded on the right of the window with a view left of it. In my app I have a calculation core, with variables that are changed in the tabs with edit boxes. I would like to initialise these variables in the calculation class and then during the initialisation of the dialogs used for tabs set the initial values in the edit boxes to those of the corresponding variable in the calculator.
Currently, I create an instance of the calculator in my document class.
I also create a CTabbedPane in the MainFrame OnCreate Method as follows:
m_TabbedPane.Create(_T(""), this, CRect(0, 0, 290, 200),
TRUE,
(UINT)-1,
WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS |
WS_CLIPCHILDREN | CBRS_RIGHT |
CBRS_FLOAT_MULTI))
m_tab = new CParametersDlg();
m_tab->Create(IDD_TAB, this);
m_TabbedPane.AddTab(m_tab);
I would the like to be able in CParameterDlg's OnInitDialog do something like:
BOOL CParameterDlg::OnInitDialog() {
CDialog::OnInitDialog()
float value = pointerToDocument->GetCalculatorVariable();
And use value to initialise an edit box. However I can't access the document from in the main frames OnCreate as it returns null (using GetActiveDocument, AfxGetApp etc).
How can I initialise the tabs then? I have thought about trying to put the Calculator in the App class instead. Or possibly trying to initialise the dialogs somewhere else which is called later when the document is properly initialised and linked? Or should I be doing things entirely differently?
I think that CMainFrame::OnCreate() is too early in the sequence of events to access the document class, it would not normally be created yet.
It would be better to wait until the docuent is created / initialised, the document class could then call a new method in CMainFrame() passing this as a parameter to create the tabs.

How to show CDockablePane after it has been closed

I understand CDockablePanes remember their state when the program is closed and then opened again, but I don't understand how I'm supposed to show a CDockablePane that has been closed in a previous use.
This is my initialisation code :
DWORD dwPaneStyle = WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | CBRS_FLOAT_MULTI;
MyPane.Create(_T("MyPane"), this, CRect(0, 0, 0, 120), TRUE, ID_VIEW_MYPANE, dwPaneStyle | CBRS_BOTTOM));
MyPane.ShowPane(TRUE, FALSE, TRUE);
So this would create my pane and then set it to be shown, but for some reason I can't see it in my main frame... What am I missing?
Also, I would like to be able to show the pane when a certain button is pushed. I already have my MessageMap and OnButtonFct() ready. I just need to know how to show the pane.
Thanks!
I found it... it seems MFC saves the entire state of how the program was left in. So when the app is launched, the panel is shown, but then the app loads the last state and hides the panel. I was able to make it visible again by using the same line :
MyPane.ShowPane(TRUE, FALSE, TRUE);
on a button push event instead of during the creation.
There seems to also be a way to disable the automatic "Load last state" behaviour by adding the following in the CWinAppEx constructor.
m_bSaveState = FALSE;

Create toolbar with event in MFC

Hi I'm having a problem with my single-document MFC application.
I want to add my own toolbar to MainFrm class (CFrameWnd).
I am a total newb with MFC. So I'm not sure that's even the place to add it.
So far:
A toolbar resource with id IDR_TOOLBAR1 is created
A toolbarbutton with id ID_SELECT_SHAPE
In MainFrm.h is CToolBar m_wndMyToolBar; declared
In MainFrm.cpp:
if (!m_wndMyToolBar.CreateEx(this, TBSTYLE_FLAT, WS_CHILD | WS_VISIBLE | CBRS_TOP | CBRS_GRIPPER | CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC) ||
!m_wndMyToolBar.LoadToolBar(IDR_TOOLBAR1))
{
TRACE0("Failed to create toolbar\n");
return -1; // fail to create
}
The toolbar is showing now. YAY!
But I don't know how to add the event handler.
Somebody who can tell if I'm on the right track? And if so, who can tell how to add that event?
Yes, you are on the right track. Your MainFrame.cpp should have a section that starts with
BEGIN_MESSAGE_MAP
and ends with
END_MESSAGE_MAP
Inside that section, you will need an entry
ON_COMMAND (ID_SELECT_SHAPE, &CFrameWnd::OnSelectShape)
In your .h file add a declaration
afx_msg void OnSelectShape();
and in the .cpp file implement the OnSelectShape function to handle your event.
Depending on what your handler needs to do and what data it needs to have, it may be easier to add the handler and implement it in the CView... class instead of the CFrameWnd class. Handlers can also be implemented in the CDocument... class. When the toolbar button is clicked, the MFC Doc-View framework will first look for a handler in the View. If there is no handler available, it then looks for one in the Document, and finally if there is not a handler there it will look for one in the main Frame window.

How to create controls at runtime?

How to create dynamic MFC controls and handle message maps of the controls at runtime?
It really depends on which controls do you want to create, especially if you want to know which flags should you set. In general it goes down to this:
Normally a CWnd-derived control is created using Create or CreateEx. For a CButton, for instance:
CButton button;
button.Create("Button text", WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON | DT_CENTER, CRect(5, 5, 55, 19), this, nID);
where the CRect specifies the button position, this is a pointer to the parent window, and nID is the control ID.
If the control doesn't come out as expected, it's probably because some flags are missing. I suggest you draw a sample control in design mode, check out the code for that control in the RC file, and copy the flags to the Create caller.
As for the message maps, they are normally routed to the parent window. The nID value you used in Create is important here, because it will be the number that identifies the control in the message map. If you have a fixed number of controls, you can hard-code the nID numbers for your controls (starting at 10000, for instance); if not, you'll have to provide a way for the parent window to identify them. Then you just add the message map entries.
ON_BN_CLICKED(10000, OnBnClicked)
ON_CONTROL_RANGE(BN_CLICKED, 10010, 10020, OnBtnsClicked)
You can use the ON_CONTROL_RANGE message map to map a range of IDs to the same function.