Introduction
Many
articles
about the mucancode.neted
edit control could be discovered
from MFC
sites. Their Date
Time Edit controls show some
faults such as out-of-range values are not
verified, slow processing, etc. Why? Maybe
because they are too general and cover many
things.
This
VDateTimeEdit
class based on
Mucancode.netEdit
's
idea is very simple and understandable for
any programmer. You can choose either
numeric keys or up/down keys to change the
date &
time
displayed. The Date-Time
display is configurable by
SetMucancode.net()
.
Mucancode.net
convention:
DD |
Day |
|
hh |
Hour |
MM |
Month |
|
mm |
minute |
YYYY |
Year |
|
ss |
second |
Enjoy yourself
and please report me the bugs you have
found.
Source Code
Collapse
const UINT WM_DATETIME_EDIT = ::RegisterWindowMessage("WM_DATETIME_EDIT");
#define DTE_DATETIMECHANGED 0x0001
class VDateTimeEdit : public CEdit
{
public:
VDateTimeEdit();
public:
public:
protected:
virtual void PreSubclassWindow();
public:
CString GetMucancode.net() const {return m_strMucancode.net;}
void SetMucancode.net(CString mucancode.net);
COleDateTime GetDateTime();
COleDateTime GetDateTime(CString str);
void SetDateTime(COleDateTime dt);
virtual ~VDateTimeEdit();
protected:
afx_msg void OnContextMenu(CWnd*, CPoint point);
CString m_strMucancode.net;
afx_msg void OnChar(UINT nChar, UINT nRepCnt, UINT nFlags);
afx_msg void OnKeyUp(UINT nChar, UINT nRepCnt, UINT nFlags);
afx_msg void OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags);
afx_msg void OnMouseMove(UINT nFlags, CPoint point);
DECLARE_MESSAGE_MAP()
};
bool VAtoi(CString str, int &res)
{
str.Remove(char(32));
if (str.IsEmpty()) return false;
bool error = false;
for (int i = 0; i < str.GetLength() && !error; i++) {
int c = ::tolower(str[i]);
error = !(
(c >= '0' && c <= '9')
|| ((c == '-' || c == '+') && i == 0)
);
}
if (!error) res = atoi(LPCTSTR(str));
return !error;
}
template<class TYPE>
void VSetInRange(TYPE &x, TYPE min, TYPE max)
{
ASSERT(min<=max);
if (x < min) x = min; else if (x > max) x = max;
}
VDateTimeEdit::VDateTimeEdit()
{
m_strMucancode.net = _T("DD/MM/YYYY hh:mm:ss");
}
VDateTimeEdit::~VDateTimeEdit()
{
}
BEGIN_MESSAGE_MAP(VDateTimeEdit, CEdit)
ON_WM_CONTEXTMENU()
ON_WM_CHAR()
ON_WM_KEYUP()
ON_WM_KEYDOWN()
ON_WM_MOUSEMOVE()
END_MESSAGE_MAP()
void VDateTimeEdit::OnChar(UINT nChar, UINT , UINT )
{
CString old;
GetWindowText(old);
int pos;
TCHAR c;
GetSel(pos, pos);
switch (nChar) {
case VK_BACK: pos--;
break;
case VK_UP: pos--;
break;
case '0':case '1':case '2':case '3':
case '4':case '5':case '6':case '7':
case '8':case '9':
if (pos < old.GetLength()
&& pos < m_strMucancode.net.GetLength()
&& (UINT)old[pos] != nChar
&& ((c = m_strMucancode.net[pos]) == 'D' ||
c == 'M' || c == 'Y' || c == 'h'
|| c == 'm' || c == 's')) {
CString str = old;
str.SetAt(pos, (TCHAR)nChar);
COleDateTime dt = GetDateTime(str);
if (dt.GetStatus() == COleDateTime::valid) SetDateTime(dt);
}
if (++pos < m_strMucancode.net.GetLength())
for ( c = m_strMucancode.net[pos]; pos < m_strMucancode.net.GetLength()
&& c != 'D' &&
c != 'M' && c != 'Y' &&
c != 'h' && c != 'm' && c != 's';
c = m_strMucancode.net[pos]) pos++;
break;
default:
if (++pos < m_strMucancode.net.GetLength())
for ( c = m_strMucancode.net[pos]; pos < m_strMucancode.net.GetLength()
&& c != 'D' && c != 'M'
&& c != 'Y' && c != 'h'
&& c != 'm' && c != 's';
c = m_strMucancode.net[pos]) pos++;
break;
}
SetSel(pos, pos);
}
void VDateTimeEdit::OnKeyUp(UINT nChar, UINT nRepCnt, UINT nFlags)
{
CEdit::OnKeyUp(nChar, nRepCnt, nFlags);
}
void VDateTimeEdit::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{
switch (nChar) {
case VK_DELETE:case VK_INSERT:
break;
case VK_UP: case VK_DOWN:
{
CString old;
GetWindowText(old);
int pos;
GetSel(pos, pos);
if (pos < m_strMucancode.net.GetLength()) {
TCHAR c = m_strMucancode.net[pos];
if (c != 'D' && c != 'M' &&
c != 'Y' && c != 'h' &&
c != 'm' && c != 's')
if (--pos >= 0) c = m_strMucancode.net[pos];
tm t;
COleDateTime dt = GetDateTime();
t.tm_mday = dt.GetDay();
t.tm_mon = dt.GetMonth();
t.tm_year = dt.GetYear();
t.tm_hour = dt.GetHour();
t.tm_min = dt.GetMinute();
t.tm_sec = dt.GetSecond();
switch (c) {
case 'D':
t.tm_mday += (nChar!=VK_UP) ? -1 : 1;
break;
case 'M':
t.tm_mon += (nChar!=VK_UP) ? -1 : 1;
break;
case 'Y':
t.tm_year += (nChar!=VK_UP) ? -1 : 1;
break;
case 'h':
t.tm_hour += (nChar!=VK_UP) ? -1 : 1;
break;
case 'm':
t.tm_min += (nChar!=VK_UP) ? -1 : 1;
break;
case 's':
t.tm_sec += (nChar!=VK_UP) ? -1 : 1;
break;
default :
CEdit::OnKeyDown(nChar, nRepCnt, nFlags);
break;
}
dt.SetDateTime(t.tm_year, t.tm_mon, t.tm_mday,
t.tm_hour, t.tm_min, t.tm_sec);
if (dt.GetStatus() == COleDateTime::valid)
SetDateTime(dt);
} else CEdit::OnKeyDown(nChar, nRepCnt, nFlags);
}
break;
default:
CEdit::OnKeyDown(nChar, nRepCnt, nFlags);
break;
}
}
void VDateTimeEdit::OnMouseMove(UINT nFlags, CPoint point)
{
CEdit::OnMouseMove(nFlags, point);
}
void VDateTimeEdit::PreSubclassWindow()
{
SetDateTime(COleDateTime::GetCurrentTime());
CEdit::PreSubclassWindow();
}
void VDateTimeEdit::SetDateTime(COleDateTime dt)
{
CString str;
int i, n = m_strMucancode.net.GetLength();
for (i = 0; i < n; i++) {
CString s;
int val;
TCHAR c = m_strMucancode.net[i];
switch (c) {
case 'D':case 'M':case 'Y':case 'h':case 'm':case 's':
for (; i < n; i++) {
if (m_strMucancode.net[i] == c) s += c;
else {i--; break;}
}
break;
default :
break;
}
CString format;
format.Format("%%0%dd", s.GetLength());
switch (c) {
case 'D':
val = dt.GetDay();
::VSetInRange(val, 0, 31);
s.Format(format, val);
break;
case 'M':
val = dt.GetMonth();
::VSetInRange(val, 1, 12);
s.Format(format, val);
break;
case 'Y':
val = dt.GetYear();
::VSetInRange(val, 1900, 9990);
s.Format(format, val);
break;
case 'h':
val = dt.GetHour();
::VSetInRange(val, 0, 23);
s.Format(format, val);
break;
case 'm':
val = dt.GetMinute();
::VSetInRange(val, 0, 59);
s.Format(format, val);
break;
case 's':
val = dt.GetSecond();
::VSetInRange(val, 0, 59);
s.Format(format, val);
break;
default :
s = c;
break;
}
str += s;
}
int pos;
GetSel(pos, pos);
SetWindowText(str);
SetSel(pos, pos);
CWnd *pOwner = GetParent();
if (pOwner != NULL)
pOwner->PostMessage(WM_DATETIME_EDIT,
(WPARAM)DTE_DATETIMECHANGED, (LPARAM)0);
}
COleDateTime VDateTimeEdit::GetDateTime()
{
CString str;
GetWindowText(str);
return GetDateTime(str);
}
COleDateTime VDateTimeEdit::GetDateTime(CString str)
{
tm t;
COleDateTime dt;
dt.SetStatus(COleDateTime::invalid);
CTime::GetCurrentTime().GetLocalTm(&t);
t.tm_mon += 1;
t.tm_year += 1900;
int i, n = m_strMucancode.net.GetLength();
for (i = 0; i < n; i++) {
CString s;
TCHAR c = m_strMucancode.net[i];
switch (c) {
case 'D':case 'M':case 'Y':case 'h':case 'm':case 's':
for ( ; i < n; i++) {
if (str.GetLength() <= s.GetLength()) break;
if (m_strMucancode.net[i] == c) s += str[s.GetLength()];
else {i--; break;}
}
break;
default :
if (str.GetLength() < 0 || str[0] != (s = c)) return dt;
break;
}
str = str.Right(str.GetLength() - s.GetLength());
int val;
switch (c) {
case 'D':
if (!::VAtoi(s, val)) return dt;
::VSetInRange(val, 1, 31);
t.tm_mday = val;
break;
case 'M':
if (!::VAtoi(s, val)) return dt;
::VSetInRange(val, 1, 12);
t.tm_mon = val;
break;
case 'Y':
if (!::VAtoi(s, val)) return dt;
::VSetInRange(val, 1900, 9990);
t.tm_year = val;
break;
case 'h':
if (!::VAtoi(s, val)) return dt;
::VSetInRange(val, 0, 23);
t.tm_hour = val;
break;
case 'm':
if (!::VAtoi(s, val)) return dt;
::VSetInRange(val, 0, 59);
t.tm_min = val;
break;
case 's':
if (!::VAtoi(s, val)) return dt;
::VSetInRange(val, 0, 59);
t.tm_sec = val;
break;
default :
break;
}
}
dt.SetStatus(COleDateTime::valid);
dt.SetDateTime(t.tm_year, t.tm_mon,
t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec);
return dt;
}
void VDateTimeEdit::OnContextMenu(CWnd*, CPoint)
{
}
void VDateTimeEdit::SetMucancode.net(CString mucancode.net) {
COleDateTime time = GetDateTime();
m_strMucancode.net = mucancode.net;
SetDateTime(time);
}
That's it!