📄 ipaddr.cpp
字号:
#pragma title("IP Address Custom Control Implementation")
// Created by Joseph A. Dziedzic, September 1997
// Revised April 1998
// Thanks to Dan Anderson, Kenny Goers, Kevin Lussier, and Doug Miller for their suggestions
// and code enhancements.
// Mail comments to dziedzic@ma.ultranet.com
#include "stdafx.h"
#include "IPAddr.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
// Style bits for the individual edit controls
const int WS_EDIT = WS_CHILD | WS_VISIBLE | ES_CENTER | ES_MULTILINE;
const TCHAR szDialogClass[] = _T("#32770"); // Special window class for dialogs
BOOL CIPAddrCtl::m_bRegistered = Register(); // Register the control during class initialization
/////////////////////////////////////////////////////////////////////////////
// CIPAddrCtl
IMPLEMENT_DYNCREATE(CIPAddrCtl, CWnd)
CIPAddrCtl::CIPAddrCtl()
{
m_bEnabled = TRUE; // Window enabled flag (TRUE by default)
m_bReadOnly = FALSE; // Read only flag (FALSE by default)
m_bNoValidate = FALSE; // Don't do immediate field validation on input
}
CIPAddrCtl::~CIPAddrCtl()
{
}
BEGIN_MESSAGE_MAP(CIPAddrCtl, CWnd)
//{{AFX_MSG_MAP(CIPAddrCtl)
ON_WM_CREATE()
ON_WM_NCDESTROY()
ON_WM_SIZE()
ON_WM_SETFOCUS()
ON_WM_PAINT()
ON_WM_ENABLE()
ON_WM_ERASEBKGND()
ON_MESSAGE(WM_SETFONT, OnSetFont)
ON_MESSAGE(IPAM_GETADDRESS, OnGetAddress)
ON_MESSAGE(IPAM_SETADDRESS, OnSetAddress)
ON_MESSAGE(IPAM_SETREADONLY, OnSetReadOnly)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CIPAddrCtl message handlers
BOOL CIPAddrCtl::Register()
{
// Register the window class of the control
WNDCLASS wc;
wc.style = CS_GLOBALCLASS | CS_HREDRAW | CS_VREDRAW; // Usual style bits
wc.lpfnWndProc = IPAddrWndProc; // Message processing code
wc.cbClsExtra = 0; // No extra bytes needed
wc.cbWndExtra = 0;
wc.hInstance = NULL; // No instance handle
wc.hIcon = NULL; // No icon
wc.hCursor = ::LoadCursor(NULL, IDC_IBEAM); // Use I-beam cursor (like edit control)
wc.hbrBackground = (HBRUSH) (COLOR_WINDOW+1); // Use default window color (overriden in OnEraseBkgnd)
wc.lpszMenuName = NULL; // No menus
wc.lpszClassName = _T("IPAddr"); // Class name
if (!::RegisterClass(&wc)) // If registration failed, subsequent dialogs will fail
{
ASSERT(FALSE);
return FALSE;
}
else
return TRUE;
}
BOOL CIPAddrCtl::Create(DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID, DWORD dwExStyle/*=0*/)
{
// Create a window class that has the properties we want
CString szWndClass = AfxRegisterWndClass(CS_GLOBALCLASS | CS_VREDRAW | CS_HREDRAW,
::LoadCursor(NULL, IDC_IBEAM), (HBRUSH) COLOR_WINDOW+1);
// Create using the extended window style
#if _MSC_VER >= 1100
// Original VC 5.0 stuff
return CWnd::CreateEx(dwExStyle, szWndClass, NULL, dwStyle, rect, pParentWnd, nID);
#else
// Back ported to VC 4.2
return CWnd::CreateEx(dwExStyle, szWndClass, NULL, dwStyle,
rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top,
pParentWnd->GetSafeHwnd(), (HMENU) nID);
#endif
}
LRESULT CALLBACK IPAddrWndProc(HWND hWnd, UINT uiMsg, WPARAM wParam, LPARAM lParam)
{
switch (uiMsg) // Dispatch on message type
{
case WM_NCCREATE: // On WM_NCCREATE we create a C++ object and attach it to the control
{
CIPAddrCtl* pCtl = new CIPAddrCtl; // Create an instance of the class
ASSERT(pCtl); // Better not fail!
BOOL b = pCtl->SubclassWindow(hWnd); // Attach the window handle to the new object
ASSERT(b); // Better not fail!
return b; // Return result to continue/abort window creation
break;
}
default: // All other messages go through default window processor
return ::DefWindowProc(hWnd, uiMsg, wParam, lParam);
}
}
int CIPAddrCtl::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CWnd::OnCreate(lpCreateStruct) == -1)
return -1;
// Save the "no immediate validation on input" style setting
m_bNoValidate = (lpCreateStruct->style & IPAS_NOVALIDATE);
// Set the styles for the parent control
ModifyStyleEx(0, WS_EX_CLIENTEDGE | WS_EX_NOPARENTNOTIFY);
// Create the four edit controls used to obtain the four parts of the IP address (size
// of controls gets set during OnSize)
for (int ii = 0; ii < 4; ii++)
{
m_Addr[ii].Create(WS_EDIT, CRect(0,0,0,0), this, IDC_ADDR1 + ii);
m_Addr[ii].LimitText(3);
m_Addr[ii].SetParent(this);
}
return 0;
}
void CIPAddrCtl::OnNcDestroy()
{
CWnd::OnNcDestroy();
// Make sure the window was destroyed
ASSERT(NULL == m_hWnd);
// Destroy this object since it won't be destroyed otherwise
delete this;
}
void CIPAddrCtl::OnSize(UINT nType, int cx, int cy)
{
CWnd::OnSize(nType, cx, cy);
// Get the width of a "." drawn in the control
CDC* pDC = GetDC();
CSize szDot = pDC->GetTextExtent(_T("."), 1);
int nDotWidth = szDot.cx;
ReleaseDC(pDC);
// Based on the size of the parent window, compute the width & height of the edit
// controls. Leave room for the three "." which will be drawn on the parent window
// to separate the four parts of the IP address.
CRect rcClient;
GetClientRect(&rcClient);
int nEditWidth = (rcClient.Width() - (3 * nDotWidth)) / 4;
int nEditHeight = rcClient.Height();
int cyEdge = ::GetSystemMetrics(SM_CYEDGE);
// Compute rectangles for the edit controls, then move the controls into place
CRect rect = CRect(0, cyEdge, nEditWidth, nEditHeight);
for (int ii = 0; ii < 4; ii++)
{
m_rcAddr[ii] = rect;
m_Addr[ii].MoveWindow(rect);
rect.OffsetRect(nEditWidth + nDotWidth, 0);
}
rect = CRect(nEditWidth, 0, nEditWidth + nDotWidth, nEditHeight);
for (ii = 0; ii < 3; ii++)
{
m_rcDot[ii] = rect;
rect.OffsetRect(nEditWidth + nDotWidth, 0);
}
}
void CIPAddrCtl::OnSetFocus(CWnd* pOldWnd)
{
CWnd::OnSetFocus(pOldWnd);
m_Addr[0].SetFocus(); // Set focus to first edit control
m_Addr[0].SetSel(0, -1); // Select entire contents
}
// Protected function called by the edit control (friend class) when it receives a
// character which should be processed by the parent
void CIPAddrCtl::OnChildChar(UINT nChar, UINT nRepCnt, UINT nFlags, CIPAddrEdit& child)
{
switch (nChar)
{
case '.': // Dot means advance to next edit control (if in first 3)
case VK_RIGHT: // Ditto for right arrow at end of text
case ' ': // Ditto for space
{
UINT nIDC = child.GetDlgCtrlID(); // Get control ID of the edit control
if (nIDC < IDC_ADDR4) // Move focus to appropriate edit control and select entire contents
{
m_Addr[nIDC - IDC_ADDR1 + 1].SetFocus();
if (VK_RIGHT != nChar) // Re-select text unless arrow key entered
m_Addr[nIDC - IDC_ADDR1 + 1].SetSel(0, -1);
}
break;
}
case VK_LEFT: // Left arrow means move to previous edit control (if in last 3)
{
UINT nIDC = child.GetDlgCtrlID(); // Get control ID of the edit control
if (nIDC > IDC_ADDR1) // Move focus to appropriate edit control
m_Addr[nIDC - IDC_ADDR1 - 1].SetFocus();
break;
}
case VK_TAB: // Tab moves between controls in the dialog
{
CWnd* pWnd;
SHORT nShift = ::GetKeyState(VK_SHIFT); // Get state of shift key
if (nShift < 0)
pWnd = GetParent()->GetNextDlgTabItem(this, TRUE);
else
pWnd = GetParent()->GetNextDlgTabItem(this, FALSE);
if (NULL != pWnd) // If there's a control, set focus to it
pWnd->SetFocus();
break;
}
case VK_RETURN: // Return implies default pushbutton press
{
DWORD dw = ((CDialog*) GetParent())->GetDefID(); // Get ID of default pushbutton
if (DC_HASDEFID == HIWORD(dw)) // If there is a default pushbutton, simulate pressing it
{
CWnd* pWnd = GetParent()->GetDlgItem(LOWORD(dw)); // Get the control
WPARAM wp = MAKEWPARAM(LOWORD(dw), BN_CLICKED); // Build wParam for WM_COMMAND
GetParent()->SendMessage(WM_COMMAND, wp, (LPARAM) pWnd->m_hWnd); // Fake like button was pressed
}
}
break;
case '-': // "Field full" indication
// Validate the contents for proper values (unless suppressed)
if (!m_bNoValidate) // If not suppressing immediate validation
{
CString szText;
child.GetWindowText(szText); // Get text from edit control
int n = _ttoi(szText); // Get numeric value from edit control
if (n < 0 || n > 255) // If out of range, notify parent
{
szText.Format(_T("%d is not a valid entry. Please specify a value between 0 and 255 for this field."), n);
MessageBox(szText, _T("Error"), MB_OK | MB_ICONEXCLAMATION);
child.SetFocus(); // Set focus to offending field
child.SetSel(0, -1); // Select all text
return;
}
}
// Advance to next field
OnChildChar('.', 0, nFlags, child);
break;
default:
TRACE(_T("Unexpected call to CIPAddrCtl::OnChildChar!\n"));
}
}
void CIPAddrCtl::OnPaint()
{
CPaintDC dc(this); // device context for painting
// Save mode and set to transparent (so background remains)
int nOldMode = dc.SetBkMode(TRANSPARENT);
// If disabled, set text color to COLOR_GRAYTEXT, else use COLOR_WINDOWTEXT
COLORREF crText;
if (m_bEnabled)
crText = ::GetSysColor(COLOR_WINDOWTEXT);
else
crText = ::GetSysColor(COLOR_GRAYTEXT);
COLORREF crOldText = dc.SetTextColor(crText);
// Draw the three "." which separate the four edit controls
for (int ii = 0; ii < 3; ii++)
dc.DrawText(_T("."), 1, m_rcDot[ii], DT_CENTER | DT_SINGLELINE | DT_BOTTOM);
// Restore old mode and color
dc.SetBkMode(nOldMode);
dc.SetTextColor(crOldText);
// Do not call CWnd::OnPaint() for painting messages
}
BOOL CIPAddrCtl::OnEraseBkgnd(CDC* pDC)
{
CRect rcClient;
GetClientRect(&rcClient);
if (m_bEnabled && !m_bReadOnly)
::FillRect(pDC->m_hDC, rcClient, (HBRUSH) (COLOR_WINDOW+1));
else
::FillRect(pDC->m_hDC, rcClient, (HBRUSH) (COLOR_BTNFACE+1));
return TRUE;
}
void CIPAddrCtl::OnEnable(BOOL bEnable)
{
CWnd::OnEnable(bEnable);
// Nothing to do unless the window state has changed
if (bEnable != m_bEnabled)
{
// Save new state
m_bEnabled = bEnable;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -