// LBSpinButtonCtrl.cpp : implementation file
//
// Copyright � 1999 Oleg Lobach, All Rights Reserved.
//
// mailto:oleglb@mail.ru
//or
// mailto:oleg@alexen.ru
//
//
// This source file may be redistributed unmodified by any means PROVIDING
// it is NOT sold for profit without the authors expressed written
// consent, and providing that this notice and the authors name and all
// copyright notices remain intact. This software is by no means to be
// included as part of any third party components library, or as part any
// development solution that offers MFC extensions that are sold for profit.
//
// If the source code is used in any commercial applications then a statement
// along the lines of:
//
// "Portions Copyright � 1999 Oleg Lobach" must be included in the "Startup
// Banner", "About Box" or "Printed Documentation". This software is provided
// "as is" without express or implied warranty. The author accepts no
// liability for any damage/loss of business that this product may cause.
//
/////////////////////////////////////////////////////////////////////////////
//****************************************************************************
#include "stdafx.h"
#include "LBSpinButtonCtrl.h"
#pragma warning(disable:4786)
#include <map>
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
using namespace std;
typedef map <HWND,HWND> HWNDMAP;
static HWNDMAP gHandleMap;
static LRESULT CALLBACK FilterBuddyMsgProc(int code,WPARAM wParam, LPARAM lParam );
static HHOOK ghHook=NULL;
/////////////////////////////////////////////////////////////////////////////
// CLBSpinButtonCtrl
CLBSpinButtonCtrl::CLBSpinButtonCtrl():m_hWndBuddy(NULL),
m_bVertical(true),m_nSpinAlign(Outside),
m_bDefaultDirection(true),m_bBuddyIsEdit(false),
m_rctIsPressed(0,0,0,0),m_bActiveSpinPressed(false),
m_bAutoDisable(true)
{
}
CLBSpinButtonCtrl::~CLBSpinButtonCtrl()
{
}
BEGIN_MESSAGE_MAP(CLBSpinButtonCtrl, CSpinButtonCtrl)
//{{AFX_MSG_MAP(CLBSpinButtonCtrl)
ON_NOTIFY_REFLECT_EX(UDN_DELTAPOS, OnDeltapos)
ON_WM_LBUTTONDOWN()
ON_WM_ERASEBKGND()
ON_WM_LBUTTONUP()
ON_WM_DESTROY()
ON_WM_PAINT()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CLBSpinButtonCtrl message handlers
void CLBSpinButtonCtrl::PreSubclassWindow()
{
//I need custom pens to draw up_down control
COLORREF clr=::GetSysColor(COLOR_3DDKSHADOW);
m_penDarkShadow=::CreatePen(PS_SOLID,0,clr);
clr=::GetSysColor(COLOR_BTNSHADOW);
m_penShadow=::CreatePen(PS_SOLID,0,clr);
clr=::GetSysColor(COLOR_3DHILIGHT);
m_penLight=::CreatePen(PS_SOLID,0,clr);
clr=::GetSysColor(COLOR_3DLIGHT);
m_penLightShadow=::CreatePen(PS_SOLID,0,clr);
clr=::GetSysColor(COLOR_BTNFACE);
m_penButtonFace=::CreatePen(PS_SOLID,0,clr);
Init();
CSpinButtonCtrl::PreSubclassWindow();
}
void CLBSpinButtonCtrl::OnPaint()
{
if(m_bAutoDisable)
{
//Draw control by hands
CPaintDC RealDC(this); // device context
CRect rctPaint=m_rctClient;
CRect rcPaintUp,rcPaintDown;
//To get rid of flickering we are drawing to memory DC (dc),
//and then BitBlting it to the screen.
//So we have to create compatible memory DC and select bitmap into it.
CDC dc;
CBitmap bmpMem,*pOldMemBmp;
dc.CreateCompatibleDC(&RealDC);
bmpMem.CreateCompatibleBitmap(&RealDC,rctPaint.Width(),rctPaint.Height());
pOldMemBmp=dc.SelectObject(&bmpMem);
//As so I bypassed WM_ERASEBCKGND, do its job here
dc.FillSolidRect(&rctPaint,::GetSysColor(COLOR_BTNFACE));
if(::IsWindow(m_hWndBuddy) && m_nSpinAlign!=Outside )
{
rctPaint.top+=2;
rctPaint.bottom-=2;
if(m_nSpinAlign == OnRightInside)
rctPaint.right-=2;
else
rctPaint.left+=2;
}
rcPaintUp= rctPaint;
//Draw control, depending on it alignment and orientation
if(m_bVertical)
{
rcPaintUp.bottom=rcPaintUp.top+rcPaintUp.Height()/2;
rcPaintDown=rctPaint;
rcPaintDown.top=rcPaintDown.bottom-rcPaintUp.Height();
dc.DrawFrameControl(&rcPaintUp,DFC_SCROLL,DFCS_SCROLLUP);
dc.DrawFrameControl(&rcPaintDown,DFC_SCROLL,DFCS_SCROLLDOWN);
}
else
{
rcPaintUp.right=rcPaintUp.left+rcPaintUp.Width()/2;
rcPaintDown=rctPaint;
rcPaintDown.left=rcPaintDown.right-rcPaintUp.Width();
dc.DrawFrameControl(&rcPaintUp,DFC_SCROLL,DFCS_SCROLLLEFT);
dc.DrawFrameControl(&rcPaintDown,DFC_SCROLL,DFCS_SCROLLRIGHT);
}
if(::IsWindow(m_hWndBuddy) && m_nSpinAlign!=Outside )
{
//We are inside buddy,so have to draw buddy's border around
//CLBSpinButtonCtrl
//We use m_rctClient as so rctPaint may have been already modified.
if(m_nSpinAlign == OnRightInside)
{
::SelectObject(dc.m_hDC,m_penShadow);
dc.MoveTo(m_rctClient.left,m_rctClient.top);
dc.LineTo(m_rctClient.right,m_rctClient.top);
::SelectObject(dc.m_hDC,m_penDarkShadow);
dc.MoveTo(m_rctClient.left,m_rctClient.top+1);
dc.LineTo(m_rctClient.right-1,m_rctClient.top+1);
::SelectObject(dc.m_hDC,m_penLight);
dc.LineTo(m_rctClient.right-1,m_rctClient.bottom-1);
dc.LineTo(m_rctClient.left-1,m_rctClient.bottom-1);
}
else
{
::SelectObject(dc.m_hDC,m_penShadow);
dc.MoveTo(m_rctClient.right,m_rctClient.top);
dc.LineTo(m_rctClient.left,m_rctClient.top);
dc.LineTo(m_rctClient.left,m_rctClient.bottom);
::SelectObject(dc.m_hDC,m_penDarkShadow);
dc.MoveTo(m_rctClient.right,m_rctClient.top+1);
dc.LineTo(m_rctClient.left+1,m_rctClient.top+1);
dc.LineTo(m_rctClient.left+1,m_rctClient.top+1);
dc.LineTo(m_rctClient.left+1,m_rctClient.bottom-1);
::SelectObject(dc.m_hDC,m_penLight);
dc.LineTo(m_rctClient.right+1,m_rctClient.bottom-1);
}
}
//If the position reached the limit, draw corresponding
//part of control as disabled
switch (m_nSpinState)
{
case DisableRight:
if(m_bDefaultDirection)
{
if(m_bVertical)
DisableRect(dc,rcPaintDown);
else
DisableRect(dc,rcPaintUp);
}
else
{
if(m_bVertical)
DisableRect(dc,rcPaintUp);
else
DisableRect(dc,rcPaintDown);
}
break;
case DisableLeft:
if(m_bDefaultDirection)
{
if(m_bVertical)
DisableRect(dc,rcPaintUp);
else
DisableRect(dc,rcPaintDown);
}
else
{
if(m_bVertical)
DisableRect(dc,rcPaintDown);
else
DisableRect(dc,rcPaintUp);
}
break;
}
if(m_bActiveSpinPressed)
{
//The control's position has changed, so we have to
//draw corresponding part as pressed
if(m_rctIsPressed.IsRectEmpty())
{
CPoint pt=::GetMessagePos();
ScreenToClient(&pt);
if(rcPaintUp.PtInRect(pt))
{
m_rctIsPressed = rcPaintUp;
}
else
{
if(rcPaintDown.PtInRect(pt))
{
m_rctIsPressed = rcPaintDown;
}
}
}
DrawPressedRect(dc,m_rctIsPressed);
m_bActiveSpinPressed=false;
}
//Copy drawing from memory device context to the screen
RealDC.BitBlt(m_rctClient.left,m_rctClient.top,m_rctClient.Width(),
m_rctClient.Height(),&dc,m_rctClient.left,m_rctClient.top,SRCCOPY);
dc.SelectObject(pOldMemBmp);
}
else
{
//Let the Windows do it's work
Default();
return;
}
}
void CLBSpinButtonCtrl::DisableRect(CDC &dc, const CRect &rectDisable) const
{
CBitmap bmpMask,*pBmpBeforeMask;
CBrush brushMask,*pOldBrush;
COLORREF clrOldBack,clrOldText;
CDC memDC;
memDC.CreateCompatibleDC(&dc);
CRect rctClient;
CRect rectToDisable=rectDisable;
GetClientRect(&rctClient);
//Create MONO bitmap
bmpMask.CreateBitmap(rctClient.Width(),rctClient.Height(),1,1,NULL);
pBmpBeforeMask=memDC.SelectObject(&bmpMask);
CDC dcSrc;
dcSrc.CreateCompatibleDC(&dc);
CBitmap bmpSrc,*pBmpBeforeSrc;
bmpSrc.CreateCompatibleBitmap(&dc,rctClient.Width(),rctClient.Height());
pBmpBeforeSrc=dcSrc.SelectObject(&bmpSrc);
clrOldBack=dc.SetBkColor(RGB(0,0,0)); //Suppose arrows are black
//Create mask into memDC (the pixels which in dc were black - become white in memDC,
//all others pixels in memDC become black)
memDC.BitBlt(rectToDisable.left,rectToDisable.top,
rectToDisable.Width(),rectToDisable.Height(),&dc,
rectToDisable.left,rectToDisable.top,SRCCOPY);
//Copy bitmap from screen to memory DC
dcSrc.BitBlt(rectToDisable.left,rectToDisable.top,
rectToDisable.Width(),rectToDisable.Height(),&dc,
rectToDisable.left,rectToDisable.top,SRCCOPY);
CBrush brushSrc;
rectToDisable.DeflateRect(1,1);
//Apply DSPxax ROP code to memory dc . As result black arrow become
// of COLOR_3DSHADOW
brushSrc.CreateSolidBrush(::GetSysColor(COLOR_3DSHADOW));
dcSrc.SelectObject(&brushSrc);
dcSrc.SetBkColor(RGB(255,255,255));
dcSrc.SetTextColor(RGB(0,0,0));
dcSrc.BitBlt(rectToDisable.left,rectToDisable.top,
rectToDisable.Width(),rectToDisable.Height(),&memDC,
rectToDisable.left,rectToDisable.top,0x00E20746L);
//Apply DSPxax ROP code to screen dc and shift result one pixel left and bottom.
//As result black arrow become shifted and of COLOR_3DLIGHT.
brushMask.CreateSolidBrush(::GetSysColor(COLOR_3DHILIGHT));
pOldBrush = dc.SelectObject(&brushMask);
dc.SetBkColor(RGB(255,255,255));
clrOldText=dc.SetTextColor(RGB(0,0,0));
dc.BitBlt(rectToDisable.left+1,rectToDisable.top+1,
rectToDisable.Width()-1,rectToDisable.Height()-1,&memDC,
rectToDisable.left,rectToDisable.top,0x00E20746L);
// Draw memory dc (dcSrc) transparently over device dc.
//As result only arrow of color COLOR_3DSHADOW will be drawn over device dc
//-------------------------------------------------------------------------
//Create mask into memDC (the pixels which in dc were COLOR_BTNFACE
//- become white in memDC,all others pixels in memDC become black)
dcSrc.SetBkColor(GetSysColor(COLOR_BTNFACE));
memDC.BitBlt(rectToDisable.left,rectToDisable.top,
rectToDisable.Width(),rectToDisable.Height(),&dcSrc,
rectToDisable.left,rectToDisable.top,SRCCOPY);
dc.BitBlt(rectToDisable.left,rectToDisable.top,
rectToDisable.Width(),rectToDisable.Height(),&dcSrc,
rectToDisable.left,rectToDisable.top,SRCINVERT);
dc.BitBlt(rectToDisable.left,rectToDisable.top,
rectToDisable.Width(),rectToDisable.Height(),&memDC,
rectToDisable.left,rectToDisable.top,SRCAND);
dc.BitBlt(rectToDisable.left,rectToDisable.top,
rectToDisable.Width(),rectToDisable.Height(),&dcSrc,
rectToDisable.left,rectToDisable.top,SRCINVERT);
//-------------------------------------------------------------------------
//Restore resourses
memDC.SelectObject(pBmpBeforeMask);
dcSrc.SelectObject(pBmpBeforeSrc);
dc.SetBkColor(clrOldBack);
dc.SetTextColor(clrOldText);
dc.SelectObject(pOldBrush);
}
void CLBSpinButtonCtrl::DrawPressedRect(CDC &dc, const CRect &rctToDown) const
{
CRect rctDown=rctToDown;
HPEN hOldPen;
//Offset bitmap one pixel left and down
dc.BitBlt(rctDown.left+1,rctDown.top+1,
rctDown.Width()-1,rctDown.Height()-1,&dc,
rctDown.left,rctDown.top,SRCCOPY);
// Draw the border of pressed button
rctDown.bottom-=1;
hOldPen = (HPEN)SelectObject(dc.m_hDC,m_penShadow);
dc.MoveTo(rctDown.left,rctDown.bottom-1);
dc.LineTo(rctDown.left,rctDown.top);
dc.LineTo(rctDown.right-1,rctDown.top);
SelectObject(dc.m_hDC,m_penLight);
dc.LineTo(rctDown.right-1,rctDown.bottom);
dc.LineTo(rctDown.left-1,rctDown.bottom);
SelectObject(dc.m_hDC,m_penDarkShadow);
dc.MoveTo(rctDown.left+1,rctDown.bottom-2);
dc.LineTo(rctDown.left+1,rctDown.top+1);
dc.LineTo(rctDown.right-2,rctDown.top+1);
SelectObject(dc.m_hDC,m_penLightShadow);
dc.LineTo(rctDown.right-2,rctDown.bottom-1);
dc.LineTo(rctDown.left,rctDown.bottom-1);
SelectObject(dc.m_hDC,m_penButtonFace);
dc.MoveTo(rctDown.left+2,rctDown.bottom-2);
dc.LineTo(rctDown.left+2,rctDown.top+2);
dc.LineTo(rctDown.right-2,rctDown.top+2);
SelectObject(dc.m_hDC,hOldPen);
}
void CLBSpinButtonCtrl::Init()
{
//We need to know m_hWndBuddy even if UDS_WRAP style applied, to fix bug
// with possible incorrect Z-order of buddy/spin
m_hWndBuddy=(HWND)::SendMessage(m_hWnd, UDM_GETBUDDY, 0, 0l);
DWORD dwStyle=::GetWindowLong(m_hWnd,GWL_STYLE);
if(dwStyle & UDS_WRAP)
m_bAutoDisable=false; //if the UDS_WRAP style was applied, we can't disable arrows
// The m_bAutoDisable can also be set from outside using SetAutoDisable method;
if(m_bAutoDisable)
{
//Determine the orientation and alignment of this CLBSpinButtonCtrl
if(dwStyle & UDS_ALIGNRIGHT )
m_nSpinAlign=OnRightInside;
else
{
if(dwStyle & UDS_ALIGNLEFT)
{
m_nSpinAlign=OnLeftInside;
}
}
if(dwStyle & UDS_HORZ)
m_bVertical=false;
//Determine the control's limits and direction of increasing of it's position
GetRange32(m_nMinPos,m_nMaxPos);
if( m_nMinPos < m_nMaxPos)
m_bDefaultDirection =false;
else
{
int nTemp = m_nMinPos;
m_nMinPos = m_nMaxPos;
m_nMaxPos=nTemp;
}
GetClientRect(&m_rctClient);
m_nPrevPos = GetPos();
//This is done special for up-down control which lives in CTabCtrl,
//as so its initial value is out of range.
if(m_nPrevPos < m_nMinPos || m_nPrevPos > m_nMaxPos)
m_nPrevPos=m_nMinPos;
//Determine the control's initial enable/disable state
m_nSpinState = BothEnable;
if(m_nPrevPos == m_nMinPos)
m_nSpinState = DisableLeft;
else
{
if(m_nPrevPos == m_nMaxPos)
m_nSpinState = DisableRight;
}
if(::IsWindow(m_hWndBuddy))
{
char buf[5];
::GetClassName(m_hWndBuddy,buf,sizeof(buf)/sizeof(buf[0]));
if(!strcmp(buf,"Edit"))
{
//The class of buddy is Edit
m_bBuddyIsEdit=true;
//I need to update the enabled/disabled state of CLBSpinButtonCtrl
//when contents of buddy window is changing.
//For instance, if user enters into buddy window the value greater
//than upper possible limit - it is obvious that the increasing
//arrow should switch to disable state.
//The best way to do it is to create WH_CALLWNDPROC hook
//and test within it if EN_UPDATE message came from buddy window
//Another posibility- keyboard hook, but then the user can foolish
//the control, using clipboard Copy/Cut/Paste functions.
//Test if WH_CALLWNDPROC hook already was installed.
// If no, set up it.
if(ghHook==NULL)
ghHook=SetWindowsHookEx(WH_CALLWNDPROC,FilterBuddyMsgProc,NULL, //WH_GETMESSAGE
GetCurrentThreadId());
//As so I use a single WH_CALLWNDPROC hook for all existing in
//an application CLBSpinButtonCtrl controls, I need to distinguish
// between these controls in the static FilterBuddyMsgProc.
//For this purpose I use static std:map<HWND,HWND> gHandleMap
//Try to find m_hWndBuddy in the gHandleMap
HWNDMAP::iterator iterHwnd=gHandleMap.find(m_hWndBuddy);
if(iterHwnd != gHandleMap.end())
{
if((*iterHwnd).second != m_hWnd)
{
//If in the gHandleMap already defined another CLBSpinButtonCtrl
//for that buddy (m_hWndBuddy), then redefine it .
gHandleMap.erase(iterHwnd);
gHandleMap.insert(HWNDMAP::value_type(m_hWndBuddy,m_hWnd));
}
}
else
//If the CLBSpinButtonCtrl assosiated with
//m_hWndBuddy is not found in the gHandleMap, then add it.
gHandleMap.insert(HWNDMAP::value_type(m_hWndBuddy,m_hWnd));
}
}
Invalidate(FALSE);
}
}
BOOL CLBSpinButtonCtrl::OnDeltapos(NMHDR* pNMHDR, LRESULT* pResult)
{
if(m_bAutoDisable)
{
NM_UPDOWN* pNMUpDown = (NM_UPDOWN*)pNMHDR;
int nNextPos=pNMUpDown->iPos+pNMUpDown->iDelta;
//Determine the enable/disable state of CLBSpinButtonCtrl
if(nNextPos > m_nMaxPos)
nNextPos = m_nMaxPos;
else
{
if(nNextPos < m_nMinPos)
nNextPos = m_nMinPos;
}
if(m_nPrevPos!=nNextPos)
{
if(pNMUpDown->iDelta)
m_bActiveSpinPressed=true;
if(nNextPos <m_nMaxPos && nNextPos >m_nMinPos)
{
m_nSpinState = BothEnable;
}
else
{
if(nNextPos == m_nMaxPos)
{
if(m_nMaxPos != m_nMinPos)
{
m_nSpinState=DisableRight;
m_bActiveSpinPressed=false;
}
else
{
m_nSpinState = BothDisable;
m_bActiveSpinPressed=false;
}
}
else
{
if(nNextPos == m_nMinPos)
{
m_nSpinState=DisableLeft;
m_bActiveSpinPressed=false;
}
}
}
m_nPrevPos=nNextPos;
Invalidate(FALSE);
//Let the Windows process UDN_DELTAPOS too
*pResult = 0;
return FALSE;
}
else
{
//If the next position of control is the same as current,
//what can happen when user clicked the disable arrow,
//then eat UDN_DELTAPOS
*pResult = 1;
return TRUE;
}
}
else
{
//Let the Windows process UDN_DELTAPOS
*pResult = 0;
return FALSE;
}
}
LRESULT CLBSpinButtonCtrl::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
{
//Let the Windows do it's work
LRESULT nRet = CSpinButtonCtrl::WindowProc(message, wParam, lParam);
switch(message)
{
case UDM_SETRANGE32:
case UDM_SETRANGE:
//Need to reinit CLBSpinButtonCtrl, as so the
//limits probably changed
Init();
break;
case UDM_SETBUDDY :
//Need to reinit CLBSpinButtonCtrl, as so the
//buddy changed
CleanUpHook();
Init();
//If buddy window(m_hWndBuddy) is placed after this
//CLBSpinButtonCtrl in Z-order , then m_hWndBuddywindow will
//get WM_PAINT message after this CLBSpinButtonCtrl control
//and in case CLBSpinButtonCtrl is attached to buddy,
// it will be overpainted by buddy's border
//This undocumented bug persist for CSpinButtonCtrl as well.
//
//To reproduce it create on dialog template attached
//CSpinButtonCtrl control and CEdit control and make
//tab order so, that for CSpinButtonCtrl tab position
//was less then for CEdit;
//Then in OnInitDiaolg call SetBuddy(pointerToEdit) function
//of CSpinButtonCtrl.
//
//
//To work around this simply place CSpinButtonCtrl
//after it's buddy in Z-order.
if(::IsWindow(m_hWndBuddy))
::SetWindowPos(m_hWnd,m_hWndBuddy,0,0,0,0,
SWP_NOMOVE|SWP_NOSIZE);
break;
}
return nRet;
}
void CLBSpinButtonCtrl::CleanUpHook() const
{
if(m_bBuddyIsEdit)
{
//If the buddy is edit, then try to find out if
//it was added to gHandleMap.
HWNDMAP::iterator iterHwnd=gHandleMap.find(m_hWndBuddy);
if(iterHwnd != gHandleMap.end() && (*iterHwnd).second == m_hWnd)
{
//If m_hWndBuddy found and is assosiated with current window,
//then delete it from gHandleMap.
iterHwnd = gHandleMap.erase(iterHwnd);
if(!gHandleMap.size() && ghHook!=NULL)
{
//If just deleted from the gHandleMap m_hWndBuddy was
//the last for current application,
//then remove a hook procedure.
UnhookWindowsHookEx( ghHook);
ghHook=NULL;
}
}
}
}
void CLBSpinButtonCtrl::OnLButtonDown(UINT nFlags, CPoint point)
{
m_rctIsPressed.SetRectEmpty();
CSpinButtonCtrl::OnLButtonDown(nFlags, point);
}
BOOL CLBSpinButtonCtrl::OnEraseBkgnd(CDC* pDC)
{
return TRUE ; //Get rid of flickering
}
void CLBSpinButtonCtrl::OnLButtonUp(UINT nFlags, CPoint point)
{
Invalidate();
UpdateWindow();
CSpinButtonCtrl::OnLButtonUp(nFlags, point);
}
bool CLBSpinButtonCtrl::SetAutoDisable(bool bSetOn)
{
// User should have an opportunity to swith on/off the custom
//behavior of the CLBSpinButtonCtrl control.
bool bPrev = m_bAutoDisable;
m_bAutoDisable = bSetOn;
Init();
return bPrev;
}
LRESULT CALLBACK FilterBuddyMsgProc( int code, WPARAM wParam, LPARAM lParam )
{
CWPSTRUCT* pData=reinterpret_cast<CWPSTRUCT*>(lParam);
if(WM_COMMAND==pData->message && EN_UPDATE==HIWORD(pData->wParam))
{
//If the incoming message is EN_UPDATE
HWNDMAP::iterator iterHwnd=gHandleMap.find(reinterpret_cast<HWND>(pData->lParam));
if(iterHwnd != gHandleMap.end())
{
//The incoming EN_UPDATE message has been sent
//to the edit control, defined in the gHandleMap.
//So get the value, entered into the edit control
//and send it to the CLBSpinButtonCtrl, assosiated
//with the edit control.
CString strText;
int nLen=::GetWindowTextLength((*iterHwnd).first);
::GetWindowText((*iterHwnd).first,strText.GetBufferSetLength(nLen),nLen+1);
strText.ReleaseBuffer();
//In case UDS_NOTHOUSANDS style not applied
//we have to delete thousands delimiter from string
strText.Remove((TCHAR)0xA0);
NMUPDOWN nmUpDn;
nmUpDn.iDelta=0;
nmUpDn.iPos=atoi(strText);
nmUpDn.hdr.code=UDN_DELTAPOS;
nmUpDn.hdr.hwndFrom=(*iterHwnd).second;
nmUpDn.hdr.idFrom=::GetDlgCtrlID((*iterHwnd).second);
///HWND hWndSpinParent=::GetParent((*iterHwnd).second);!!! watermark
::SendMessage(::GetParent((*iterHwnd).second),
WM_NOTIFY,(WPARAM)nmUpDn.hdr.idFrom,
(LPARAM)&nmUpDn);
}
}
return CallNextHookEx(ghHook,code,wParam,lParam);
}
void CLBSpinButtonCtrl::OnDestroy()
{
//Remove current CLBSpinButtonCtrl control from the gHandleMap and
//if it is the last control, defined in the gHandleMap
//then remove a hook procedure.
CleanUpHook();
CSpinButtonCtrl::OnDestroy();
}