⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 spinedit.cpp

📁 一个自定义的SPIN控件
💻 CPP
📖 第 1 页 / 共 2 页
字号:
// SpinEdit.cpp : implementation file
//
/////////////////////////////////////////////////////////////////////////////
// SpinEdit.h : header file
//
// SpinEdit Control header file
//
// This code is free for personal and commercial use.
// 
// Written by Huang Chaoyi (ahaa007@263.net)
// Please use and enjoy. Please let me know if you find bugs. 
//
// There are some code blocks copied from Chris Maunder's CGridCtrl class. 
// Thank you, Chris Maunder.
/////////////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "SpinEdit.h"
#include "TCHAR.h"
#include "seedit.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif


#define IDC_SEEDIT            1    // ID of inplace edit controls

IMPLEMENT_DYNCREATE(CSpinEdit, CWnd)

//在对话框中使用的函数
void AFXAPI DDX_SpinEditControl(CDataExchange* pDX, int nIDC, CSpinEdit& rControl)
{
    if (rControl.GetSafeHwnd() == NULL)    // not subclassed yet
    {
        ASSERT(!pDX->m_bSaveAndValidate);

        HWND hWndCtrl = pDX->PrepareCtrl(nIDC);

        if (!rControl.SubclassWindow(hWndCtrl))
        {
            ASSERT(FALSE);      // possibly trying to subclass twice?
            AfxThrowNotSupportedException();
        }
#ifndef _AFX_NO_OCC_SUPPORT
        else
        {
            // If the control has reparented itself (e.g., invisible control),
            // make sure that the CWnd gets properly wired to its control site.
            if (pDX->m_pDlgWnd->GetSafeHwnd() != ::GetParent(rControl.GetSafeHwnd()))
                rControl.AttachControlSite(pDX->m_pDlgWnd);
        }
#endif //!_AFX_NO_OCC_SUPPORT

    }
}

// Get the number of lines to scroll with each mouse wheel notch
// Why doesn't windows give us this function???
UINT GetMouseScrollLines()
{
    int nScrollLines = 3;            // reasonable default
    HKEY hKey;

    if (RegOpenKeyEx(HKEY_CURRENT_USER,  _T("Control Panel\\Desktop"),
                     0, KEY_QUERY_VALUE, &hKey) == ERROR_SUCCESS)
    {
        TCHAR szData[128];
        DWORD dwKeyDataType;
        DWORD dwDataBufSize = sizeof(szData);

        if (RegQueryValueEx(hKey, _T("WheelScrollLines"), NULL, &dwKeyDataType,
                           (LPBYTE) &szData, &dwDataBufSize) == ERROR_SUCCESS)
        {
            nScrollLines = _tcstoul(szData, NULL, 10);
        }
        RegCloseKey(hKey);
    }

    return nScrollLines;
}

/////////////////////////////////////////////////////////////////////////////
// CSpinEdit

CSpinEdit::CSpinEdit()
{
	RegisterWindowClass(  );

	m_dbValue			= 0;
	m_dbBase			= 0;
	m_dbLower			= 0;
	m_dbUpper			= 100;
	m_dbStep			= 1;
	m_bValueType		= SE_INT;
	m_szExtraText		= _T("");
	m_bPointPos				= 0;				

	m_bEditShow			= FALSE;
	m_bLowerBtnPushed	= FALSE;
	m_bUpperBtnPushed	= FALSE;
	m_bNeedExtraText	= TRUE;
	m_nTimerID			= 0;					
	m_nTimerInterval	= 100;

	m_nStepsPerWheelNotch = GetMouseScrollLines(); // Get the number of steps
	m_dbOldValue    = 0;

	m_pEdit		= NULL;
}

// Register the window class if it has not already been registered.
BOOL CSpinEdit::RegisterWindowClass()
{
    WNDCLASS wndcls;
    HINSTANCE hInst = AfxGetInstanceHandle();

    if (!(::GetClassInfo(hInst, SPINEDITCTRL_CLASSNAME, &wndcls)))
    {
        // otherwise we need to register a new class
        wndcls.style            = CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW;
        wndcls.lpfnWndProc      = ::DefWindowProc;
		wndcls.cbClsExtra       = wndcls.cbWndExtra = 0;
        wndcls.hInstance        = hInst;
        wndcls.hIcon            = NULL;
        wndcls.hCursor          = NULL;
        wndcls.hbrBackground    = (HBRUSH) (COLOR_3DFACE + 1);
        wndcls.lpszMenuName     = NULL;
        wndcls.lpszClassName    = SPINEDITCTRL_CLASSNAME;

        if (!AfxRegisterClass(&wndcls)) {
            AfxThrowResourceException();
            return FALSE;
        }
    }

    return TRUE;
}

CSpinEdit::~CSpinEdit()
{
	if( m_pEdit	!= NULL )
		delete m_pEdit;
}


BOOL CSpinEdit::Create( CWnd* pParentWnd, CRect rect, UINT nID, DWORD dwStyle )
{
 	DWORD dwSpinEditStyle = WS_BORDER|WS_CHILD | WS_TABSTOP|dwStyle;

    if (!CWnd::Create(SPINEDITCTRL_CLASSNAME, NULL, dwSpinEditStyle, rect, pParentWnd, nID)) 
        return FALSE;

	SetFont( pParentWnd->GetFont() );

	try
	{
		if( m_pEdit == NULL )
		{
			m_pEdit = new CSEEdit;
			m_pEdit->Create( 0, this, IDC_SEEDIT );				
		}
	}
	catch(CMemoryException *pEx )
	{
		TRACE( "exception at create CSpinEdit, memory not enough!" );
		pEx->Delete();
		return FALSE;
	}	

	return TRUE;
}

BOOL CSpinEdit::SubclassWindow(HWND hWnd) 
{    
    if (!CWnd::SubclassWindow(hWnd)) return FALSE;

	try
	{
		if ( m_pEdit == NULL)
		{
			m_pEdit = new CSEEdit;
			m_pEdit->Create( 0, this, IDC_SEEDIT );				
		}	
	}
	catch(CMemoryException *pEx )
	{
		TRACE( "exception when creating CSpinEdit, memory not enough!\n" );
		pEx->Delete();
		return FALSE;
	}

    return TRUE;
}

BEGIN_MESSAGE_MAP(CSpinEdit, CWnd)
	//{{AFX_MSG_MAP(CSpinEdit)
	ON_WM_KILLFOCUS()
	ON_WM_KEYUP()
	ON_WM_PAINT()
	ON_WM_LBUTTONUP()
    ON_WM_LBUTTONDOWN()
	ON_WM_LBUTTONDBLCLK()
    ON_WM_CHAR()
	ON_WM_CAPTURECHANGED()
	ON_WM_TIMER()
	ON_WM_GETDLGCODE()
	ON_WM_SETCURSOR()
	ON_WM_SETFOCUS()	
	ON_WM_MOUSEWHEEL()
	ON_NOTIFY(SEN_ENDLABELEDIT, IDC_SEEDIT, OnEndInplaceEdit)
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CSpinEdit message handlers

/////////////////////////////////////////////////////////////////////////////
// CSpinEdit message handlers

void CSpinEdit::OnKillFocus(CWnd* pNewWnd) 
{
	CWnd::OnKillFocus(pNewWnd );
	Invalidate();	
}

// Handles mouse wheel notifications
// Note - if this doesn't work for win95 then use OnRegisteredMouseWheel instead
BOOL CSpinEdit::OnMouseWheel(UINT nFlags, short zDelta, CPoint pt) 
{
    // A m_nRowsPerWheelNotch value less than 0 indicates that the mouse
    // wheel scrolls whole pages, not just lines.
    if (m_nStepsPerWheelNotch == -1)
    {
        int nPagesScrolled = zDelta / 120;

        if (nPagesScrolled > 0)
            for (int i = 0; i < nPagesScrolled; i++)
                AddValue( m_nStepsPerWheelNotch );
        else
            for (int i = 0; i > nPagesScrolled; i--)
                SubValue( m_nStepsPerWheelNotch );
    }
    else
    {
        int nRowsScrolled = m_nStepsPerWheelNotch * zDelta / 120;

        if (nRowsScrolled > 0)
            for (int i = 0; i < nRowsScrolled; i++)
                AddValue( );
        else
            for (int i = 0; i > nRowsScrolled; i--)
                SubValue( );
    }

    return CWnd::OnMouseWheel(nFlags, zDelta, pt);
}

/////////////////////////////////////////////////////////////////////////////

void CSpinEdit::OnPaint() 
{
	CPaintDC dc(this); // device context for painting	
	OnDraw( &dc );
}

void CSpinEdit::OnDraw(CDC* pDC)
{
	CRect interrect, rect;
    CRect clipRect;
    if (pDC->GetClipBox(&clipRect) == ERROR) return;

	DrawFrame( pDC );

	if( GetUpperBtnRect( rect ) )
	{
		if( interrect.IntersectRect( rect, clipRect ) )
		{
			DrawUpperBtn( pDC );
		}
	}

	if( GetLowerBtnRect( rect ) )
	{
		if( interrect.IntersectRect( rect, clipRect ) )
		{
			DrawLowerBtn( pDC );
		}
	}

	if( GetTextRect( rect ) )
	{
		if( interrect.IntersectRect( rect, clipRect ) )
		{
			DrawText( pDC );
		}
	}
}

UINT CSpinEdit::OnGetDlgCode() 
{
    UINT nCode = DLGC_WANTARROWS | DLGC_WANTCHARS;

    if ( !IsCTRLpressed()) 
        nCode |= DLGC_WANTTAB;

    return nCode;
}

BOOL CSpinEdit::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message) 
{
    if (nHitTest == HTCLIENT)
    {
        SetCursor(::LoadCursor(NULL, IDC_ARROW));
    }
    
    return CWnd::OnSetCursor(pWnd, nHitTest, message);
}

// make a focused frame
void CSpinEdit::OnSetFocus(CWnd* pOldWnd) 
{
    CWnd::OnSetFocus(pOldWnd );
	Invalidate();
}

//stop changing value automaticly
void CSpinEdit::OnCaptureChanged(CWnd *pWnd) 
{
    if (pWnd->GetSafeHwnd() == GetSafeHwnd()) return;

	// kill timer if active
    if (m_nTimerID != 0)
    {
        KillTimer(m_nTimerID);
        m_nTimerID = 0;
    }
}

//if the lower button or upper button was pressed, decrease or increase the value automaticly
void CSpinEdit::OnTimer(UINT nIDEvent)
{
	ASSERT(nIDEvent == WM_LBUTTONDOWN);
    if (nIDEvent != WM_LBUTTONDOWN) return;

	if (m_bUpperBtnPushed) AddValue();
	if (m_bLowerBtnPushed) SubValue();
    
}

// Instant editing of cells when keys are pressed
void CSpinEdit::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags) 
{
    if (!IsCTRLpressed())
    {
		ShowEdit( nChar );    
    }

    CWnd::OnChar(nChar, nRepCnt, nFlags);
}

