Run function after dialog is shown - c++

I am using a MFC wizard with CPropertyPages.
Is there any way of calling a function after the page is shown?
At the moment the function starts when I hit the "Next"-button of the previous page.
I tried to call the function from OnShowWindow, OnCreate, OnSetActive, DoModal, but none of them worked.
Thanks for your help!

Usually it's enough to override OnSetActive(). However, this method is called before the CPropertyPage is made visible and focused. If you have to perform a task after the page is shown, you have to post your own message in OnSetActive:
// This message will be received after CMyPropertyPage is shown
#define WM_SHOWPAGE WM_APP+2
BOOL CMyPropertyPage::OnSetActive() {
if(CPropertyPage::OnSetActive()) {
PostMessage(WM_SHOWPAGE, 0, 0L); // post the message
return TRUE;
}
return FALSE;
}
LRESULT CMyPropertyPage::OnShowPage(UINT wParam, LONG lParam) {
MessageBox(TEXT("Page is visible. [TODO: your code]"));
return 0L;
}
BEGIN_MESSAGE_MAP(CMyPropertyPage,CPropertyPage)
ON_MESSAGE(WM_SHOWPAGE, OnShowPage) // add message handler
// ...
END_MESSAGE_MAP()

Related

On Update command is not working for dynamically created button in MFC

In my code, i have added button in view class in OnCreate(). I included On Command and On Update COmmand funtionality. Here On command fucntion is working when i click the button. But On Update COmmand is not working. Im updating the pressing status of the button using this OnUpdateCommand().
In OnCreate()
rBar.left = 580;
rBar.right = 620;
cBZoomOut.Create("",WS_CHILD|WS_VISIBLE|BS_BITMAP ,rBar,this,IDC_TZOOMOUT);
cBZoomOut.SetIcon(IDI_TZOOMOUT);
rBar.left = 625;
rBar.right = 665;
cBZoomin.Create("",WS_CHILD|WS_VISIBLE|BS_BITMAP ,rBar,this,IDC_TZOOMIN);
cBZoomin.SetIcon(IDI_TZOOMIN);
Message maps for those buttons.
afx_msg void OnUpdateTzoomout(CCmdUI *pCmdUI);
afx_msg void OnTzoomin();
afx_msg void OnUpdateTzoomin(CCmdUI *pCmdUI);
afx_msg void OnTzoomout();
ON_UPDATE_COMMAND_UI(IDC_TZOOMOUT, &CTrendView::OnUpdateTzoomout)
ON_COMMAND(IDC_TZOOMIN, &CTrendView::OnTzoomin)
ON_UPDATE_COMMAND_UI(IDC_TZOOMIN, &CTrendView::OnUpdateTzoomin)
ON_COMMAND(IDC_TZOOMOUT, &CTrendView::OnTzoomout)
On command and OnUpdatecommand function:
void CTrendView::OnTzoomout()
{
sTimeStatus.Format("<=>%d",Minute/2);
}
void CTrendView::OnUpdateTzoomout(CCmdUI *pCmdUI)
{
if (Minute == 16)
pCmdUI->Enable(FALSE);
else
pCmdUI->Enable(TRUE);
}
In both Zoomin and Zoomout function, OnUpdateCommnad is not working.
This routing isn't done automatically.
You have to handle WM_IDLEUPDATECMDUI. Usually you call the internal OnUpdateCmdUI virtual function. This finally calls UpdateDialogControls.
You find the details in TN021
Just set a breakpoint on a working OnUpdate Handler. And look into the call stack. Than you can see and imagine how the whole stuff works.
There is also a possible way to use WM_KICKIDLE and UpdateDialogControls. See this article.
Try the following.
In TrendView.h add this:
afx_msg LRESULT OnKickIdle(WPARAM wParam, LPARAM lParam);
In TrendView.cpp add this:
#include <afxpriv.h>
...
ON_MESSAGE(WM_KICKIDLE, OnKickIdle)
...
LRESULT CTrendView::OnKickIdle(WPARAM wParam, LPARAM lParam)
{
UpdateDialogControls(this, FALSE);
return 0;
}

Replicating user input from edit control of one MFC dialog to an edit control of another dialog

In one of my dialog based MFC application, I've used two dialogs of similar look. The requirement is when user populates an edit box of one dialog with some data the same to be replicated to similar edit box of another dialog instantly. I'm trying to implement it with EN_CHANGE event of the edit control; where when any change is detected application post a message with updated data to other dialog to update the content of its own edit box. The problem is when the second dialog is setting its edit box content with the received data from first dialog, EN_CHANGE event is getting triggered from the second dialog, which is obvious, resulting in an endless back and forth message exchange. Could anybody please suggest me some solution for instant replicating user inputs between edit boxes of two MFC dialogs while keeping MFC application type as dialog based?
In my implementation both the Dialogs are CDialog derived and have the following CEdit event handler and Message handler methods:
For CScreen1 class:
void CScreen1::OnEnChangeEditUser()
{
static CString msg;
m_username.GetWindowText(msg);
::PostMessage(m_mScreen2,WM_INTER_LOGIN,10,(LPARAM)&msg); //m_mScreen2 is the HWND of 2nd dlg
}
LRESULT CScreen1::OnInterLoginMsg(WPARAM wParam, LPARAM lParam)
{
CString *msg=(CString*)lParam;
switch((int)wParam)
{
case 10:
m_username.SetWindowText(msg->GetString()); //m_username is CEdit Ctrl
delete msg;
break;
}
return 0;
}
For CScreen2 class:
void CScreen2::OnEnChangeEditUser()
{
static CString msg;
m_username.GetWindowText(msg);
::PostMessage(m_mScreen1,WM_INTER_LOGIN,10,(LPARAM)&msg); //m_mScreen1 is the HWND of 1st dlg
}
LRESULT CScreen2::OnInterLoginMsg(WPARAM wParam, LPARAM lParam)
{
CString *msg=(CString*)lParam;
switch((int)wParam)
{
case 10:
m_username.SetWindowText(msg->GetString()); //m_username is CEdit Ctrl
delete msg;
break;
}
return 0;
}
Simply use a boolean variable to play around. I have updated your code here.
For CScreen1 class:
BOOL postchanges = TRUE; //always TRUE
void CScreen1::OnEnChangeEditUser()
{
if (!postchanges)
return;
static CString msg;
m_username.GetWindowText(msg);
::PostMessage(m_mScreen2,WM_INTER_LOGIN,10,(LPARAM)&msg); //m_mScreen2 is the HWND of 2nd dlg
}
LRESULT CScreen1::OnInterLoginMsg(WPARAM wParam, LPARAM lParam)
{
CString *msg=(CString*)lParam;
switch((int)wParam)
{
case 10:
postchanges = FALSE; // do not post msg
m_username.SetWindowText(msg->GetString()); //m_username is CEdit Ctrl
postchanges = TRUE; // revert back
delete msg;
break;
}
return 0;
}
For CScreen2 class: do the same
As the requirement is replicating user input between two Edit controls of different Dialogs; it can be handled through processing keystroke messages.

CPrintDialog::OnInitDialog does not get called every time

I am using MyPrintDialog extended CPrintDialog.
The problem is:
OnInitDialog() method of MyPrintDialog does not get called on first time when the application try to open Print dialog.
I am trying to set the Printer name in the Print Dialog from the OnInitDialog() method.
Printer name is specified by the user in the application which I want to use for the Printing.
My OnInitDialog() method
CComboBox *wndCmbBox = (CComboBox *)GetDlgItem( IDC_PER_USER ); /*IDC_PER_USER which is ID of Prin Dialog combo */
if( wndCmbBox != NULL )
{
wndCmbBox->SelectString( -1, PrinterName );
}
Or is their any way to set the user choice Printer name in the Print dialog..?
Please Explain.
Edit
Yes, by onInit() I mean OnInitDialog()
I am using VS 2012 with Win7 32 bit. I am facing this issue only first call of DoModel(). Next consecutive DoModel() methods calls OnInitDialog().
I have debug the issue and found something
INT_PTR CALLBACK AfxDlgProc(HWND hWnd, UINT message, WPARAM, LPARAM)
{
if (message == WM_INITDIALOG)
{
// special case for WM_INITDIALOG
CDialog* pDlg = DYNAMIC_DOWNCAST(CDialog, CWnd::FromHandlePermanent(hWnd));
if (pDlg != NULL)
return pDlg->OnInitDialog();
else
return 1;
}
return 0;
}
The above is the function of dlgcore.cpp. When issue is reproduced I observed that DYNAMIC_DOWNCAST returns NULL.
Please note that I have customized CPrintDialog and added one check box in it. I doubt if it is creating the issue.
If you meant to say OnInitDialog() instead of OnInit(), then it's possible that your problem is explained by this MSKB article although it seems to have been fixed after VC6 SP1.

