WINCE6下自绘EDIT.docx
《WINCE6下自绘EDIT.docx》由会员分享,可在线阅读,更多相关《WINCE6下自绘EDIT.docx(11页珍藏版)》请在冰豆网上搜索。
WINCE6下自绘EDIT
WINCE6.0中自画EDIT
SUNNY.MAN
本人在此郑重声明,写这个EDIT绝不是为了好看或是哗众取宠。
我的ARM主频是400M的,当我一次性放了超过30个EDIT时,显示这个对话框时,刷新时居然是一行行的刷出来的EDIT。
所以自己在对话框的界面上,自已画了这个EDIT,其实只是在对话框上完成固定资产区域的输入,顺便把画的过程中容易出现的一些问题也了下来,这就是本文的由来…
一、EDIT外观图
图中所示的红色区域内的就是EDIT控件,这个控件其实只是对话框上的一个区域。
下面我将粗略的讲一下我的开发过程。
首先把这个控件命名为CCEEditBox公共继承自CCECwnd.这个CCECwnd是我自定义的一个c++类,只为了以后控件的虚拟化,给所有的控件人为的制造一个父类。
它不是来自CWnd类,它只是一个c++类。
它的成员变量有如下几个:
BOOLm_bFocus;//是否获得焦点
intm_nControlType;//控件类型
CStringm_strCaption;//内容
CRectm_rect;//自己的RECT所在位置
CDialog*m_pParent;
BOOLm_bInitializationed;//是否内存DC已经初始化
COLORREFm_TextColor;
CFontm_TextFont;
BOOLm_bVisible;
二.控件的定义
classCCEEdit:
publicCCECWnd
2.1.成员变量
private:
CCEMemDCm_memDC;//内存DC,就是简单的封装了一下CDC和CBitmap的使用。
CRectm_ClientRect;
BOOLm_bSetCur;
BOOLm_bCareShow;
intm_nCharPosition;//开始画字符位置
intm_nStartTypeCharPosition;//输入字符的位置
intm_nMaxChar;
BOOLm_bOnlyNumber;
2.2.成员函数
2.2.1VidDrawBoder()
任何一个控件都必须完成边框的绘制,以便有一个区域。
我这个也不例外,当初始化完成后,将根据m_rect在父对话框的背景上画EDIT的边框。
CRectrct(0,0,m_rect.Width(),m_rect.Height());
m_memDC->FillSolidRect(rct,RGB(255,2555,255));
m_memDC->Draw3dRect(rct,RGB(0,0,255),RGB(0,0,255));//输入字符的位置
2.2.2voidDrawNewCaption()
在边框内写文字,暂时设定文字为白底蓝字,每次有新的输入、删除、字符显示的开始位置变化,都要重新输出文字内容。
intnCount=0;
for(inti=1;i<=m_strCaption.GetLength()-m_nCharPosition;i++)
{
CSizesize=m_memDC->GetTextExtent
(m_strCaption.Mid(m_nCharPosition,i));
intnRes=m_rect.left+size.cx;
if(nRes>m_rect.right)
{
break;
}
else
nCount++;
}
m_memDC->DrawText(m_strCaption.Mid(m_nCharPosition,nCount),
m_ClientRect,DT_LEFT|DT_VCENTER|DT_SINGLELINE);
采用FOR主要是不能让写入的文字超出绘定的矩形框,因为场景中的文字不是等比例的字体,所以“l”和“国”是不可能等宽的。
所以只能一个个的字符判断,当然可以按一个中间字来估算,然后前移或后移,会提高效率,但我没有做这个优化。
如果不进行这个算法,就会显示半个字符,可你会发现MS的EDIT没有这种情况。
2.2.3需要处理的消息
1.来自父窗口的On_Paint消息
我在对话框中放了一个链表,用来记录所有加入的控件,在背景被刷新时,当循环发送给所有的控件,并带着无效区域,各控件按区域是否包含自己来判断自己是否需要重画。
如下代码在控件的OnPaint里
if(rect!
=NULL)
{
CRectrr;
if(rr.IntersectRect(&m_rect,rect)==FALSE)
{
return;
}
}
pDes->BitBlt(m_rect.left,m_rect.top,m_rect.Width(),m_rect.Height(),m_memDC,0,0,SRCCOPY);
if(m_bFocus)//如果焦点发生变化,则应该对光标进行处理。
{
if(m_bCareShow==FALSE)
{
m_bCareShow=TRUE;
if(m_pParent)
{
m_pParent->SetEditCare(this);
}
}
}
else
{
if(m_bCareShow)
{
m_bCareShow=FALSE;
if(m_pParent)
{
m_pParent->RemoveEditCare(this);
}
}
}
2.BOOLOnLButtonDown(UINTnFlags,CPointpoint)
当有左键按下时,此时应该把光标闪烁的位置,进行移动,同时如果获得焦点,应该告知父对话框,刚才有焦点那个控件,应该失去。
if(nFlags==1)
{
if(m_rect.PtInRect(point))
{
if(m_bFocus==FALSE)//显示光标并处理位置
{
m_bFocus=TRUE;
if(m_pParent)
{
m_pParent->UnFocus(this);
intnLeft=CalculatePosition(point);
:
:
SetCaretPos(nLeft,m_rect.top+(m_rect.Height()-14)/2);
:
:
ShowCaret(m_pParent->m_hWnd);
m_bCareShow=TRUE;
}
}
else//处理光标位置
{
if(m_bCareShow)
{
intnLeft=CalculatePosition(point);
:
:
SetCaretPos(nLeft,m_rect.top+(m_rect.Height()-14)/2);
}
}
returnTRUE;
}/**/
returnFALSE;
}
3.CalculatePosition(CPoint)
这个函数用来计算,当前鼠标在第几个字符后面,应该在哪里闪烁。
intnRes=0;
intOldRes=0;
m_nStartTypeCharPosition=m_nCharPosition;//一个为字符的输入位置,一个是显示开始位置
for(inti=0;i<=m_strCaption.GetLength()-m_nCharPosition;i++)
{
CSizesize=m_memDC->GetTextExtent(m_strCaption.Mid(m_nCharPosition,i));
m_nStartTypeCharPosition=m_nCharPosition+i;//因为显示的不一定是第一个字符
nRes=m_rect.left+size.cx;
if(nRes>pt.x||nRes>m_rect.right)//如果这个字符在鼠标后面,或是到了边框最右边
{
if(nRes-(nRes-OldRes)/2>pt.x)//是否超过了1/2字符如果超过,就向后,如果没有向前
{
nRes=OldRes;
m_nStartTypeCharPosition-=1;
if(m_nStartTypeCharPosition<0)
m_nStartTypeCharPosition=0;
}
returnnRes;
}
OldRes=nRes;
}
m_CaretCxOld=nRes;
returnnRes;
4.BOOLOnLButtonUp(UINTnFlags,CPointpoint)
主要是发出控件被单击的消息,以便产生单击事件,同时进行一些状态的更新,本控件没有状态的任何改变。
if(m_rect.PtInRect(point))
{
if(m_pParent)
{
m_pParent->InvalidateRect(m_rect);
OnControlClick();
returnTRUE;
}
}
returnFALSE;
5.BOOLOnMouseMove(UINTnFlags,CPointpoint)
这个移动,应该分两种情况,一种是按着鼠标左键的时候,一种是没有。
按着左键应该处理选择的长度问题,因为我没有选择区故这种情况没有处理。
我只是做了鼠标指针的变化,也就是把箭头形状变为输入光标。
if(m_rect.PtInRect(point))
{
if(m_bSetCur==FALSE)
{
SetCursor(AfxGetApp()->LoadStandardCursor(IDC_IBEAM));
m_bSetCur=TRUE;
}
returnTRUE;
}
else
{
if(m_bSetCur)
{
SetCursor(AfxGetApp()->LoadStandardCursor(IDC_ARROW));
m_bSetCur=FALSE;
}
}
returnFALSE;
6.字符的输入
EDIT最重要的就要是输入字符,所以处理onChar就是必不可少的,输入时要从输入光标处开始输入,同时又要判断输入的字符,因为OnChar这个消息来自对话框,所以一定要在有焦点时才处理输入,这个很重要。
BOOLOnChar(UINTnChar,UINTnRepCnt,UINTnFlags)
{
if(m_bFocus)
{
BOOLbAllowType=FALSE;
if(m_bOnlyNumber)//只允许输入数字时,需要判断是否是0-9和小数点
{
if(nChar>=48&&nChar<=57)//48="0"57="9"
{
bAllowType=TRUE;
}
elseif(nChar==46)//46"."
{
if(m_strCaption.Find(nChar)==-1)
{
bAllowType=TRUE;
}
}
}
else
{
if(nChar!
=8)//如果是退格,不应该进行输入处理
bAllowType=TRUE;
}
if(bAllowType&&m_strCaption.GetLength(){
CStringstr;
str.Format(L"%s%c%s",m_strCaption.Mid(0,m_nStartTypeCharPosition),
nChar,m_strCaption.Mid(m_nStartTypeCharPosition));
m_strCaption=str;
m_nStartTypeCharPosition++;
CSizesize=m_memDC->GetTextExtent(m_strCaption.Mid(m_nCharPosition,
m_nStartTypeCharPosition-m_nCharPosition));
if(m_rect.right<(m_rect.left+size.cx))//确保输入光标不超过最右边
{
intnCount=m_strCaption.GetLength()-m_nStartTypeCharPosition;
if(nCount==0)
nCount=1;
m_nCharPosition+=nCount;
size=m_memDC->GetTextExtent(m_strCaption.Mid(m_nCharPosition
m_nStartTypeCharPosition-m_nCharPosition));
}
:
:
SetCaretPos(m_rect.left+1+size.cx,m_rect.top+(m_rect.Height()-14)/2);
DrawNewCaption();
m_pParent->InvalidateRect(m_rect);
}
if(nChar==8)//8<-
{
CStringstr;
str.Format(L"%s%s",m_strCaption.Mid(0,m_nStartTypeCharPosition-1)
m_strCaption.Mid(m_nStartTypeCharPosition));
m_strCaption=str;
m_nStartTypeCharPosition--;
CSizesize=m_memDC->GetTextExtent(m_strCaption.Mid(m_nCharPosition));
//如果字符长度原先大于矩形
if(m_nCharPosition>0&&(m_rect.left+size.cx){
m_nCharPosition--;
}
size=m_memDC->GetTextExtent(m_strCaption.Mid(m_nCharPosition,
m_nStartTypeCharPosition-m_nCharPosition));
if(m_nStartTypeCharPosition<0)
m_nStartTypeCharPosition=0;
:
:
SetCaretPos(m_rect.left+1+size.cx,m_rect.top+(m_rect.Height()-14)/2);
DrawNewCaption();
m_pParent->InvalidateRect(m_rect);
}
}
returnTRUE;
}
7.BOOLOnKeyDown(UINTnChar,UINTnRepCnt,UINTnFlags)
KeyDown主要是处理左右键,以便移动输入的光标,并且左右显示编辑框中的内容。
if(nChar==37)//向左的键头
{
if(m_nStartTypeCharPosition>0)
m_nStartTypeCharPosition--;
if(m_nCharPosition>0&&m_nCharPosition>=m_nStartTypeCharPosition)
{
m_nCharPosition--;
}
CSizesize=m_memDC->GetTextExtent(m_strCaption.Mid(m_nCharPosition
m_nStartTypeCharPosition-m_nCharPosition));
:
:
SetCaretPos(m_rect.left+1+size.cx,m_rect.top+(m_rect.Height()-14)/2);
DrawNewCaption();
m_pParent->InvalidateRect(m_rect);
}
elseif(nChar==39)
{
if(m_nStartTypeCharPositionm_nStartTypeCharPosition++;
CSizesize=m_memDC->GetTextExtent(m_strCaption.Mid(m_nCharPosition,
m_nStartTypeCharPosition-m_nCharPosition));
if(m_rect.right<(m_rect.left+size.cx))
{
m_nCharPosition++;
size=m_memDC->GetTextExtent(m_strCaption.Mid(m_nCharPosition,
m_nStartTypeCharPosition-m_nCharPosition));
}
:
:
SetCaretPos(m_rect.left+1+size.cx,m_rect.top+(m_rect.Height()-14)/2);
DrawNewCaption();
m_pParent->InvalidateRect(m_rect);
}
returnTRUE;
8.创建控件并初始化DC
voidintEditAndDC(CRectrect,CDC*pParentDC,CCEDialog*pParent,
CStringstrCaption)这个主要用来创建控件,相当于CEdit的Create,但也稍有区别,这个的调用时需要父窗口的内存DC已经创建。
当然窗口什么时候创建,或是用DISPLSYDC也可以,但我建议在对话框初始化完成时,因为这更符合MFC的习惯。
m_strCaption=strCaption;//按纽名称
m_rect=rect;//自己的RECT所在位置
m_pParent=pParent;
if(m_bInitializationed==FALSE)
{
m_bInitializationed=TRUE;
m_memDC.CreateDC(pParentDC,m_rect);
CRectrct(0,0,m_rect.Width(),m_rect.Height());
m_memDC->SetBkMode(TRANSPARENT);
m_memDC->SelectObject(&m_TextFont);
m_memDC->SetTextColor(m_TextColor);
m_ClientRect=rct;
DrawNewCaption();
m_pParent->AddControl((CCECWnd*)this);
}
as_mhy@