MFC的Button和Static控件

最近要写一个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); 
    }//www.sctarena.com 
    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就会发送到控件而不是对话框上。

 

http://www.sctarena.com/Article/Article.asp?nid=3985

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值