MFC how to know a resizing of view is finished - mfc

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

Related

App crashes if closing a dialog while Async Operations are still underway

Firstly I'm not familiar with using Async, so the below approach could be completely wrong.
I'm currently decoding multiple images and displaying them in a dialog window.
This works fine, as long as I don't attempt to close the dialog while the decodes are still running.
If I do close the dialog, I get a Debug Assertion Error at return hh;
In my class header I have:
class CmytestDlg: public CDialog
{
protected:
// Generated message map functions
BOOL OnInitDialog() override;
std::future<HBITMAP> futurebmps[12];
};
In the .cpp:
BOOL CmytestDlg::OnInitDialog()
{
int totalJobs = 12;
// Queue up all the items,
for (int i = 0; i < totalJobs; i++)
{
futurebmps[i] = std::async(std::launch::async,
[this](const int i, const std::string& name) {
// This line is just here to simulate a long running image decode
std::this_thread::sleep_for(std::chrono::seconds(10));
HBITMAP hh = nullptr;
PostMessage(8888, i, 0); // Send a message to display the image
return hh; // <<< Debug Assertion Error Here (If I close dialog to soon)
}, i, std::to_string(0));
}
The message to draw the image is handled by OnRefreshAsyncView:
LRESULT CmytestDlg::OnRefreshAsyncView(WPARAM wParam, LPARAM lParam)
{
// For the purpose of a minimal demo, even if this is empty It still casuses the issue.
return 0;
}
I believe that the issue is triggered by the call to PostMessage, if I comment out this line, I no longer get the crash.
How do I safely handle the scenario where the dialog is closing? So I can prevent this crash.
My psychic powers suggest that your ATL/MFC program is asserting on the PostMessage call because the window handle became invalid after after you closed/destroyed it. Somewhere in the middle of that PostMessage call is an ASSERT that the m_hWnd instance is non-null. Hence, the assert is calling attention to a real bug in your code.
PostMessage is is a wrapper for the Win32 API: PostMessage And when you invoke the wrapper function on a different thread, it's likely asserts that m_hWnd (or similar HWND member) is non-null.
Consider writing additional (thread safe) code (with a mutex) that prevents PostMessage from getting called at the same time or after WM_CLOSE/WM_DESTROY is getting processed on the handle.
Something like this:
void CmytestDlg::SafePostMessage(UINT uMsg, WPARAM wp, LPARAM lp)
{
std::unique_lock<std::mutex> lck(_mutex);
if (_isHwndValid)
{
PostMessage(uMsg, wp, lp);
}
}
void CmytestDlg::OnClose(...)
{
std::unique_lock<std::mutex> lck(_mutex);
_isHwndValid = false;
}
Where OnClose is your handler for a WM_CLOSE message and isHwndValid is a bool member of your class that gets set to true when the window is created. And _mutex is a std::mutex.
To extend selbie's answer:
If you want your threads to stop early, you can turn the flag into a std::atomic and check it regularly during your computations, like:
std::mutex _mutex;
std::atomic<bool> _shutdown = false;
void CmytestDlg::LongRunningThing()
{
do_something();
if (_shutdown) return;
do_something_else()
if (_shutdown) return;
...
}
void CmytestDlg::SafePostMessage(UINT uMsg, WPARAM wp, LPARAM lp)
{
std::unique_lock<std::mutex> lck(_mutex);
if (!_shutdown)
{
PostMessage(uMsg, wp, lp);
}
}
void CmytestDlg::OnClose(...)
{
std::unique_lock<std::mutex> lck(_mutex);
_shutdown = false;
}

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

Display formatted text on selecting item in the Combobox

I have a combobox in that I want to display different string on selecting an item in Combo.
My combo box is a dropdown combobox.
For eg: I have following in my combobox.
Alex - Manager
Rain - Project Lead
Shiney - Engineer
Meera - Senior Engineer
OnSelecting an item in combobox I want to diaply only name i.e. Alex.
I tried below code
struct details{
CString name;
CString des;
};
BOOL CComboTestDlg::OnInitDialog()
{
CDialog::OnInitDialog();
details d1;
d1.name = _T("alex");
d1.des =_T("manager");
m_vec.push_back(d1);
details d2;
d2.name = _T("Rain");
d2.des =_T("Engineer");
m_vec.push_back(d2);
// TODO: Add extra initialization here
for(int i=0;i<m_vec.size();i++)
{
m_ctrlCombo.AddString(m_vec[i].name+m_vec[i].des);
m_ctrlCombo.SetItemData(i,(DWORD_PTR)&m_vec[i]);
}
m_ctrlCombo.SelectString(-1,m_vec[0].name);
m_ctrlCombo.SetWindowText(m_vec[0].name);
return TRUE; // return TRUE unless you set the focus to a control
}
void CComboTestDlg::OnCbnSelchangeCombo1()
{
int nItem = m_ctrlCombo.GetCurSel();
details* det = (details*)m_ctrlCombo.GetItemData(nItem);
PostMessage(SETCOMBOTEXT,IDC_COMBO1,(LPARAM)(LPCTSTR)det->name);
}
BOOL CComboTestDlg::PreTranslateMessage(MSG* pMsg)
{
MSG msg1=*pMsg;//I am loosing the value after checking ..so storing temp.
MSG msg;
CopyMemory(&msg, pMsg, sizeof(MSG));
HWND hWndParent = ::GetParent(msg.hwnd);
while (hWndParent && hWndParent != this->m_hWnd)
{
msg.hwnd = hWndParent;
hWndParent = ::GetParent(hWndParent);
}
if (pMsg->message==SETCOMBOTEXT && (pMsg->wParam == IDC_COMBO1))
SetDlgItemText(IDC_COMBO1, (LPCTSTR)pMsg->lParam);
if(pMsg->message==WM_KEYDOWN)
{
if(pMsg->wParam==VK_RETURN && msg.hwnd ==m_ctrlCombo.m_hWnd )
{
OnCbnSelchangeCombo1();
}
}
return CDialog::PreTranslateMessage(pMsg);
}
I am able to achieve my requirement OnComboSelChange() and Arrow Keys event but on pressing enter key after using arrow keys in combo box, it is not showing formatted text in combo box.
I think the most reliable and easy to implement solution is to subclass the edit control of the combobox. Intercept the WM_SETTEXT message and change the text as you like before forwarding it to the rest of the chain (finally the original window proc).
Install the sub class proc in OnInitDialog():
COMBOBOXINFO cbi{ sizeof(cbi) };
if( m_ctrlCombo.GetComboBoxInfo( &cbi ) )
{
SetWindowSubclass( cbi.hwndItem, ComboEditSubClassProc, 0, 0 );
}
ComboEditSubClassProc() could look like this:
LRESULT CALLBACK ComboEditSubClassProc( HWND hWnd, UINT uMsg, WPARAM wParam,
LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData )
{
switch( uMsg )
{
case WM_SETTEXT:
{
CString text = reinterpret_cast<LPCTSTR>( lParam );
// Extract the name (everything before "-").
CString name = text.SpanExcluding( _T("-") );
name.TrimRight();
// Forward the modified text to any other sub class procs, aswell
// as the original window proc at the end of the chain.
return DefSubclassProc( hWnd, uMsg, 0, reinterpret_cast<LPARAM>( name.GetString() ) );
}
case WM_NCDESTROY:
{
// We must remove our subclass before the subclassed window gets destroyed.
// This message is our last chance to do that.
RemoveWindowSubclass( hWnd, ComboEditSubClassProc, uIdSubclass );
break;
}
}
return DefSubclassProc( hWnd, uMsg, wParam, lParam );
}
Notes:
Contrary to my original solution of processing CBN_SELCHANGE, the current solution also works correctly if the combobox drop-down list is closed by pressing Return or is dismissed.
I think it is in general more reliable because we don't have to rely on the order of the notifications. The combobox has to finally call WM_SETTEXT to change the content of the edit control so this message will always be received.
There will also be no flickering as in the original solution where the text was first changed by the combobox and then modified by our code only after the fact.

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.

crash on ShowWindow()

I am developing a Notepad++ plugin.
I have a simple dialog box, which is created using CreateDialogParam(). It is initially hidden.
After a menu click, I call ShowWindow() with SW_SHOW on its handle, which results in this exception:
c000041d
Upon debugging , I found that these two messages are sent to my dialog just before crash
WM_SHOWWINDOW
WM_WINDOWPOSCHANGING
Both of them, I am not handling. Here is my dlgproc code. Any idea why it would happen ?
BOOL CALLBACK StaticDialog::dlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_INITDIALOG :
{
// Get the additional init data
StaticDialog *pStaticDlg = (StaticDialog *)(lParam);
// Store the handle in the object
pStaticDlg->_hSelf = hwnd;
::SetWindowLongPtr(hwnd, GWL_USERDATA, (long)lParam);
// Store the co-ordinates in the object
::GetWindowRect(hwnd, &(pStaticDlg->_rc));
// Forward the message for further processing
pStaticDlg->run_dlgProc(message, wParam, lParam);
// TRUE if it processed the message
return TRUE;
}
default :
{
// Retrieve the user data
StaticDialog *pStaticDlg = (StaticDialog *)(::GetWindowLongPtr(hwnd, GWL_USERDATA));
if (!pStaticDlg)
return FALSE;
// Send the message for further processing
return pStaticDlg->run_dlgProc(message, wParam, lParam);
// return FALSE if it processed the message
}
}
}
BOOL CALLBACK MarkDownViewDialog::run_dlgProc( UINT Message, WPARAM wParam, LPARAM lParam)
{
switch (Message)
{
case WM_INITDIALOG:
{
EmbedBrowserObject(this->_hSelf);
DisplayHTMLPage(this->_hSelf,L"http://www.microsoft.com");
DisplayHTMLStr(this->_hSelf, L"<H2><CENTER>HTML string test</CENTER></H2><P><FONT COLOR=RED>This is a <U>HTML string</U> in memory.</FONT>");
break;
}
default:
break;
}
return FALSE;
}
The dialog box is created through plugin mechanism of Notpead++. I am putting the source code from its file StaticDialog.cpp. The control goes through else block in code below.
void StaticDialog::create(int dialogID, bool isRTL, bool isModeles)
{
if (isRTL)
{
DLGTEMPLATE *pMyDlgTemplate = NULL;
HGLOBAL hMyDlgTemplate = makeRTLResource(dialogID, &pMyDlgTemplate);
_hSelf = ::CreateDialogIndirectParam(_hInst, pMyDlgTemplate, _hParent, (DLGPROC)dlgProc, (LPARAM)this);
::GlobalFree(hMyDlgTemplate);
}
else
_hSelf = ::CreateDialogParam(_hInst, MAKEINTRESOURCE(dialogID), _hParent, (DLGPROC)dlgProc, (LPARAM)this);
//int i=GetLastError();
if (!_hSelf)
{
//systemMessage(_T("StaticDialog"));
return;
}
if (isModeles) {
_isModeles = isModeles;
::SendMessage(_hParent, NPPM_MODELESSDIALOG, MODELESSDIALOGADD, (WPARAM)_hSelf);
}
}
My entire solution source code is hosted here:
https://github.com/madhur/Npp-Markdown-Viewer
Issue Resolution
Here is what I have done to troubleshoot the problem:
Created a Win32 project in which I create a dialog box and put exactly same source code to embed browser control in WM_INITDIALOG and it works. This Win32 project links with the same static library, which the original source code is linking with. This is the source of the sample project mail file:
https://github.com/madhur/Npp-Markdown-Viewer/blob/master/dll/example/example.c
The static library I referenced above, is nothing but it contains functions to embed browser control in HWND and to render either an URL or string. I have taken the source from here: http://www.codeguru.com/Cpp/I-N/ieprogram/article.php/c4379
This is the source of static library main file:
https://github.com/madhur/Npp-Markdown-Viewer/blob/master/dll/dll.c
If I comment this single line, the plugin works, basically it loads the dialog box without the Browser control in it:
EmbedBrowserObject(this->_hSelf);
The actual exception in Visual Studio is raised on this line:
return pStaticDlg->run_dlgProc(message, wParam, lParam);