void CSpinEdit::OnLButtonDblClk(UINT nFlags, CPoint point) 
{
	CRect rect;
    GetClientRect(rect);

    // If outside client area, return 
    if ( !rect.PtInRect(point))  return;

	//Inclease the current value if the mouse point in the rect of upper button
	if( GetUpperBtnRect( rect ) )
	{
		if( rect.PtInRect( point ) )
		{
			SetFocus();        // Auto-destroy any InPlaceEdit's
			m_bUpperBtnPushed = TRUE;
			AddValue();
		}
	}

	//Declease the current value if the mouse point in the rect of lower button
	if( GetLowerBtnRect( rect ) )
	{
		if( rect.PtInRect( point ) )
		{
			SetFocus();        // Auto-destroy any InPlaceEdit's
			m_bLowerBtnPushed = TRUE;
			SubValue( );
		}
	}

	//Edit the current value if the mouse point in the rect of Text Area
	if( GetTextRect( rect ) )
	{
		if( rect.PtInRect( point ) )
		{
			ShowEdit( VK_LBUTTON );				
		}
	}
	
    CWnd::OnLButtonDblClk(nFlags, point);
}

void CSpinEdit::OnLButtonDown(UINT nFlags, CPoint point)
{
    SetFocus();        // Auto-destroy any InPlaceEdit's
	SetCapture();

	CRect rect;
    GetClientRect(rect);

    // If outside client area, return 
    if ( !rect.PtInRect(point))  return;

	//Inclease the current value if the mouse point in the rect of upper button
	if( GetUpperBtnRect( rect ) )
	{
		if( rect.PtInRect( point ) )
		{
			m_bUpperBtnPushed = TRUE;
			AddValue( );			
			m_nTimerID = SetTimer(WM_LBUTTONDOWN, m_nTimerInterval, 0);
		}
	}

	//Declease the current value if the mouse point in the rect of lower button
	if( GetLowerBtnRect( rect ) )
	{
		if( rect.PtInRect( point ) )
		{
			m_bLowerBtnPushed = TRUE;
			SubValue( );			
			m_nTimerID = SetTimer(WM_LBUTTONDOWN, m_nTimerInterval, 0);
		}
	}
    
	//如果鼠标在文本区则直接进入编辑状态
	if( GetTextRect( rect ) )
	{
		if( rect.PtInRect( point ) )
		{
			ShowEdit( VK_LBUTTON );				
		}
	}

	CWnd::OnLButtonDown(nFlags, point);
}


void CSpinEdit::OnLButtonUp(UINT nFlags, CPoint point)
{
    CWnd::OnLButtonUp(nFlags, point);
    if (GetCapture()->GetSafeHwnd() == GetSafeHwnd())
    {
        ReleaseCapture();
		if( m_nTimerID != 0 )
		{
			KillTimer(m_nTimerID);
		    m_nTimerID = 0;
		}
    }

	m_bUpperBtnPushed = FALSE;
	m_bLowerBtnPushed = FALSE;
	RedrawLowerBtn();
	RedrawUpperBtn();
	RedrawText();

	//notify the parent that the spintedit ctrl was clicked
    CWnd *pOwner = GetOwner();
    if (pOwner && IsWindow(pOwner->m_hWnd))
        pOwner->PostMessage(WM_COMMAND, MAKELONG(GetDlgCtrlID(), BN_CLICKED), 
                            (LPARAM) GetSafeHwnd());
}

void CSpinEdit::OnEndInplaceEdit(NMHDR* pNMHDR, LRESULT* pResult) 
{    
    SE_NOTIFYMSG *pMsg = (SE_NOTIFYMSG *)pNMHDR;
    
    if (!IsWindow(GetSafeHwnd()))
        return;

	m_bEditShow = FALSE;

    if( strtod(pMsg->szText, NULL ) != m_dbValue )
	{
		double tempValue;
		tempValue = _tcstod(pMsg->szText, NULL );
		SetCurValue( tempValue );
	}

	if( pMsg->lParam == VK_TAB )
	{
		OnChar( VK_TAB, 1, 0 );
	}
	
    *pResult = 0;
}

