ChartCtrl源码剖析之——CChartLegend类

CChartLegend类用来绘制每一个波形的描述信息,它处于该控件的区域,如下图所示: 

CChartLegend类的头文件。

#if !defined(AFX_CHARTLEGEND_H__CD72E5A0_8F52_472A_A611_C588F642080B__INCLUDED_)
#define AFX_CHARTLEGEND_H__CD72E5A0_8F52_472A_A611_C588F642080B__INCLUDED_
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
#include "ChartObject.h"
#include "ChartCtrl.h"
#include "ChartString.h"
class CChartSerie;
class CChartLegend : public CChartObject  
{
    friend CChartCtrl;
public:
    void SetFont(int iPointSize, const TChartString& strFaceName);
    CChartLegend(CChartCtrl* pParent);
    virtual ~CChartLegend();
    enum DockSide
    {
        dsDockRight,
        dsDockLeft,
        dsDockTop,
        dsDockBottom
    };
    void DockLegend(DockSide dsSide);
    void UndockLegend(int iLeftPos, int iTopPos);
    void SetTransparent(bool bTransparent);  
    void SetHorizontalMode(bool bHorizontal);
private:
    void Draw(CDC* pDC);
    void ClipArea(CRect& rcControl, CDC* pDC);
    void UpdatePosition(CDC* pDC, const CRect& rcControl);
    TChartString m_strFontName;
    int          m_iFontSize;
    bool m_bDocked;    // true if the legend is docked
    DockSide m_DockSide;
    // If the legend is not docked:
    int m_iLeftPos;
    int m_iTopPos;
    bool m_bIsTransparent;
    bool m_bIsHorizontal;
    CSize m_BitmapSize;
};
#endif // !defined(AFX_CHARTLEGEND_H__CD72E5A0_8F52_472A_A611_C588F642080B__INCLUDED_)

CChartLegend类的源文件。