How to avoid EN_CHANGE notifications when sending WM_SETTEXT?

I have a CEdit derived control that displays the string "N/A" when the undelying data is null. I recently added code to empty the control(SetWindowText("");) when it gains focus and set if back to "N/A"(SetWindowText("N/A")) when the focus is lost if the user left the control empty.
The only problem is that setting the window text to "" or "N/A" triggers EN_CHANGE, so my dialog thinks that the data has changed.
How can I avoid EN_CHANGE from being fired when calling SetWindowText (WM_SETTEXT)?
NOTES
-I know I can set the edit control to Multiline=TRUE but that's not accpectable for me.
-My application is MBCS so I can't use SetCueBanner
-I want an elegant solution. Setting the parent window to NULL temporarily is not an elegant solution.
EDIT:
-I want the solution to be in my custom control, not in each dialog
Thanks
The way I've done it before (last time, like 20 minutes ago; in fact I was thinking about asking the same question), is by setting a flag. When I'm about to set the text programatically, I set the flag, and I check it in the EN_CHANGE handler:
void CMyDialog::MyFunction()
{
setEditTextProgramatically = true;
c_Edit.SetWindowText(_T("Whatever"));
setEditTextProgramatically = false;
}
void CMyDialog::OnEnChangeEdit()
{
if (!setEditTextProgramatically)
{
// Do whatever you need to do
}
}
I know it's not the most elegant solution, but it works, at least for me.
I've always wondered why MFC doesn't provide a way to distinguish user input from changes from code, but that's the way it is.
I finally found a suitable solution to my problem.
First, I added a flag to my derived control's header file and I initialized it to false in the constructor
bool m_bNoEnChange;
I overrode the OnChildNotify in my derived control's header file and in the implementation, I checked for the WM_COMMAND message with the EN_CHANGE parameter. I then returned TRUE to prevent the message from being sent to the parent(dialog/page)
virtual BOOL OnChildNotify(UINT message, WPARAM wParam, LPARAM lParam, LRESULT* pLResult);
BOOL CADEdit::OnChildNotify(UINT message, WPARAM wParam, LPARAM lParam, LRESULT* pLResult)
{
if(message == WM_COMMAND && HIWORD(wParam) == EN_CHANGE)
{
//If the flag is set, don't send the message to the parent window
if(m_bNoEnChange)
return TRUE;
}
return CEdit::OnChildNotify(message, wParam, lParam, pLResult);
}
Finally, when the control gains and loses focus, I wrapped the problematic SetWindowText with my flag
m_bNoEnChange = true;
SetWindowText(_T(""));
m_bNoEnChange = false;
This solution is the best in my case because I don't have to modify each dialog.
You could disable (EnableWindow(FALSE) or send WM_ENABLE with param FALSE) the control prior to sending WM_SETTEXT then enabling it afterwards. That should prevent the EN_CHANGE
There is probably some more elegant method :p
The below code uses a C++ 11 feature, but that can easily be changed.
HEADER
// CEditOptionalNotify.h
//
// CEdit derived class allowing the control's text value to be
// set without (optionally) causing EN_CHANGE processing.
//
#pragma once
class CEditOptionalNotify : public CEdit
{
//DECLARE_DYNAMIC(CEditOptionalNotify)
// Enable use of RUNTIME_CLASS macro and CObject::IsKindOf()
public:
CEditOptionalNotify();
virtual ~CEditOptionalNotify();
enum class PerformOnChangeProcessing { No, Yes };
void vSetText(const TCHAR* pText, PerformOnChangeProcessing e);
protected:
afx_msg BOOL bConsiderEnChangeAsHandled();
bool m_bChangeNotificationsEnabled;
DECLARE_MESSAGE_MAP()
};
IMPLEMENTATION
// EditOptionalNotify.cpp : implementation file
//
#include "stdafx.h"
#include <EditOptionalNotify.h>
//IMPLEMENT_DYNAMIC(CEditOptionalNotify, CEdit)
CEditOptionalNotify::CEditOptionalNotify() :
m_bChangeNotificationsEnabled(true)
{
}
CEditOptionalNotify::~CEditOptionalNotify()
{
}
BEGIN_MESSAGE_MAP(CEditOptionalNotify, CEdit)
ON_CONTROL_REFLECT_EX(EN_CHANGE, bConsiderEnChangeAsHandled)
END_MESSAGE_MAP()
BOOL CEditOptionalNotify::bConsiderEnChangeAsHandled()
{
return (m_bChangeNotificationsEnabled ? FALSE : TRUE);
}
void CEditOptionalNotify::vSetText(const TCHAR* pText, PerformOnChangeProcessing e)
{
bool bChangeNotificationsDesired = (PerformOnChangeProcessing::No == e ? false : true);
if (bChangeNotificationsDesired != m_bChangeNotificationsEnabled)
{
m_bChangeNotificationsEnabled = bChangeNotificationsDesired;
CEdit::SetWindowText(pText);
m_bChangeNotificationsEnabled = (bChangeNotificationsDesired ? false : true);
}
else
CEdit::SetWindowText(pText);
}
LRESULT CMainDlg::OnEnUpdateEditID(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
{
//using static variable
static bool isCodeChangeText = false;
if(isCodeChangeText)
return 0;
……//Deal Window Text
if(old == new)
return 0;
int nSel = m_editPID.GetSel();//record cursor pos
isCodeChangeText = true;
m_editID.SetWindowText(new);
m_editID.SetSel(nSel);
isCodeChangeText = false;
return 0;
}
In case somebody else finds this discussion...
As Steven wrote UpdateData does not cause an EN_CHANGE being sent.
Under the hood MFC calls AfxSetWindowText with which one can specify one hwnd.

MFC how to know a resizing of view is finished

I am wondering how to catch the fact that a view (CView in a CMDIChildWnd frame) has been resized, and that the user just released the left mouse button.
None of OnSize, OnSizing and OnLButtonUp work.
I have tried the solution in http://www.codeguru.com/forum/showthread.php?t=59476 and it doesn't work.
I am working on VC2010 with W7.
Thanks in advance.
Try WM_NCLBUTTONUP. I don't know if a view can be resized other than by the mouse, but if it can you probably also want to respond to WM_EXITSIZEMOVE as in the link you gave.
I recently needed to do this. I used a combination of OnSysMessage to capture the SC_SIZE and SC_MOVE event and WM_ENTERSIZEMOVE and WM_EXITSIZEMOVE within the CMDIChildWnd derived class. Within the OnSysMessage handler I set a public variable for IsSizing. I can then check the variable within the OnEnterSizeMove function and act accordingly. Within the OnExitSizeMove function, it resets the variable to FALSE.
BEGIN_MESSAGE_MAP(CChildFrame, CMDIChildWnd)
//{{AFX_MSG_MAP(CChildFrame)
ON_WM_SYSCOMMAND()
ON_MESSAGE(WM_ENTERSIZEMOVE, OnEnterSizeMove)
ON_MESSAGE(WM_EXITSIZEMOVE, OnExitSizeMove)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
And for the handlers:
void CChildFrame::OnSysCommand(UINT nID, LPARAM lParam)
{
switch(nID&0xfff0)
{
case SC_SIZE:
m_bIsSizing = TRUE;
break;
case SC_MOVE:
m_bIsSizing = FALSE;
break;
}
CMDIChildWnd::OnSysCommand(nID, lParam);
}
LRESULT CChildFrame::OnEnterSizeMove(UINT wParam, LPARAM lParam)
{
if(m_bIsSizing)
{
TRACE("CChildFrame::OnEnterSizeMove\n");
}
return 0; //don't flag we processed the message
}
LRESULT CChildFrame::OnExitSizeMove(UINT wParam, LPARAM lParam)
{
if(m_bIsSizing)
{
TRACE("CChildFrame::OnExitSizeMove\n");
m_bIsSizing = FALSE; // set to false before calling OnSizing
CRect dlgRect;
pView->GetClientRect(dlgRect);
pView->InvalidateRect(NULL, FALSE);
pView->SendMessage(WM_SIZE, WPARAM(SIZE_RESTORED), MAKELONG(dlgRect.Width(), dlgRect.Height()));
}
return 0; //don't flag we processed the message
}
Then within your View code, you can check if the Frame is sizing within your OnSize handler.
void CMyView::OnSize(UINT nType, int cx, int cy)
{
CChildFrame* pFrame=(CChildFrame*)GetParentFrame();
if(!pFrame->m_bIsSizing) {
CWaitCursor cur;
//DO UPDATE CODE
}
}