MFC的Button和Static控件
原创jsl_mes 博主文章分类:Windows程序设计 ©著作权
©著作权归作者所有:来自51CTO博客作者jsl_mes的原创作品,请联系作者获取转载授权,否则将追究法律责任
最近要写一个MFC的对话框程序,发现要把MFC的对话框写的有色彩点并不容易,不像在C#里设置属性指就好,而是要自己去写一些代码完成对话框的绘画操作。比如一个简单的鼠标移入、移出操作,都要自己去写代码。由于我只用到了Button和Static两种控件,一切看上去还是比较顺利,所以谈谈自己的经验。
1、对话框的背景
MFC中没有属性能够设定对话框的背景颜色或是图片,需要我们在程序中进行操作。首先,需要实现WM_CTLCOLOR的消息操作,通过这个消息我们能够控制对话框以及Static控件(包括Group)的背景色、前景色。该消息的处理函数原型如下:
HBRUSH OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor);
其中,通过pDC能够文件的颜色、背景模式,通过pWnd可以获取正在绘制的控件ID,通过nCtlColor可以判断当前正在绘制的控件类型。这里我需要控制对话框的背景,所以要进行如下操作:
HBRUSH hbr = CDialogEx::OnCtlColor(pDC, pWnd, nCtlColor);
if (nCtlColor == CTLCOLOR_DLG)
{
return m_backgroundBrush; //返回对话框背景的画刷
}
else if (nCtlColor == CTLCOLOR_STATIC)
{
pDC->SetBkMode(TRANSPARENT); //所有Static控件的背景色为透明
if (pWnd->GetDlgCtrlID() == IDC_NOTE)
{
pDC->SetTextColor(RGB(255, 255, 255)); //针对特殊的static控件,设置单独的文字颜色 }
}
return hbr; //不是要自绘的控件,返回默认值
2、按钮(Button)控件
一开始很奇怪,在WM_CTLCOLOR的消息处理函数进行如下操作竟然没有用:
if (pWnd->GetDlgCtrlID() == IDB_TEST) //按钮文字颜色
{
pDC->SetTextColor(RGB(0, 0, 255)); }
原来按钮控件的颜色、背景等属性无法通过WM_CTLCOLOR消息实现,要改变这些属性,就必须要自己从CButton类继承一个类,然后改写其绘画函数——DrawItem。通过DrawItem函数的参数可以获取控件的大小、状态、类型、绘画DC等信息,有了它们,重绘Button就简单了。下面给出简单实例:
void CMyButton::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
{
if (lpDrawItemStruct->CtlType != ODT_BUTTON) //由于继承自CButton,这一句肯定成立
{
return;
}
CRect rect = lpDrawItemStruct->rcItem;
CDC *pDC = CDC::FromHandle(lpDrawItemStruct->hDC);
UINT state = lpDrawItemStruct->itemState;
TCHAR strText[MAX_PATH] = {0};
::GetWindowText(m_hWnd, strText, MAX_PATH);
//获取按钮的状态
if (state & ODS_FOCUS)
{
m_bSelected = TRUE;
}
else
{
m_bSelected = FALSE;
}
//根据按钮的状态填充按钮的底色
if (m_bOver)
{
pDC->FillRect(&rect, &m_overBrush);
}
else if (m_bSelected)
{
pDC->FillRect(&rect, &m_selectedBrush);
}
else
{
pDC->FillRect(&rect, &m_normalBrush);
}
// 画按钮标题,水平和垂直都居中。把DC当作一个画布
// 如果要使DT_VCENTER(垂直居中)有效,必须同时设置DT_SINGLELINE(单行)风格
if (_tcslen(strText) > 0)
{
pDC->SelectObject(m_font);
pDC->SetBkMode(TRANSPARENT);
pDC->SetTextColor(RGB(255, 255, 255));
pDC->DrawText(strText, &lpDrawItemStruct->rcItem, DT_SINGLELINE | DT_CENTER | DT_VCENTER);
}
}
但是写了以上代码,发现还是不管用,原来,还需要设置按钮的属性为自绘类型,即需要设置BS_OWNERDRAW属性。这个可以在对话框的OnInitDialog函数或Button类的PreSubClassWindow函数中,调用Button的SetButtonStyle方法设置,如下:
UINT uStyle = GetButtonStyle(); // 得到按钮的愿风格
SetButtonStyle(uStyle | BS_OWNERDRAW); // 加入自画风格
为了判断按钮当前的状态,如是否有鼠标停留、焦点等,就需要跟踪鼠标的移动,这个可以通过WM_MOUSEMOVE消息实现。但是鼠标移入、移出的操作无法通过Vc的Wizard实现,在Vs2010里可以通过Wizard添加,但是函数不会被调用。为了能够知道鼠标移入、移出操作,需要在WM_MOUSEMOVE消息中增加如下代码,已跟踪鼠标事件:
if (!m_bTrack) //记录是否已经设置跟踪事件
{
TRACKMOUSEEVENT ent = {0};
ent.dwFlags = TME_HOVER | TME_LEAVE;
ent.cbSize = sizeof(TRACKMOUSEEVENT);
ent.dwHoverTime = 10;
ent.hwndTrack = this->m_hWnd;
m_bTrack = TrackMouseEvent(&ent);
}
通过以上代码,我们就可以收到WM_MOUSEHOVER、WM_MOUSELEAVE事件,通过将鼠标停留时间ent.dwHoverTime(触发WM_MOUSEHOVER的时间)设置为足够短,我们就可以模拟出鼠标移入事件。为了能够处理这两个消息,需要通过MFCWizard增加WM_MOUSEHOVER、WM_MOUSELEAVE的事件处理函数。
void CMyButton::OnMouseHover(UINT nFlags, CPoint point)
{
m_bOver = TRUE;
InvalidateRect(NULL, TRUE);
CButton::OnMouseHover(nFlags, point);
}
void CMyButton::OnMouseLeave()
{
m_bTrack = FALSE;
m_bOver = FALSE;
InvalidateRect(NULL, FALSE);
CButton::OnMouseLeave();
}
3、Static控件
虽然可以通过对话框的WM_CTLCOLOR消息设置Static控件的字体颜色和背景模式,但是如果要更好地控制其字体、背景等属性,以及添加鼠标事件,就要自己写代码了。方法很简单,和Button类似,需要自己写一个继承自CStatic的类,然后改写DrawItem方法,实现自己的绘画方法。
只是有一点需要注意,默认情况下Static是不发送消息的,即鼠标点击、移动等事件是没有效果的,需要设置SS_NOTIFY属性,这可以在继承Static类的PreSubClassWindow中进行入操作设置:
ModifyStyle(SS_TYPEMASK, SS_OWNERDRAW|SS_NOTIFY);
以上代码同时设置自绘和消息通知属性。
4、无标题对话框的移动
在Windows的应用程序中,都可以通过点击标题栏拖拽窗口进行移动,但是如果窗口没有标题栏怎么办呢,即将对话框的Border属性设置为None,如何实现窗口移动呢?在我的对话框应用中,在窗口顶部放了一个Static控件,想把它作为窗口的标题栏,通过拖拽Static空间拖动窗口。有两种方法可以实习这个目的。
一个很容易想到的方法是写一个继承自CStatic类的自绘控件,然后关联该类的对象到充当标题栏的Static控件。通过处理Static控件的WM_MOUSEMOVE、WM_LBUTTONDOWN、WM_LBUTTONUP消息,就可以得知鼠标是否按下、移动、松开,然后在WM_MOUSEMOVE的处理函数计算鼠标移动的距离,然后获得父窗口的指针,通过MoveWindow函数移动父窗口。显然,这种方法比较复杂,需要进行的操作也比较繁琐。
既然点击标题栏能够移动窗口,而点击其他地方却效果不同,那么windows肯定通过什么标志来判断要采取什么操作,我们应该能够不需要自己去实现移动窗口,而是告诉Windows,点击Static这个“标题”时就是点击真正的标题栏。控件在响应点击操作之前,都会发送WM_NCHITTEST消息,用于判断鼠标点击的位置,而消息处理函数的返回值就指出了位置。WM_NCHITTEST的处理函数如下:
LRESULT CWinAppDlg::OnNcHitTest(CPoint point)
{
CRect rect;
GetDlgItem(IDC_TITLE)->GetClientRect(rect);
ClientToScreen(rect);
return rect.PtInRect(point) ? HTCAPTION : CDialogEx::OnNcHitTest(point);
}
在上面的代码中,首先获得Static控件IDC_TITLE的位置,然后判断鼠标点击位置是否位于控件内,如果是,则返回HTCAPTION,告诉Windows点击的是标题栏,否则返回默认值。显然这种移动窗口的做法要简单的多,而且它利用MFC的原理进行操作,更稳定也更具兼容性。这几句代码也有一个限制,即标题栏只能是Static控件,因为Static默认是不接收事件、消息,所以WM_NCHITTEST由对话框进行处理,如果是Button等其他控件,WM_NCHITTEST就会发送到控件而不是对话框上。
上一篇:MFC程序的theApp对象
下一篇:自行设计MFC向导对话框
提问和评论都可以,用心的回复会被更多人看到
评论
发布评论
相关文章
-
MFC static控件
MFC static控件
控件 c MFC static -
MFC双缓冲 防止闪屏
好久没有写mfc的东西了。今天帮别人看改个闪屏的问题。弄了挺久的。。
设备描述 位图 句柄 闪屏 -
android双缓冲截图 android双缓冲和三缓冲
一、双缓冲(Double Buffer) 双缓冲甚至是多缓冲,在许多情况下都很有用。一般需要使用双缓冲区的地方都是由于“生产者”和“消费者”供需不一致所造成的。这样的情况在很多地方可能会发生,使用多缓冲可以很好的解决。举几个常见的例子: 例 1. 在网络传输过程中数据的接收,
android双缓冲截图 SurfaceFlinger服务 双缓冲 Android 应用程序