6.CControlUI的大小及位置

来源:互联网 发布:新浪短链接数据分析 编辑:程序博客网 时间:2024/05/16 00:53

1.简介

CControlUI中有三个重要的参数,分别为:

- RECT m_rcItem;   //控件实际大小及位置 - SIZE m_cxyFixed; //控件预设大小- SIZE m_cXY;      //控件预设位置

2.非float控件的讨论

控件的浮动与非浮动由参数 m_bFloat 标识,此处讨论 m_bFloat = false 的情形。

2.1 m_rcItem

//获取控件大小及位置const RECT& CControlUI::GetPos() const{    return m_rcItem;}//设置控件大小及位置:1.由父控件设置void CControlUI::SetPos(RECT rc){    //设置控件大小位置      m_rcItem = rc;    //控件大小位置改变消息处理    if( !m_bSetPos )     {        m_bSetPos = true;        if( OnSize ) OnSize(this);        m_bSetPos = false;    }    //计算需要重新刷新的区域,并进行刷新    m_pManager->Invalidate(invalidateRc);}

2.2 m_cxyFixed

设置 m_cxyFixed.cy 与下面设置 m_cxyFixed.cx 方式相同,不再重复。

int CControlUI::GetFixedWidth() const{    return m_cxyFixed.cx;}//预设置控件大小:1.父控件设置  2.通过“pos”参数时,pos.right-pos.left   3.通过“width”参数void CControlUI::SetFixedWidth(int cx){    //设置大小    m_cxyFixed.cx = cx;    //更新父控件布局    NeedParentUpdate();}

重点:当容器在设置子控件布局时,会使用该参数估计子控件大小,从而为子控件分配实际大小及位置: SetPos()。

SIZE CControlUI::EstimateSize(SIZE szAvailable){    return m_cxyFixed;}

2.3 m_cXY

预设置控件的位置。

// 实际大小位置使用GetPos获取,这里得到的是预设的参考值(代码原注释)SIZE CControlUI::GetFixedXY() const{    return m_cXY;}//void CControlUI::SetFixedXY(SIZE szXY){    m_cXY.cx = szXY.cx;    m_cXY.cy = szXY.cy;    NeedParentUpdate();}

3.float控件的讨论

浮动控件与非浮动控件的主要区别在于,当控件的预设大小改变时,是否重新刷新父控件中子控件的布局。举例如下:

void CControlUI::SetFixedXY(SIZE szXY){    m_cXY.cx = szXY.cx;    m_cXY.cy = szXY.cy;    if( !m_bFloat )         NeedParentUpdate();    else         NeedUpdate();}

另外一个区别在 SetPos() 函数中:

void CControlUI::SetPos(RECT rc){    //设置实际位置及大小    ......    //更新预设控件大小及位置    if( m_bFloat )     {        CControlUI* pParent = GetParent();        if( pParent != NULL )         {            RECT rcParentPos = pParent->GetPos();            if( m_cXY.cx >= 0 )                 m_cXY.cx = m_rcItem.left - rcParentPos.left;            else                 m_cXY.cx = m_rcItem.right - rcParentPos.right;            if( m_cXY.cy >= 0 )                 m_cXY.cy = m_rcItem.top - rcParentPos.top;            else                 m_cXY.cy = m_rcItem.bottom - rcParentPos.bottom;            m_cxyFixed.cx = m_rcItem.right - m_rcItem.left;            m_cxyFixed.cy = m_rcItem.bottom - m_rcItem.top;        }    }    //刷新重绘无效区域    ......}

注:其实 SetPos() 不仅会调整自身的位置和大小,如果其包含其他控件,则它会调整子控件的位置和大小。


4.结束

m_rcItem 为控件的实际大小,实际所占用的位置。

m_cxyFixed 为如果控件在某个容器中时,希望父容器能够为其分配的大小,但是仅仅是希望。一般情况下父容器也是很通情达理的。

父容器会调用子控件的 EstimateSize() 函数来询问子控件所希望设置的大小即 m_cxyFixed。

SetPos() 函数同样重要,不仅设置自己的位置,也会调整子控件的位置(前提它是容器且有子控件)。

5.附

(1) NeedUpdate() 函数

在各种调整预设大小的函数中,我们看到各种调用 NeedUpdate() 函数,在此简介一下:

void CControlUI::NeedUpdate(){    if( !IsVisible() )         return;    m_bUpdateNeeded = true;    Invalidate();    //通过设置m_pManager中的 m_bUpdateNeeded 设置控件大小更新    if( m_pManager != NULL )         m_pManager->NeedUpdate();}

m_bUpdateNeeded 主要用在处理 CPaintManagerUI::MessageHandler() 中的 WM_PAINT 消息时,判断是否需要重设控件位置及大小:

case WM_PAINT:{    //判断是否需要重绘    if( m_bUpdateNeeded )     {        m_bUpdateNeeded = false;        RECT rcClient = { 0 };        ::GetClientRect(m_hWndPaint, &rcClient);        if( !::IsRectEmpty(&rcClient) )         {            if( m_pRoot->IsUpdateNeeded() ) //是否需要更新            {                m_pRoot->SetPos(rcClient);            }            else             {                //循环处理需要更新的控件                CControlUI* pControl = NULL;                while( pControl = m_pRoot->FindControl(__FindControlFromUpdate, NULL, UIFIND_VISIBLE | UIFIND_ME_FIRST) )                 {                    pControl->SetPos( pControl->GetPos() );                }            }            //第一次绘制的话,发送 DUI_MSGTYPE_WINDOWINIT 消息        }    }    //重绘......    // 如果仍需要调整大小,再调用 InvalidateRect() 更新刷新区域    // 在下一次处理 WM_PAINT 消息时进行重绘    if( m_bUpdateNeeded )     {        ::InvalidateRect(m_hWndPaint, NULL, FALSE);    }}    return true;

当然 NeedParentUpdate() 也一样,效果和 NeedUpdate() 基本相同,只是把父控件也设置成了需要更新。

void CControlUI::NeedParentUpdate(){    if( GetParent() )     {        GetParent()->NeedUpdate();        GetParent()->Invalidate();    }    else     {        NeedUpdate();    }    if( m_pManager != NULL )         m_pManager->NeedUpdate();}

(2) Invalidate() 函数

在 SetPos() 的最后一句,调用了 m_pManager->Invalidate(invalidateRc); Invalidate() 函数的效果是更新刷新区域,在下一次处理 WM_PAINT 消息时进行重绘。

下面看下控件的 Invalidate() 函数的实现:

void CControlUI::Invalidate(){    if( !IsVisible() ) return; //不可见就没有必要了    RECT invalidateRc = m_rcItem;    CControlUI* pParent = this;    RECT rcTemp;    RECT rcParent;    //循环搜索控件的交集,即需要重绘的区域    while( pParent = pParent->GetParent() )    {        rcTemp = invalidateRc;        rcParent = pParent->GetPos();        if( !::IntersectRect(&invalidateRc, &rcTemp, &rcParent) )         {            return;        }    }    //调用 m_pManager 的 Invalidate() 函数实现系统重绘    if( m_pManager != NULL )         m_pManager->Invalidate(invalidateRc);}

看看 m_pManager 的 Invalidate() 函数:

void CPaintManagerUI::Invalidate(RECT& rcItem){    ::InvalidateRect(m_hWndPaint, &rcItem, FALSE);}

::InvalidateRect() 系统函数会将 rcItem 区域添加无效区域中,在下次处理 WM_PAINT 消息时进行重绘。

0 0
原创粉丝点击