以一个显示用户通话时长的界面为例,要在一个static控件上绘制“通话时长:XX:XX:XX”
关于绘制,可以使用获得到控件的句柄和CDC,通过 DrawText 绘制,也可以通过API函数UpdateData 或 SetWindowText 进行更新。
关于计时,可以通过线程来计时也可以通过利用计时器来计时。
下面,按照绘制方式的不同来进行比较,看哪个最方便啦~
方法一: DrawText + 线程计时
这无疑是最笨的一种方法啦...
代码:
//变量
CDC* pDCStatic;//static控件的CDC
BOOL bIintState;//是否初始化完毕(对WM_SIZE的处理必须是在初始化完毕后,否则出错)
BOOL bOnLine;//线程运行控制符
DWORD dwStartTime;//用来记录通话起始时间 在需要开始计时的地方通过 GetTickCount()获得
HANDLE hUpdateEvent;//用于线程同步的Event
CWinThread* pThreadUpdate;//计时线程句柄
static UINT CALLBACK PaintWindow(LPVOID lParam);//计时线程
void GetTimeSpace(DWORD dwStartTime,CString & szTime);//用来得到时长的CString形态
//对话框初始化函数
BOOL C****Dlg::OnInitDialog()
{
CDialog::OnInitDialog();
// TODO: Add extra initialization here
this->pDCStatic = NULL;
this->bOnLine = TRUE;
//创建线程同步事件
this->hUpdateEvent = CreateEvent(NULL, TRUE, FALSE, "UpdateEvent") ;
//创建计时线程
this->pThreadUpdate=new CWinThread;
this->pThreadUpdate->m_bAutoDelete=true;
this->pThreadUpdate=AfxBeginThread(AFX_THREADPROC(PaintWindow),this,THREAD_PRIORITY_NORMAL,0,0,NULL);
//初始化结束置位
this->bIintState = FALSE;
return TRUE; // return TRUE unless you set the focus to a control
// EXCEPTION: OCX Property Pages should return FALSE
}
//窗口WM_SIZE响应函数
void C****Dlg::OnSize(UINT nType, int cx, int cy)
{
CDialog::OnSize(nType, cx, cy);
// TODO: Add your message handler code here
if(!this->bIintState)
{
CRect rcOnLine;
//获取当前窗口的客户区大小
GetClientRect(&rcOnLine);
CWnd* hStatic = GetDlgItem(IDC_TIME);
this->pDCStatic = hStatic->GetDC();
//得到在static上绘字的大小,以便于决定static控件位置和大小
CSize font = this->pDCStatic->GetTextExtent("你");
//和客户区同宽,起点在客户区的1/4处,高位字高的3倍
this->rcAllText = this->rcOnLine;
this->rcAllText.top += this->rcOnLine.Height()/4;
this->rcAllText.bottom = this->rcAllText.top+(font.cy*3);
//挪动static控件
hStatic->MoveWindow( &this->rcAllText, TRUE );
}
}
//计时线程
UINT C****Dlg::PaintWindow(LPVOID lParam)
{
C****Dlg * vnol = (C****Dlg*)lParam;
while(vnol->bOnLine)
{
WaitForSingleObject(vnol->hUpdateEvent,INFINITE);//等待事件被置位,此线程暂停于此
::SendMessage(vnol->m_hWnd,WM_PAINT,0,0);//给窗口发送刷新消息
Sleep(100);//时间间隔为100ms
}
return 1;
}
//刷新函数
void C****Dlg::OnPaint()
{
CPaintDC dc(this); // device context for painting
// TODO: Add your message handler code here
// Do not call CDialog::OnPaint() for painting messages
//得到当前时长
CString strStaticText;
CString strTimeSpace;
//自定义的函数,用来得到时长的CString形态
//dwStartTime在需要开始计时的地方获得
this->GetTimeSpace(this->dwStartTime,strTimeSpace);
strStaticText = "通话时长:" + strTimeSpace;
CBrush brush(RGB(255,255,255));
//将控件区绘制白色背景
this->pDCStatic->FillRect(CRect(0,0,this->rcAllText.Width(),this->rcAllText.Height()),&brush);
//在控件上通过DrawText将字显示到屏幕上
this->pDCStatic->DrawText(szText,CRect(0,0,this->rcAllText.Width(),this->rcAllText.Height()),DT_CENTER|DT_VCENTER|DT_SINGLELINE);
}
//这三种方法共用函数,用来得到时差的CString形态
void C****Dlg::GetTimeSpace(DWORD dwStartTime,CString & szTime)
{
DWORD dwNowTime=0;
DWORD dwSpace=0;
char time[10]={0,};
//获取当前时间
dwNowTime = GetTickCount();
//开始计算时差
dwSpace = dwNowTime-dwStartTime;
dwSpace = dwSpace/1000;//转化为以秒为单位
int sec = dwSpace` ;
int min = ((dwSpace - sec)/60)`;
int hour = ((dwSpace - sec)/60)/60;
sprintf(time,"-:-:-",hour,min,sec);
szTime.Format("%s",(char*)time);
return;
}
方法二:setwindowText + 计时器
这个方法还好吧。思路就是:启动定时器,每隔一段时间就对控件setwindowText。
在这个方法里,我们用两个控件来进行,一个是staticText控件,一个是Edit控件。
setwindowText:设置控件的显示信息,是针对单个控件而言滴!
CWnd::SetWindowText
void SetWindowText( LPCTSTR lpszString );
Parameters
lpszString
Points to a CString object or null-terminated string to be used as the new title or control text.
与setwindowText相对应的是 GetwindowText。
GetwindowText:获取控件的当前内容(对于Edit控件,可以在它的EN_CHANGE处理中得到当前的显示)。
CWnd::GetWindowText
int GetWindowText( LPTSTR lpszStringBuf, int nMaxCount ) const;
void GetWindowText( CString& rString ) const;
代码如下:
//变量
CString m_staticText;//要显示在static控件上的字符串
CString m_EditText;//要显示在Edit控件上的字符串
BOOL m_bInitState;//是否处于初始化状态 (若要重载OnSize 则必须是在初始化结束后,否则报错)
DWORD m_dwStartTime;//起始时间 在需要开始计时的地方通过 GetTickCount()获得
void GetTimeSpace(DWORD dwStartTime,CString & szTime);//用来的到当前时间差字符串的函数
//窗口的初始化函数
BOOL C****Dlg::OnInitDialog()
{
CDialog::OnInitDialog();
// Set the icon for this dialog. The framework does this automatically
// when the application's main window is not a dialog
SetIcon(m_hIcon, TRUE); // Set big icon
SetIcon(m_hIcon, FALSE); // Set small icon
// TODO: Add extra initialization here
//设置定时器
SetTimer(1,500,NULL);
this->m_bInitState = FALSE;
return TRUE; // return TRUE unless you set the focus to a control
}
//计时器响应函数
void CUpdateDateDlg::OnTimer(UINT nIDEvent)
{
// TODO: Add your message handler code here and/or call default
switch(nIDEvent)
{
case 1:
//定时器时间到
{
//计算出时差str
CString strTemp;
this->GetTimeSpace(this->m_dwStartTime,strTemp);
this->m_staticText = "通话时长:"+strTemp;
//edit和static显示的是一样的
this->m_EditText = this->m_staticText;
//得到控件的句柄
CWnd* hStatic = GetDlgItem(IDC_MYSTATIC);
CWnd* hEdit = GetDlgItem(IDC_MYEDIT);
//把语句显示到两个控件上
hStatic->SetWindowText(this->m_staticText);
hEdit->SetWindowText(this->m_editText);
}
break;
default:
CDialog::OnTimer(nIDEvent);
}
}
//Edit控件显示发生改变时EN_CHANGE的响应函数
void C****Dlg::OnChangeMyedit()
{
CString szNowText;
CWnd* hEdit = GetDlgItem(IDC_MYEDIT);
hEdit->GetWindowText(szNowText);
}
方法三:UpdateData + 定时器
这个方法跟方法二思路是一样滴,不过区别在于UpdateData的用法啦~
UpdateData刷新的是整个对话框,而不是像SetWindowText那样是针对控件进行处理。
UpdateData是MFC的API,所以这个方法要基于MFC使用。
使用这个方法,首先要通过ClassWizard建立控件和变量之间的联系。当你修改了变量的值,而希望对话框控件更新显示,就应该在修改变量后调用 UpdateData(FALSE);如果你希望知道用户在对话框中到底输入了什么,就应该在访问变量前调用UpdateData(TRUE)。
// ClassWizard建立控件和变量之间的联系
//通过ClassWizard建立控件和变量之间联系的代码反映
.h
//{{AFX_DATA(CUpdateDateDlg)
enum { IDD = IDD_UPDATEDATE_DIALOG };
CString m_staticText;
CString m_editText;
//}}AFX_DATA
.cpp
void CUpdateDateDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CUpdateDateDlg)
DDX_Text(pDX, IDC_MYSTATIC, m_staticText);
DDX_Text(pDX, IDC_MYEDIT, m_editText);
//}}AFX_DATA_MAP
}
//变量
BOOL m_bInitState;//是否处于初始化状态 (若要重载OnSize 则必须是在初始化结束后,否则报错)
DWORD m_dwStartTime;//起始时间
void GetTimeSpace(DWORD dwStartTime,CString & szTime);//用来的到当前时间差字符串的函数
//窗口的初始化函数
BOOL C****Dlg::OnInitDialog()
{
CDialog::OnInitDialog();
// Set the icon for this dialog. The framework does this automatically
// when the application's main window is not a dialog
SetIcon(m_hIcon, TRUE); // Set big icon
SetIcon(m_hIcon, FALSE); // Set small icon
// TODO: Add extra initialization here
//设置定时器
SetTimer(1,500,NULL);
this->m_bInitState = FALSE;
return TRUE; // return TRUE unless you set the focus to a control
}
//计时器的响应函数
void CUpdateDateDlg::OnTimer(UINT nIDEvent)
{
// TODO: Add your message handler code here and/or call default
switch(nIDEvent)
{
case 1:
//定时器时间到
{
//计算出时差
this->GetTimeSpace(this->m_dwStartTime,this->m_editText);
this->m_staticText = this->m_editText;
this->UpdateData(false);
}
break;
default:
CDialog::OnTimer(nIDEvent);
}
}
//edit控件显示内容发生变化时EN_CHANGE的响应函数
void C****Dlg::OnChangeMyedit()
{
this->UpdateData(true);
}
UpdateData详解:
UpdateData(TRUE):
是将控件的状态传给其关联的变量,当然你要为控件关联上变量才行。
用于将屏幕上控件中的数据交换到变量中。
UpdateData(FALSE):
是将控件的关联变量的值传给控件并改变控件状态。
用于将数据在屏幕中对应控件中显示出来。
注意:
UpdateData刷新的是当前对话框。
使用UpdateData()函数时,当前界面上所有绑定了的变量(即通过MFC ClassWizard给控件添加了对应的变量)都会被UpdateData(TRUE)更新成对应控件中的内容;同样所有绑定了变量的控件中的内容也会 UpdateData(FALSE)更新成对应变量中的内容。
重要补充:
GetWindowText()是获取控件当前内容,是对单个控件而言;
UpdateData()是作用于整个CWnd的DDX数据交换机制之中的,是控件和数据的双向通道。