//add curruent by dtStepNum*dbStep
BOOL CSpinEdit::AddValue( DWORD dwStepNum )
{
	double tempValue;
	
	tempValue = m_dbValue + dwStepNum*m_dbStep;
	
	if( ( tempValue < m_dbLower ) || ( tempValue > m_dbUpper ) )
	{
		tempValue = DoubleModValue( ( tempValue-m_dbLower ), ( m_dbUpper-m_dbLower ), m_bPointPos );
		if( m_dbValue == m_dbUpper )
		{
			m_dbValue = m_dbLower;
		}
		else
		{
			m_dbValue = m_dbLower+tempValue;
		}
	}
	else
	{
		m_dbValue = tempValue;
	}

	if( IsWindow ( m_hWnd ) )
	{
		RedrawText();
		RedrawUpperBtn();
	}

	return TRUE;
}

//subtract curruent by dtStepNum*dbStep
BOOL CSpinEdit::SubValue( DWORD dwStepNum )
{
	double tempValue;
	
	tempValue = m_dbValue - dwStepNum*m_dbStep;
		
	if( ( tempValue < m_dbLower ) || ( tempValue > m_dbUpper ) )
	{
		tempValue = DoubleModValue( ( tempValue-m_dbLower ), ( m_dbUpper-m_dbLower ), m_bPointPos );
		if( m_dbValue == m_dbLower )
		{
			m_dbValue = m_dbUpper;
		}
		else
		{
			m_dbValue = m_dbUpper+tempValue;
		}
	}
	else
	{
		m_dbValue = tempValue;
	}

	if( IsWindow ( m_hWnd ) )
	{
		RedrawText();
		RedrawLowerBtn();
	}

	return TRUE;
}

// Get the value and extra-text
CString CSpinEdit::GetShowText( BOOL bNeedExtraText )
{
	CString strShow;
	switch( m_bValueType )
	{
	case SE_INT: 
		{
			if( bNeedExtraText && m_bNeedExtraText )
				strShow.Format( _T("%d%s"), (int)m_dbValue, m_szExtraText );
			else
				strShow.Format( _T("%d"), (int)m_dbValue );
			break;
		}

	case SE_FLOAT:
	case SE_DOUBLE: 
		{
			CString strFormat;
			if( bNeedExtraText && m_bNeedExtraText )
			{
				strFormat.Format( _T("%%.%df%%s"), m_bPointPos );		//strFormat = "%.xf%s"
				strShow.Format( strFormat, m_dbValue, m_szExtraText );
			}
			else
			{
				strFormat.Format( _T("%%.%df"), m_bPointPos );		//strFormat = "%.xf"
				strShow.Format( strFormat, m_dbValue );
			}
			break;
		}
	}	//OBTAIN strShow

	return strShow;
}
// draw value and extra-text
BOOL CSpinEdit::DrawText( CDC* pDC)
{
	BOOL bResult = TRUE;
    BOOL bMustReleaseDC = FALSE;

	if( m_bEditShow ) return bResult;

    CRect rect;
    if (!GetTextRect( rect)) return FALSE;
	
    if (!pDC) 
	{
        pDC = GetDC();
        if (pDC) bMustReleaseDC = TRUE;
    }

    if (pDC)
    {
        int nSavedDC = pDC->SaveDC();
		pDC->SetBkMode(TRANSPARENT);
		
		CFont ft;
		// Create the appropriate font and select into DC
		if (!ft.CreateStockObject(DEFAULT_GUI_FONT))
		if (!ft.CreatePointFont(80, "MS Sans Serif"))
			return FALSE;
		pDC->SelectObject(&ft);    
		rect.DeflateRect(2, 0);

		CString strShow = GetShowText( );
		CBrush brush(RGB(255,255,255));
		pDC->FillRect( rect, &brush );
		pDC->DrawText(strShow, -1, rect, DT_CENTER|DT_VCENTER|DT_SINGLELINE|DT_END_ELLIPSIS);

		pDC->RestoreDC(nSavedDC);
    } else
        InvalidateRect(rect, TRUE);     // Could not get a DC - invalidate it anyway
                                        // and hope that OnPaint manages to get one

    if (bMustReleaseDC) 
        ReleaseDC(pDC);

    return bResult;
}

// mainly drawing the black triangle on upper button

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -