Accessing parent window from a dialog in mfc - c++

I am making a doc/view arch SDI application.
I invoke a COptionsDialog in CSquaresView.
void CSquaresView::OnOptions()
{
COptionsDialog dlg(this);
if (dlg.DoModal() == IDOK)
...
}
In COptionsDialog I want to access CSquaresView.
BOOL COptionsDialog::OnInitDialog()
{
CDialog::OnInitDialog();
CWnd *pParent = GetParent();
if (pParent) {
CSquaresView *pView = dynamic_cast<CSquaresView*>(pParent); //pView is always NULL
if (pView != NULL)
{
CSquaresDoc* pDoc = pView->GetDocument();
...
}
But I always get pView as NULL;
Please help me to solve this problem.

The observed behavior makes sense. A (modal) dialog's owner must be
an overlapped or pop-up window [...]; a child window cannot be an owner window.
CView-derived class instances generally are child windows. As such they cannot be the owner of a (modal) dialog. When you pass a child window into the c'tor of a CDialog-derived class, the system walks up the window hierarchy until it finds an overlapped or pop-up window, and uses that as the owner of the dialog. Regardless of whether you then call GetParent, GetAncestor, or CWnd::GetOwner, it is this true owner (usually your CFrameWnd-derived implementation) where window traversal starts.
Thus, you cannot generally use standard window traversal to find the window passed into a (modal) dialog's constructor. However, MFC records the CWnd(-derived) class instance you pass into your COptionsDialog constructor and stores it in a protected member variable m_pParentWnd, inherited from the CDialog class.
As long as COptionsDialog derives public/protected from CDialog or CDialogEx, the implementation can access this class member.
The following OnInitDialog implementation will do what you're looking for:
BOOL COptionsDialog::OnInitDialog()
{
CDialog::OnInitDialog();
CSquaresView *pView = dynamic_cast<CSquaresView*>(m_pParentWnd);
if (pView != NULL)
{
CSquaresDoc* pDoc = pView->GetDocument();
...
}
There are other options available. For example, you could supply a COptionsDialog constructor that takes both a CWnd* and a CSquaresDoc*, delegating the first onto the base class c'tor and storing the document pointer in a (private) class member. This makes for code that's easier to follow in that it explicitly spells out, that the dialog depends on the document.

Add a static method GetCurrentView() to your view class:
.cpp file
CSquaresView* CSquaresView::GetCurrentView()
{
CFrameWnd* pFrame = (CFrameWnd*)(AfxGetApp()->m_pMainWnd);
return dynamic_cast<CSquaresView*>(pFrame->GetActiveView());
}
.h file
class CSquaresView : public CView
{
...
static CSquaresView* GetCurrentView();
...
};
Now you can just call GetCurrentView() from anywhere you want:
...
CSquaresView *pView = CSquaresView::GetCurrentView();
...
This works for SDI applications. For MDI applications the solution might be somewhat different.
Another thing you could do is just create a public member CSquaresView *m_pView in COptionsDialog and set that to this like:
void CSquaresView::OnOptions()
{
COptionsDialog dlg(this);
dlg.m_pView = this;
if (dlg.DoModal() == IDOK)
...
}
or modify the COptionsDialog constructor so m_pView is set directly by the constructor, or something similar.

Use GetOwner(). Only WS_CHILD windows have parents, other windows have owners.

Related

How to Stop a CChildView Timer from Modeless Dialog

I have created a timer with SetTimer( 1 , 25 , 0 ); during CChildView::PreSubclassWindow().
In CChildView::OnTimer() I do InvalidateRect( 0 , 0 ); to cause an CChildView::OnPaint(). This causes a record from a file to be read and painted.
All of this works just fine.
During MyApp::InitInstance() I created a modeless dialog with several controls that all work as expected.
What I want to do is control the timer running in CChildView from a dialog control.
The problem is, I can't find or have access to the instance of CChildView.
Hence I can't call my SetTimerSpeed() function in CChildView.
How can I do this, please?
When creating the dialog store a pointer to your view in the dialog object. You can just use this one to access the view instance. Add a view-pointer member in your dialog class declaration:
class CMyDialog : public CDialogEx
{
public:
CChildView *pView;
.
.
}
And when calling it:
CMyDialog ctDlg;
ctDlg.pView = this;
ctDlg.DoModal();
// or ctDlg.Create();
The code above of course can be called from a handler (function) in CChildView.
If the application is SDI you don't even need this, you can just store the pointer to the view in a global variable, it is very much OK:
// Declaration in the .h file
extern CChildView *pChldVw;
// Definition in the .cpp file
CChildView *pChldVw;
// CChildView constructor
CChildView::CChildView() noexcept
{
pChldVw = this;
}
If the application is MDI and the dialog is not created in the CChildView code, it is still possible to find the view instance by calling something like:
CMDIFrameWnd *pFrame = (CMDIFrameWnd*)AfxGetApp()->GetMainWnd();
// Get the active MDI child window.
CMDIChildWnd *pChild = (CMDIChildWnd*)pFrame->GetActiveFrame();
// Get the active view attached to the active MDI child window.
CChildView *pView = (CChildView)pChild->GetActiveView();
There are also some "enumeration" functions, like:
GetFirstDocTemplatePosition() / GetNextDocTemplate()
GetFirstDocPosition() / GetNextDoc()
GetFirstViewPosition() / GetNextView()

Create MFC controls in CDialog constructor [win32/MFC]

I'm working on the library, that wraps some MFC classes and methods. I want the user to be able to dynamically create a CDialogEx using a template in memory. For the modal dialogs, I call CDialog::InitModalIndirect and then CDialog::DoModal. For the modeless dialogs, I call CDialog::CreateIndirect and then CWnd::Show.
The code looks something like this:
// inside my library
class MyDialog : public CDialogEx
{
public:
MyDialog(CWnd* parent) : CDialogEx()
{
parent_ = parent;
my_template_data_ = CreateSomeGenericTemplate();
// OnInitDialog should be preferably called here
}
void ShowModal()
{
InitModalIndirect(my_template_data_, parent_);
DoModal(); // but it's called here - too late
}
void ShowModeless()
{
CreateIndirect(my_template_data_, parent_);
Show(); // but it's called here - too late
}
MyButton* GetButton(int id)
{
// returns the instance of my MyButton, which is a subclassed CButton
}
private:
BOOL MyDialog::OnInitDialog() override
{
CDialogEx::OnInitDialog();
// CWnd::Create for the UI controls can only be called here
}
};
// user's code
// user creates the dialog - in the constructor it's not clear if modal or modeless
1. MyDialog user_dialog(some_parent); // here, I need the controls to be created
2. user_dialog.GetButton(42)->SetWindowText(L"new text"); // user wants to initialize his controls
// but he can't, because MyButton::Create was not called yet
3. user_dialog.ShowModal(); // and only then display the dialog
//by default, here the MFC calls OnInitDialog - too late,
//the SetText method needed to be set on line 2.
My problem is, that the dialog's controls (buttons etc.) can only be created inside the CDialog::OnInitDialog method, which is called automatically after DoModal (for modal)/Show (for modeless) methods. I need the controls to be created and properly initialized (with the CWnd::Create method) preferably inside the constructor. I thought about calling Show/DoModal directly inside the constructor but I don't yet know if it's going to be modal or modeless dialog. It there a solution to this? Many thanks in advance.
Why don't you refactor your code and put the common code in a InitUI() method and call it from both sides?
And don't init interface on the constructor. Do it in OnInitDialog for modal; and in Create, or OnCreate (which implies a ON_WM_CREATE() map entry) for modeless dialogs.

MFC Edit control message handle access Document object

I create a new class extend CEdit to override some of the message handles.
My ultimate goal is when edit control is on focus, some of the toolbar buttons become available.
I created a bool variable in doc. then the pCmdUI->enable() set to this bool. The onfocus is overridden in new edit control class. I'm having trouble to update this bool vairbale from the onfocus message handle.
void CMFCDoc::OnUpdateTextColor(CCmdUI *pCmdUI)
{
// TODO: Add your command update UI handler code here
pCmdUI->Enable(shape_onfocus_);
}
class CMFCDoc : public COleServerDoc
{
...
bool shape_onfocus_;
}
//edit control
#include <afxwin.h>
class CEditControl :
public CEdit
{
public:
CEditControl();
~CEditControl();
DECLARE_MESSAGE_MAP()
afx_msg void OnEnSetfocus();
};
void CEditControl::OnEnSetfocus()
{
//----- I want to update shape_onfocus_ here. -----
this->SetWindowTextA(_T("Hello world"));
}
Assuming your CEditControl instance is a child of some sort of CView, you could go about it like this:
void CEditControl::OnEnSetfocus()
{
CView *view = static_cast<CView *>(GetParent());
CMFCDoc *doc = static_cast<CMFCDoc *>(view->GetDocument());
doc->shape_onfocus_ = true;
...
}
Assuming the edit-control is a child of a CView-derived class, you should better put the OnUpdateUI() handler in the view class, not the document one.
For example, if the view-class is CFormView-derived (dialog), you could simply write:
void CMyView::OnUpdateTextColor(CCmdUI *pCmdUI)
{
pCmdUI->Enable(GetFocus()==GetDlgItem(IDC_MYEDIT));
}
This piece of code works for both SDI and MDI applications.
If the view class is not CFormView-derived (the edit-box was created programmatically), the code above could be modified slightly, and instead of calling GetDlgItem() you should enumerate the view's children list (search your edit-box there).
If the only reason to override the edit-control was to capture the EN_SET/KILLFOCUS messages, sorry this wasn't worth the effort, as you could simply capture these in the view's code. Then the view's message-map would contain:
ON_EN_SETFOCUS(IDC_MYEDIT, &CMyView::OnEnSetfocusMyEdit)
ON_EN_KILLFOCUS(IDC_MYEDIT, &CMyView::OnEnKillfocusMyEdit)
and the view-class code:
void CMyView::OnEnSetfocusMyEdit()
{
// TODO: Add your control notification handler code here
}
void CMyView::OnEnKillfocusMyEdit()
{
// TODO: Add your control notification handler code here
}
These are generated by the wizard. Go to the Class View tab, select your class and then go to the Events page; in the Controls subtree you can find your control and add handlers for its events. But all this is not needed, as you can just use GetFocus()/GetDlgItem() as suggested above.
And as other members said, you can access the document class from any of its views by calling the GetDocument() function.

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).

mfc access to formview item from dialog box

In my SDI application i need to get this behawiour. After I click on a button on the FormView, a CDialog opens. When I press the OK button on the CDialog, I call a function of the FormView. I don't want to close the CDialog. I try to do it with modeless dialog, but when i call formview function from dialog, i can't access to formview's control, like it's lost hwnd; the error is can't read memory of m_hwnd, the hwnd is ???.
This is my code:
Open modeless dialog:
CCampiDlg *m_pDialog = NULL;
HWND hCampi = NULL;
// Invoking the Dialog
m_pDialog = new CCampiDlg;
if (m_pDialog != NULL)
{
BOOL ret = m_pDialog->Create(m_pDialog->IDD, this);
if (!ret) //Create failed.
{
AfxMessageBox(_T("Error creating Dialog"));
}
m_pDialog->ShowWindow(SW_SHOW);
}
when i press the ok button in the dialog i do:
CEditorTxView pView;
box2 = (CEdit*)(GetDlgItem(IDC_CAMPI_BOX2));
box2->GetWindowTextW(campo);
pView.inserisciCampo(1, campo);
In inserisciCampo function in CEditorTxView (CFormView) i have to do operation with my control txtCtrl, but it's lost hwnd. The declaration of txtCtrl is in the CEditorTxView.h
CTx1 txtCtrl;
And initialize it in DoDataExchange function:
void CEditorTxView::DoDataExchange(CDataExchange* pDX)
{
CFormView::DoDataExchange(pDX);
DDX_Control(pDX, IDC_TX1, txtCtrl);
}
Someone can help me plz?
I can give you two answers here:
How to do what you are asking (get access to a control of the CFormView from a modeless dialog)
How to solve your underlying problem (communicate changes in a modeless dialog to the owner view)
For the first one, you have to declare a pointer to the view in the dialog class and initialize it in the constructor of the view:
class CCampiDlg : public CDialog
{
public:
CCampiDlg(CEditorTxView* pView, CWnd*pParent = NULL) // Change declaration to add pointer to view
: m_pView(pView)
{
}
// ... Whatever
private:
CEditorTxView* m_pView;
}
Now in your button handler:
CEdit* box2 = (CEdit*)(GetDlgItem(IDC_CAMPI_BOX2)); // Why not use a control variable?
box2->GetWindowTextW(campo);
m_pView->inserisciCampo(1, campo);
This should do what you are asking for. However, it is the wrong way to do it.
The problem with this approach is that the dialog knows way too much about its parent. It knows it is of type CEditorTxView and that it has a member called inserisciCampo, that takes a number and some text.
It shouldn't know that much. In fact, knowing anything about it, other than it is of type CView or even CWnd, is too much.
If the dialog knows about the view, you can't reuse the dialog with other views, and anytime the view changes its representation (what now is a textbox may be a combobox in the future, for example) the dialog must change accordingly.
The solution would be to send a message to the parent, explaining what's happened. Then the parent (the view) should know haw to handle that event. For example:
class CCampiDlg : public CDialog
{
public:
CCampiDlg(CWnd*pParent = NULL) {}
protected:
OnOk()
{
CString campo;
c_CampiBox2.GetWindowText(campo);
GetParent()->SendMessage(UWM_CAMPO2_SET, 0, (LPARAM)&campo);
}
}
In the view:
// It can be ON_REGISTERED_MESSAGE:
ON_MESSAGE(UWM_CAMPO2_SET, OnCampo2Set)
//...
LRESULT CEditorTxView::OnCampo2Set(WPARAM, LPARAM lParam)
{
CString* s = (CString*) lParam;
inserisciCampo(1, *campo);
return 0;
}
Now, you have decoupled the view and the dialog. The dialog knows nothing about the view. You can change its type, change the representation, even make it a dialog, and you don't have to change anything in the dialog. And if you need that same modeless dialog somewhere else, you just drop it there, create a message handler in the parent, and voilĂ !
For further explanations and better examples, check these articles:
Dialog and control design (Your case is explained in the section "Notifications to the environment")
Message management
On Ok button click the below code is running:
CEditorTxView pView;
box2 = (CEdit*)(GetDlgItem(IDC_CAMPI_BOX2));
box2->GetWindowTextW(campo);
pView.inserisciCampo(1, campo);
Note that, you are creating the new pView in stack and it does't attach with any window. You are not actually referring the view that already created and launched your dialog acting a parent. Revisit the above code and try the get the view:
Try the below code, if it is not working (Google it)
CFrameWnd * pFrame = (CFrameWnd *)(AfxGetApp()->m_pMainWnd);
CView * pView = pFrame->GetActiveView();