#include "stdafx.h"
#include "ChartLegend.h"
#include "ChartSerie.h"
#include "ChartCtrl.h"
#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
CChartLegend::CChartLegend(CChartCtrl* pParent):CChartObject(pParent)
{
    m_ObjectColor = RGB(255,255,255);
    m_iFontSize = 100;
    m_strFontName = _T("Times New Roman");
    m_bIsVisible = false;
    m_bDocked = true;
    m_DockSide = dsDockRight;
    m_iLeftPos = m_iTopPos = 0;
    m_bIsTransparent = false;
    m_bIsHorizontal = false;
    m_bShadow = true;
    m_iShadowDepth = 3;
    m_BitmapSize.cx = 16;
    m_BitmapSize.cy = 16;
}
CChartLegend::~CChartLegend()
{
}
void CChartLegend::SetFont(int iPointSize, const TChartString& strFaceName)
{
    m_iFontSize = iPointSize;
    m_strFontName = strFaceName;
    m_pParent->RefreshCtrl();
}
void CChartLegend::SetTransparent(bool bTransparent)
{
    m_bIsTransparent = bTransparent;
    m_pParent->RefreshCtrl();
}
void CChartLegend::SetHorizontalMode(bool bHorizontal)
{
    m_bIsHorizontal = bHorizontal;
    m_pParent->RefreshCtrl();
}
void CChartLegend::DockLegend(DockSide dsSide)
{
    m_bDocked = true;
    m_DockSide = dsSide;
    m_pParent->RefreshCtrl();
}
void CChartLegend::UndockLegend(int iLeftPos, int iTopPos)
{
    m_bDocked = false;
    m_iLeftPos = iLeftPos;
    m_iTopPos = iTopPos;
    m_pParent->RefreshCtrl();
}
void CChartLegend::ClipArea(CRect& rcControl, CDC* pDC)
{
    UpdatePosition(pDC,rcControl);
    if (m_ObjectRect.IsRectEmpty())
        return;
    if (m_bDocked)
    {
        switch (m_DockSide)
        {
        case dsDockRight:
            rcControl.right = m_ObjectRect.left + 2;
            break;
        case dsDockLeft:
            rcControl.left = m_ObjectRect.right - 2;
            break;
        case dsDockTop:
            rcControl.top = m_ObjectRect.bottom + 2;
            break;
        case dsDockBottom:
            rcControl.bottom = m_ObjectRect.top - 2;
            break;
        }
    }
}
void CChartLegend::UpdatePosition(CDC* pDC, const CRect& rcControl)
{
    CRect NewPosition;
    NewPosition.SetRectEmpty();
    if (!m_bIsVisible)
    {
        SetRect(NewPosition);
        return;
    }
    CFont* pOldFont;
    CFont NewFont;
    NewFont.CreatePointFont(m_iFontSize,m_strFontName.c_str(),pDC);
    pOldFont = pDC->SelectObject(&NewFont);
    int Height = 0;        
    int Width = 0;        
    int MaxText = 0;
    CSize TextSize;
    size_t SeriesCount = m_pParent->GetSeriesCount();
    int Drawn = 0;
    for (size_t i=0;i<SeriesCount;i++)
    {
        CChartSerie* pSerie = m_pParent->GetSerie(i);
        if ( (pSerie->GetName() == _T("")) || !pSerie->IsVisible() )
            continue;
        Drawn++;
        TextSize = pDC->GetTextExtent(pSerie->GetName().c_str());
        if (!m_bIsHorizontal)
        {
            if (TextSize.cy>m_BitmapSize.cy)
                Height += TextSize.cy + 2;
            else
                Height += m_BitmapSize.cy + 2;
            if (TextSize.cx > MaxText)
                MaxText = TextSize.cx;
        }
        else
        {
            Width += TextSize.cx + 4 + m_BitmapSize.cx + 10;
            if (TextSize.cy > MaxText)
                MaxText = TextSize.cy;
        }
    }
    pDC->SelectObject(pOldFont);
    DeleteObject(NewFont);
    if (!Drawn)
    {
        SetRect(NewPosition);
        return;
    }
    if (!m_bIsHorizontal)
    {
        Width += MaxText + m_BitmapSize.cx + 12;
        Height += 4 + 4 - 2;    // Top and bottom margins. -2 because space counted once too much
    }
    else
    {
        Width += 2 + 2 - 10;
        Height = 4 + max(m_BitmapSize.cy,MaxText) + 4;
    }
    
    if (!m_bDocked)
    {
        NewPosition.top = m_iTopPos;
        NewPosition.left = m_iLeftPos;
        NewPosition.bottom = m_iTopPos + Height + 2;
        NewPosition.right = m_iLeftPos + Width;
    }
    else
    {
        switch (m_DockSide)
        {
        case dsDockRight:
            NewPosition.top = ((rcControl.bottom-rcControl.top)/2) - ((Height + 2)/2);
            NewPosition.left = rcControl.right - (Width + 6);
            NewPosition.bottom = NewPosition.top + Height;
            NewPosition.right = NewPosition.left + Width;
            break;
        case dsDockLeft:
            NewPosition.top = ((rcControl.bottom-rcControl.top)/2) - ((Height + 2)/2);
            NewPosition.left = rcControl.left + 3;
            NewPosition.bottom = NewPosition.top + Height;
            NewPosition.right = NewPosition.left + Width;
            break;
        case dsDockTop:
            NewPosition.top = rcControl.top + 3;  //((rcControl.bottom-rcControl.top)/2) - ((Height + 2)/2);
            NewPosition.left = ((rcControl.right-rcControl.left)/2) - (Width/2);  // rcControl.left + 3;
            NewPosition.bottom = NewPosition.top + Height;
            NewPosition.right = NewPosition.left + Width;
            break;
        case dsDockBottom:
            NewPosition.top = rcControl.bottom - (Height + 2);  //((rcControl.bottom-rcControl.top)/2) - ((Height + 2)/2);
            NewPosition.left = ((rcControl.right-rcControl.left)/2) - (Width/2);  // rcControl.left + 3;
            NewPosition.bottom = NewPosition.top + Height;
            NewPosition.right = NewPosition.left + Width;
            break;
        }
    }
    SetRect(NewPosition);
}
void CChartLegend::Draw(CDC *pDC)
{
    if (!pDC->GetSafeHdc())
        return;
    if (!m_bIsVisible)
        return;
    if (m_ObjectRect.IsRectEmpty())
        return;
    CPen SolidPen(PS_SOLID,0,RGB(0,0,0));
    CPen* pOldPen;
    CFont* pOldFont;
    CFont NewFont;
    NewFont.CreatePointFont(m_iFontSize,m_strFontName.c_str(),pDC);
    // Draw the shadow
    if (m_bShadow)
    {
        CRect ShadowRect = m_ObjectRect;
        ShadowRect.OffsetRect(m_iShadowDepth,m_iShadowDepth);
        CBrush BrushShadow;
        BrushShadow.CreateSolidBrush(m_ShadowColor) ;
        pDC->FillRect(ShadowRect,&BrushShadow);
    }
    if (!m_bIsTransparent)
    {
        //Fill back color
        CBrush BrushBack;
        BrushBack.CreateSolidBrush(m_ObjectColor) ;
        pDC->FillRect(m_ObjectRect,&BrushBack);
    }
    pOldFont = pDC->SelectObject(&NewFont);
    pOldPen = pDC->SelectObject(&SolidPen);
    //Draw rectangle:
    pDC->MoveTo(m_ObjectRect.left,m_ObjectRect.top);
    pDC->LineTo(m_ObjectRect.right,m_ObjectRect.top);
    pDC->LineTo(m_ObjectRect.right,m_ObjectRect.bottom);
    pDC->LineTo(m_ObjectRect.left,m_ObjectRect.bottom);
    pDC->LineTo(m_ObjectRect.left,m_ObjectRect.top);
    int iPrevMode = pDC->SetBkMode(TRANSPARENT);
    CRect rectBitmap(m_ObjectRect.left+2,m_ObjectRect.top+5,
                     m_ObjectRect.left+2+m_BitmapSize.cx,
                     m_ObjectRect.top+6+m_BitmapSize.cy);
    int SeriesCount = m_pParent->GetSeriesCount();
    for (int i=0;i<SeriesCount;i++)
    {
        CChartSerie* pSerie = m_pParent->GetSerie(i);
        if ( (pSerie->GetName() == _T("")) || !pSerie->IsVisible() )
            continue;
        int MaxHeight = 0;
        CSize TextSize = pDC->GetTextExtent(pSerie->GetName().c_str());
        if (TextSize.cy > m_BitmapSize.cy)
        {
            pDC->ExtTextOut(rectBitmap.right+4,rectBitmap.top,ETO_CLIPPED,NULL,pSerie->GetName().c_str(),NULL);
            CRect rectTemp(rectBitmap);
            int YOffset = TextSize.cy/2 - rectBitmap.Height()/2;
            rectTemp.OffsetRect(0,YOffset);
            pSerie->DrawLegend(pDC,rectTemp);
            MaxHeight = TextSize.cy;
        }
        else
        {
            int YOffset = rectBitmap.CenterPoint().y - TextSize.cy/2;
            pDC->ExtTextOut(rectBitmap.right+4,YOffset,ETO_CLIPPED,NULL,pSerie->GetName().c_str(),NULL);
            MaxHeight = m_BitmapSize.cy;
            pSerie->DrawLegend(pDC,rectBitmap);
        }
        
        if (!m_bIsHorizontal)
            rectBitmap.OffsetRect(0,MaxHeight+2);
        else
            rectBitmap.OffsetRect(m_BitmapSize.cx+4+TextSize.cx+10,0);
    }
    pDC->SetBkMode(iPrevMode);
    pDC->SelectObject(pOldFont);
    DeleteObject(NewFont);
    pDC->SelectObject(pOldPen);
    DeleteObject(SolidPen);
}

ClipArea函数的作用是将ChartLegend与波形绘制区域分离开,其中rcControl表示的是绘制波形的区域,m_ObjectRect表示的是绘制ChartLegend的区域。UpdatePosition函数用来在当前绘制区域里面计算ChartLegend所处的位置并记录下该位置。Draw函数用来绘制ChartLegend控件,调用CChartSeries类的DrawLegend用来绘制ChartLegend里面的波形图像信息。 

posted @ 2017-07-19 17:00  不安分的萝卜  阅读(2947)  评论(0编辑  收藏  举报