6.CControlUI的大小及位置

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 消息时进行重绘。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值