Detecting Up / Down arrows in CSpinButtonCtrl MFC C++ - mfc

Is there any way to differentiate when the Up or Down arrow of a CSpinButtonCtrl is pressed?
I am trying to use the OnPointerdown event, but I don't know how to do it...
afx_msg LRESULT CMySpinButtonCtrl::OnPointerdown(WPARAM wParam, LPARAM lParam)
{
if(IS_POINTER_PRIMARY_WPARAM(wParam))
{
//TODO
}
return 0;
}
I will appreciate any kind of help.

Is there any way to differentiate when the Up or Down arrow of a CSpinButtonCtrl is pressed?
You should use UDN_DELTAPOS to do this.
Right-click the control in the Resource Editor and select Add Event Handler:
Select the UDN_DELTAPOS message and click Add and Edit:
You will be provided with skeleton code:
void CMFCApplication1Dlg::OnDeltaposSpin1(NMHDR *pNMHDR, LRESULT *pResult)
{
LPNMUPDOWN pNMUpDown = reinterpret_cast<LPNMUPDOWN>(pNMHDR);
// TODO: Add your control notification handler code here
*pResult = 0;
}
The NMUPDOWN article explains about the structure that you use. What you need to do is test the iDelta value. Example:
void CColumnOrderDlg::OnDeltaposSpinColumns(NMHDR* pNMHDR, LRESULT* pResult)
{
LPNMUPDOWN pNMUpDown = reinterpret_cast<LPNMUPDOWN>(pNMHDR);
if (pNMUpDown != nullptr)
{
if( pNMUpDown->iDelta > 0)
// Up - Do stuff;
else if(pNMUpDown->iDelta < 0)
// Down - Do stuff;
}
*pResult = 0;
}
There is also a useful article here where it states:
If you use a spin control for some other purpose, for example, to page through a sequence of windows or dialog boxes, then add a handler for the UDN_DELTAPOS message and perform your custom action there.

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

VisualStudio MFC CListCtrl SetItemText fails

I created a MFC Visual Studio Project with a CListCtrl. I added some items in the CDialog class like:
int l_iItem = m_listCtrl.InsertItem(LVIF_TEXT|LVIF_STATE, counter, someString, 0, LVIS_SELECTED, 0, 0);
m_listCtrl.SetItemText( l_iItem, 1, blockHexChar );
m_listCtrl.SetItemText( l_iItem, 2, description);
This works fine.
Afterwards i want to edit a subitem (over double click event). Works also fine.
If the editing is finished (this is in the CListCtrl class),
OnEndLabelEdit(NMHDR* pNMHDR, LRESULT* pResult)
will be called. It looks like this
LV_DISPINFO *plvDispInfo = (LV_DISPINFO *)pNMHDR;
LV_ITEM *plvItem = &plvDispInfo->item;
if (plvItem->pszText != NULL)
{
bool res = SetItemText(plvItem->iItem, plvItem->iSubItem, plvItem->pszText);
}
I always getting 0 back, so the SetItemText is failing.
Any idea what i'm doing wrong?
Cheers ehmkey
You have to post yourself a user-defined message (WM_USER+NNN) using PostMessage from within OnEndLabelEdit. Change label in response to that message.
LVN_ENDLABELEDIT passes a pointer to NMLVDISPINFO through lParam. I think you're looking at the wrong structure when processing the notification. Using the class wizard to generate the event handler in VS2013 gives
void CMFCApplication6Dlg::OnLvnEndlabeleditList1(NMHDR *pNMHDR, LRESULT *pResult)
{
NMLVDISPINFO *pDispInfo = reinterpret_cast<NMLVDISPINFO*>(pNMHDR);
// TODO: Add your control notification handler code here
*pResult = 0;
}
Thanks for your input, but the problem was in the MESSAGE_MAP(....) in CDialog class.
I have a member here, which holds the list and I directly mapped to the CListCtrl Class.
BEGIN_MESSAGE_MAP(CPeriDialog, CDialog)
ON_NOTIFY(LVN_ENDLABELEDIT, IDC_LIST1, &CListCtrl::OnEndLabelEdit)
END_MESSAGE_MAP()
Now I made a wrapper function which forward the event to the right object.
BEGIN_MESSAGE_MAP(CPeriDialog, CDialog)
ON_NOTIFY(LVN_ENDLABELEDIT, IDC_LIST1, &CPeriDialog::EndEdit)
END_MESSAGE_MAP()
Function simple looks like this
void CPeriDialog::EndEdit(NMHDR* pNMHDR, LRESULT* pResult)
{
m_listCtrl.OnEndLabelEdit(pNMHDR, pResult);
}

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.

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