同UCanCode一起释放 Visual C++, C# 的巨大能量!

E-Form++可视化组件库图形元件额外控制点的创建探讨?

 

作为全球领先的VC++ / MFC可视化图形源码组件库,E-Form++在国内国外数百家企业得到了广泛的使用,为那些想要创建 各种高性能图形可视化显示的开发人员提供了最佳的应用组件选择。使用和扩展E-Form++提供的图形元件是一个非常重要的工作,本文将探讨如何如何 为这些图形元件创建额外控制点:

 

一、什么是额外控制点?

默认情况下,E-Form++已经为画布上的各种图形元件的编辑操作提供了几种标准的控制点:

 

1、系统默认基本图形编辑控制点,通过这些额外控制点,可以对图形进行缩放,旋转,移动,顶点编辑等等操作。具体包括:

 

1)、10控制点设计,包括8个顶点编辑控制点,一个旋转控制点,一个中心移动控制点。

2)、直线与曲线顶点编辑控制点。

3)、线段中心切分控制点等。

 

2、在很多时候,如果需要调整图形的其他独立的几何参数,就需要其他额外控制点来进行。如下图所示:


通过一个额外空的黄色控制点,我们可以调整圆角矩形的圆角弧度。

有的时候,我们可能会需要两个或者更加多的额外控制点来调整图形的多个几何参数,如下图所示的元件:

通过额外控制点,我们可以分别调整元件的高度和宽度。

 

二、系统提供了哪些额外控制点:

     使用E-Form++来设计附带任何数量的复杂的2D几何图形元件都是可能的,系统缺省提供了6个额外控制点可以直接使用。这6个额外控制点在系统内部已经进行了恰当的处理,在具体的图形元件中,只需要通过简单的几个步骤来定义相应的规则即可创建出来。下面我们主要对如何使用这6个系统提供的额外控制点进行讨论:

  1. 这6个额外控制点的名称是什么?
    
    
    在CFODrawShape类中,系统预置了如下6个额外控制点:
    
    
    
    
    
    // Anchor point.
    
    
    CPoint m_ptAnchor;
    
    
    
    
    
    // Save anchor point.
    
    
    CPoint m_ptSaveAnchor;
    
    
    
    
    
    // Track anchor point.
    
    
    CPoint m_ptTrackAnchor;
    
    
    
    
    
    //////////////////////////////////////
    
    
    // Anchor extent anchor.
    
    
    //////////////////////////////////////
    
    
    // Anchor point.
    
    
    CPoint m_ptExtAnchor;
    
    
    
    
    
    // Save anchor point.
    
    
    CPoint m_ptSaveExtAnchor;
    
    
    
    
    
    // Track anchor point.
    
    
    CPoint m_ptTrackExtAnchor;
    
    
    
    
    
    //////////////////////////////////////
    
    
    // Third extent anchor.
    
    
    //////////////////////////////////////
    
    
    // Anchor point.
    
    
    CPoint m_ptThirdAnchor;
    
    
    
    
    
    // Save anchor point.
    
    
    CPoint m_ptSaveThirdAnchor;
    
    
    
    
    
    // Track anchor point.
    
    
    CPoint m_ptTrackThirdAnchor;
    
    
    
    
    
    //////////////////////////////////////
    
    
    // Four extent anchor.
    
    
    //////////////////////////////////////
    
    
    // Anchor point.
    
    
    CPoint m_ptFourAnchor;
    
    
    
    
    
    // Save anchor point.
    
    
    CPoint m_ptSaveFourAnchor;
    
    
    
    
    
    // Track anchor point.
    
    
    CPoint m_ptTrackFourAnchor;
    
    
    
    
    
    //////////////////////////////////////
    
    
    // Five extent anchor.
    
    
    //////////////////////////////////////
    
    
    // Anchor point.
    
    
    CPoint m_ptFiveAnchor;
    
    
    
    
    
    // Save anchor point.
    
    
    CPoint m_ptSaveFiveAnchor;
    
    
    
    
    
    // Track anchor point.
    
    
    CPoint m_ptTrackFiveAnchor;
    
    
    
    
    
    //////////////////////////////////////
    
    
    // Five extent anchor.
    
    
    //////////////////////////////////////
    
    
    // Anchor point.
    
    
    CPoint m_ptTextAnchor;
    
    
    
    
    
    // Save anchor point.
    
    
    CPoint m_ptSaveTextAnchor;
    
    
    
    
    
    // Track anchor point.
    
    
    CPoint m_ptTrackTextAnchor;
    
    
    
    
    
    每个额外控制点均由三个参数组成,例如:m_ptAnchor, m_ptSaveExtAnchor, m_ptTrackAnchor,其中m_ptSaveExtAnchor保存该额外控制点最原始的坐标值,这个坐标值是需要保存到文件中去的,其他两个值均可以通过该值和坐标转换矩阵CFOMatrix计算得到。m_ptAnchor 参数为过程中变化值,可用该值来进行绘制以及其他计算。m_ptTrackAnchor改值为拖拉过程中的过程值,该值只在拖拉的过程中有效。主要用于拖拉过程中的绘制。
  2. 如何选择需要的额外控制点?
     

    以上6个额外控制点的地位是一样的,您可以自由选择到底使用哪些额外参数值。例如需要2个额外控制点,就可以选择:
    // Anchor point.
    CPoint m_ptAnchor;

    // Save anchor point.
    CPoint m_ptSaveAnchor;

    // Track anchor point.
    CPoint m_ptTrackAnchor;

    //////////////////////////////////////
    // Anchor extent anchor.
    //////////////////////////////////////
    // Anchor point.
    CPoint m_ptExtAnchor;

    // Save anchor point.
    CPoint m_ptSaveExtAnchor;

    // Track anchor point.
    CPoint m_ptTrackExtAnchor;
     

    也可以选择:

    //////////////////////////////////////
    // Third extent anchor.
    //////////////////////////////////////
    // Anchor point.
    CPoint m_ptThirdAnchor;

    // Save anchor point.
    CPoint m_ptSaveThirdAnchor;

    // Track anchor point.
    CPoint m_ptTrackThirdAnchor;

    //////////////////////////////////////
    // Four extent anchor.
    //////////////////////////////////////
    // Anchor point.
    CPoint m_ptFourAnchor;

    // Save anchor point.
    CPoint m_ptSaveFourAnchor;

    // Track anchor point.
    CPoint m_ptTrackFourAnchor;
     

    只是选中了哪一组就需要对哪一组参数进行处理,其他的就不用管了。

     

  3. 使用额外控制点的基本步骤是什么?

    在决定使用了那几组额外控制点后,在具体使用时,可参考如下步骤:

    1)、通过ClassWizard做新的派生类,即派生新的图形元件类。

    2)、在派生类的Create函数中,通过传入的参数坐标值,对额外控制点的位置进行初始化,也就是决定该额外控制点最初需要显示在什么位置。

    3)、在Serialize中将该额外控制参数值保存到文件中去。

    4)、在GenAnchorPathPoints等虚函数中,对该额外控制点的相对运行路径进行设定。这个运行路径一定是一个相对值,也就是当参考点的位置发生变化后,会自动计算新的位置来保持几何形状不变化。

    5)、覆盖GetPlusSpotLocation虚函数,在该虚函数中将选择的额外控制点,加入到显示列表中。

    6)、覆盖ExtGeometryUpdated虚函数,计算由额外控制点关联起来的新的区域,这个主要用于鼠标移动的探测。

    7)、覆盖OnDraw3d和OnDrawFlat虚函数,来绘制新的几何形状。

、 使用额外控制点示范1:

我们以创建两个额外控制点的电力接线图母线元件做示例,如下图:

通过移动额外控制点,可以分别控制两端的文本的位置,当我们移动,缩放该母线的时候,额外控制点的文本会自动计算需要显示出来的相对位置。制作这两个额外控制点的基本步骤如下:
 

1、创建一个从CFORectShape派生下来的新类:CFOMatherLineShape。
 

2、覆盖CFORectShape的Create虚函数,计算两个额外控制点需要显示的初始位置坐标,代码如下:




/////////////////////////////////////////////////////////////////////////////


/////////////////////////////////////////////////////////////////////////////


void CFOMatherLineShape::Create(CRect &rcPos,CString strCaption)


{


	AllocMemory(4);


	


	m_lpShapePoints[0] = CPoint(rcPos.left,  rcPos.top);


	m_lpShapePoints[1] = CPoint(rcPos.right, rcPos.top);


	m_lpShapePoints[2] = CPoint(rcPos.right, rcPos.bottom);


	m_lpShapePoints[3] = CPoint(rcPos.left,  rcPos.bottom);


	SetObjectCaption(strCaption);


	CFODrawPortsShape::Create(rcPos,strCaption);





	FOPRect rce = rcPos;


	CPoint ptx = rce.LeftCenter();


	ptx.x -= 50;


	m_ptSaveAnchor = m_ptAnchor = ptx;





	ptx = rce.RightCenter();


	ptx.x += 50;


	m_ptSaveExtAnchor = m_ptExtAnchor = ptx;





	BuildAllDefaultPorts();


	CreateDefaultPort(0.25,0.50);


	CreateDefaultPort(0.75,0.50);


	UpdateComp();





}
这里必须同时初始化SaveAnchor和Anchor两个值,这两个值在最初的时候是一样的。SaveAnchor是需要保存起来的原始值,而Anchor是绘制变量。

 

3、将使用的该额外控制点保存到文件中去,调用:




void CFOMatherLineShape::Serialize(CArchive& ar)


{


	CFORectShape::Serialize(ar);


	if(ar.IsStoring())


	{


		//FODO:Add your own code here.


		ar << m_ptSaveAnchor;


		ar << m_ptSaveExtAnchor;


	}


	else


	{


		//FODO:Add your own code here.


		ar >> m_ptSaveAnchor;


		ar >> m_ptSaveExtAnchor;





		CFOMatrix* pMat = GetValidateMatrix();


		


		CPoint ptx;


		ptx = m_ptSaveAnchor;


		pMat->ChangeValue(ptx);


		m_ptAnchor = ptx;


		


		ptx = m_ptSaveExtAnchor;


		pMat->ChangeValue(ptx);


		m_ptExtAnchor = ptx;





	}


}
只需要保存SaveAnchor值,Anchor值可以通过CFOMatrix来计算获得。
4、覆盖GetPlusSpotLocation虚函数将额外控制点增加到显示列表中:



void CFOMatherLineShape::GetPlusSpotLocation(CFOPHandleList& lstHandle)


{


	CFOPHandle* pHandle = NULL;


	pHandle = new CFOPHandle(FOPPoint(m_ptAnchor),SPOT_ANCHOR);


	lstHandle.AddHandle(pHandle);


	


	pHandle = new CFOPHandle(FOPPoint(m_ptExtAnchor),SPOT_2_ANCHOR);


	lstHandle.AddHandle(pHandle);9


	


}


5、覆盖GeometryUpdated计算包括额外控制点在内的鼠标探测区域,代码如下:



void CFOMatherLineShape::GeometryUpdated(CFOArea* pArea)


{


//	CFORectShape::GeometryUpdated(pRgn);





	if (m_lpShapePoints && pArea)


	{


		pArea->BuildPolygonArea(m_lpShapePoints, m_nCompPtCount);


		m_rectPosition = pArea->GetBoundingRect();


		FOPRect rcNew = FOPRect(m_ptAnchor, m_ptExtAnchor);


		rcNew.InflateRect(szSaveText1);


		rcNew.InflateRect(szSaveText2);


		m_rectPosition = CFODrawHelper::GetMaxRectExt(rcNew, m_rectPosition);





		CalcExtendBoundRect(m_rectPosition);


	}





	//FODO:Add your own code below.





}
鼠标探测区域是通过CFOArea来处理的。
6、覆盖OnDraw3d函数来完成新的图形的绘制,代码如下:



void CFOMatherLineShape::OnDraw3d(CDC *pDC)


{


	CFODrawShape::OnDraw3d(pDC);





	//FODO:Add your own code below.


	CRect rcDraw = GetDrawRect();


	CPoint ptCenter = rcDraw.CenterPoint();


	int nLineWidth = 8;


	GetPropIntValue(nLineWidth, P_ID_MATHER_LINEWIDTH);





	rcDraw = CRect(rcDraw.left, ptCenter.y - nLineWidth, rcDraw.right, ptCenter.y + nLineWidth);





	BOOL bSelected = IsSelected();


	if(bSelected)


	{


		pDC->FillSolidRect(rcDraw, RGB(0,255,0));


	}


	else


	{


		pDC->FillSolidRect(rcDraw, GetBkColor());


	}





	///////////////////////


	CString strText1 = _T("");


	GetPropStringValue(strText1, P_ID_MATHER_TEXT1);


	UINT nAlign = GetDrawFormatType();


	{


		CString strFontFace = _T("Arial");


		int nFontSize = 16;


		COLORREF crFont = RGB(0,0,0);


		GetPropStringValue(strFontFace, P_ID_1_FONTNAME);


		GetPropIntValue(nFontSize, P_ID_1_FONTSIZE);


		GetPropColorValue(crFont, P_ID_1_FONTCOLOR);





		FOFontInfo font0;


		font0.SetPointSize(nFontSize);


		font0.m_strFaceName = strFontFace;


		CFont *pFont = GetFontFromCache(16, font0, pDC);


		CFont *pOldFont = (CFont *)pDC->SelectObject(pFont);





		CRect rcNew = GetSnapRect();


		if(!m_bWithTextWrap)


		{


			rcNew.InflateRect(gfxData.fo_DefaultSnapLineSizPix * 20000,gfxData.fo_DefaultSnapLineSizPix * 20000,


				gfxData.fo_DefaultSnapLineSizPix * 20000,gfxData.fo_DefaultSnapLineSizPix * 20000);


		}





		int nH;


		CSize sizeText = GetTextSize(pDC,rcNew,strText1,nH);


		sizeText += CSize(4,4);


		


		szSaveText1 = sizeText;





		CPoint ptCenter;


		


		ptCenter = m_ptAnchor;


		


		CRect rcTextBox;


		rcTextBox.left = (ptCenter.x - sizeText.cx / 2);


		rcTextBox.right = rcTextBox.left + sizeText.cx;


		


		rcTextBox.top = (ptCenter.y - sizeText.cy / 2);


		rcTextBox.bottom = rcTextBox.top + sizeText.cy;


		


		rcTextBox.NormalizeRect();





		COLORREF crColor = crFont;


		if(bSelected)


		{


			crColor = RGB(0,255,0);


		}


		DoDrawText(pDC,strText1,rcTextBox,nAlign,crColor,FALSE);





		pDC->SelectObject(pOldFont);


	}


	


	strText1 = _T("");





	GetPropStringValue(strText1, P_ID_MATHER_TEXT2);


	


	{


		CString strFontFace = _T("Arial");


		int nFontSize = 16;


		COLORREF crFont = RGB(0,0,0);


		GetPropStringValue(strFontFace, P_ID_2_FONTNAME);


		GetPropIntValue(nFontSize, P_ID_2_FONTSIZE);


		GetPropColorValue(crFont, P_ID_2_FONTCOLOR);


		


		FOFontInfo font0;


		font0.SetPointSize(nFontSize);


		font0.m_strFaceName = strFontFace;


		CFont *pFont = GetFontFromCache(17, font0, pDC);


		CFont *pOldFont = (CFont *)pDC->SelectObject(pFont);


		


		CRect rcNew = GetSnapRect();


		if(!m_bWithTextWrap)


		{


			rcNew.InflateRect(gfxData.fo_DefaultSnapLineSizPix * 20000,gfxData.fo_DefaultSnapLineSizPix * 20000,


				gfxData.fo_DefaultSnapLineSizPix * 20000,gfxData.fo_DefaultSnapLineSizPix * 20000);


		}


		


		int nH;


		CSize sizeText = GetTextSize(pDC,rcNew,strText1,nH);


		sizeText += CSize(4,4);


		


		szSaveText1 = sizeText;


		


		CPoint ptCenter;


		


		ptCenter = m_ptExtAnchor;


		


		CRect rcTextBox;


		rcTextBox.left = (ptCenter.x - sizeText.cx / 2);


		rcTextBox.right = rcTextBox.left + sizeText.cx;


		


		rcTextBox.top = (ptCenter.y - sizeText.cy / 2);


		rcTextBox.bottom = rcTextBox.top + sizeText.cy;


		


		rcTextBox.NormalizeRect();


		


		COLORREF crColor = crFont;


		if(bSelected)


		{


			crColor = RGB(0,255,0);


		}


		DoDrawText(pDC,strText1,rcTextBox,nAlign,crColor,FALSE);


		


		pDC->SelectObject(pOldFont);


	}


	


}
7、其他
由于我们需要这两个额外控制点可以任意移动位置,因此不需要将其固定在任何路径上,这一点与系统保留的缺省做法完全一致,因此不需要覆盖任何其他虚函数来处理路径问题。

四、额外控制点示例2

 

下面我们创建一个更加复杂的额外控制点示例,如下图:

这个图形包括两个额外控制点,一个用于调整文本的位置,另外一个用于调整直线中心的箭头的位置。要求文本的位置可以放在任何地方,而箭头只能在直线上沿线移动,下面是创建步骤:

 

1、从CFOLinkShape派生一个新类CFOPCenterArrowLink。

 

2、覆盖类中的Create函数,设定这两个额外控制点的初始位置:




/////////////////////////////////////////////////////////////////////////////


/////////////////////////////////////////////////////////////////////////////


BOOL CFOPCenterArrowLink::Create(CArray<CPoint,CPoint>* ptArray,CFOPortShape *pFrom,CFOPortShape *pTo)


{


	int nCount = ptArray->GetSize();


	if(nCount == 2)


	{


		AllocMemory(nCount);


		m_lpShapePoints[0] = ptArray->GetAt(0);


		m_lpShapePoints[1] = ptArray->GetAt(1);





		CPoint pt1 = CPoint(m_lpShapePoints[0]);


		CPoint pt2 = CPoint(m_lpShapePoints[1]);





		CPoint ptCenter = CFODrawHelper::GetCenterPoint(pt1, pt2);


		m_ptSaveAnchor = m_ptAnchor = ptCenter;





		ptCenter = CFODrawHelper::GetPercentPoint(pt1, pt2, 0.50);


		CPoint pt3 = CFODrawHelper::GetChuiZhiPoint(pt1, pt2, ptCenter, 20);


		


		m_ptSaveExtAnchor = m_ptExtAnchor = pt3;


	}


	else


	{


		AllocMemory(2);


		m_lpShapePoints[0] = ptArray->GetAt(0);


		m_lpShapePoints[1] = ptArray->GetAt(1);





		CPoint pt1 = CPoint(m_lpShapePoints[0]);


		CPoint pt2 = CPoint(m_lpShapePoints[1]);


		


		CPoint ptCenter = CFODrawHelper::GetCenterPoint(pt1, pt2);


		m_ptSaveAnchor = m_ptAnchor = ptCenter;





		m_ptSaveExtAnchor = m_ptExtAnchor = ptArray->GetAt(3);


	}


	


	if(pFrom != NULL)


	{


		m_pFromPort = pFrom;


	}


	


	if(pTo != NULL)


	{


		m_pToPort = pTo;


	}





	CRect m_rcSavePos = GetMaxRect();


	CFODrawShape::Create(m_rcSavePos);





	UpdateDataManager();


	RelayoutPoints();


	UpdateComp();


	return TRUE;


}
该文本的初始位置在直线的中心的垂直线上,而箭头位于直线的中心位置。


3、覆盖Serialize函数,将额外控制点的数据保存到文件中去:




void CFOPCenterArrowLink::Serialize(CArchive& ar)


{


	CFOLinkShape::Serialize(ar);


	if(ar.IsStoring())


	{


		//FODO:Add your own code here.


		ar << m_ptSaveAnchor;


		ar << m_ptSaveExtAnchor;


	}


	else


	{


		//FODO:Add your own code here.


		ar >> m_ptSaveAnchor;


		ar >> m_ptSaveExtAnchor;


		


		CFOMatrix* pMat = GetValidateMatrix();


		


		CPoint ptx;


		ptx = m_ptSaveAnchor;


		pMat->ChangeValue(ptx);


		m_ptAnchor = ptx;





		ptx = m_ptSaveExtAnchor;


		pMat->ChangeValue(ptx);


		m_ptExtAnchor = ptx;





		UpdateRatio();


		UpdateExtRatio();


	}


}

4、覆盖GenAnchorPathPoints和GenExtAnchorPathPoints分别计算两个额外控制点的运行路径:




void CFOPCenterArrowLink::GenAnchorPathPoints(CArray<CPoint,CPoint> &points, BOOL &bWithOutLine, const BOOL &bTrack)


{


	bWithOutLine = FALSE;


	points.RemoveAll();


	//points;


	//bTrack;


	if(!bTrack)


	{


		CPoint ptStart = CPoint(m_lpShapePoints[0]);


		CPoint ptEnd = CPoint(m_lpShapePoints[1]);


		points.Add(ptStart);


		points.Add(ptEnd);


	}


	else


	{


		CPoint ptStart = CPoint(m_lpShapeTrackPoints[0]);


		CPoint ptEnd = CPoint(m_lpShapeTrackPoints[1]);


		points.Add(ptStart);


		points.Add(ptEnd);


	}


	


}


这个额外控制点要求始终在端点0-1之间移动,不允许移动出去。因此bWithOutLine = FALSE;后面只需要将直线的两个端点加入路径中即可。



void CFOPCenterArrowLink::GenExtAnchorPathPoints(CArray<CPoint,CPoint> &points, BOOL &bWithOutLine, const BOOL &bTrack)


{


	bWithOutLine = TRUE;


	points.RemoveAll();


	//points;


	//bTrack;


	if(!bTrack)


	{


		CPoint ptStart = CPoint(m_lpShapePoints[0]);


		CPoint ptEnd = CPoint(m_lpShapePoints[1]);


		


		CPoint ptCenter = CFODrawHelper::FOPCalcPointOnLine(ptStart, ptEnd, m_dExtAnchorRatio);


		//	CPoint ptCenter = CFODrawHelper::NearestPointOnLine(m_ptAnchor, pt1, pt2);


		CPoint pt3 = CFODrawHelper::GetChuiZhiPoint(ptStart, ptEnd, ptCenter, 8);


		//	CPoint pt3, pt4;


		//	CFODrawHelper::GetPingXingPoint(pt1, pt2, pt3, pt4, 8);


		points.Add(ptCenter);


		points.Add(pt3);


	}


	else


	{


		CPoint ptStart = CPoint(m_lpShapeTrackPoints[0]);


		CPoint ptEnd = CPoint(m_lpShapeTrackPoints[1]);


		


		CPoint ptCenter = CFODrawHelper::FOPCalcPointOnLine(ptStart, ptEnd, m_dExtAnchorRatio);


		//	CPoint ptCenter = CFODrawHelper::NearestPointOnLine(m_ptAnchor, pt1, pt2);


		CPoint pt3 = CFODrawHelper::GetChuiZhiPoint(ptStart, ptEnd, ptCenter, 8);


		//	CPoint pt3, pt4;


		//	CFODrawHelper::GetPingXingPoint(pt1, pt2, pt3, pt4, 8);


		points.Add(ptCenter);


		points.Add(pt3);


	}


}


由于文本可以自由的移动到任何位置,因此我们将bWithOutLine = TRUE; 后面的代码中我们使用了一个m_dExtAnchorRatio参数来确保在任何位置均能得到合理的计算。

 

5、覆盖GetPlusSpotLocation虚函数,将额外控制点加入显示列表:




void CFOPCenterArrowLink::GetPlusSpotLocation(CFOPHandleList& lstHandle)


{


	CFOPHandle* pHandle = NULL;


	pHandle = new CFOPHandle(FOPPoint(m_ptAnchor),SPOT_ANCHOR);


	lstHandle.AddHandle(pHandle);


	


	pHandle = new CFOPHandle(FOPPoint(m_ptExtAnchor),SPOT_2_ANCHOR);


	lstHandle.AddHandle(pHandle);


}


SPOT_ANCHOR,SPOT_2_ANCHOR是预置好的这两个额外控制点的参数,固定做法。

6、覆盖ExtGeometryUpdated虚函数,计算包括额外控制点在内的可探测区域:




void CFOPCenterArrowLink::ExtGeometryUpdated(CFOArea* pArea)


{


	pArea->Clear();


	if (m_lpShapePoints && pArea)


	{


	


		CPoint ptPoints[3];


		ptPoints[0] = m_lpShapePoints[0];


		ptPoints[1] = m_ptAnchor;


		ptPoints[2] = m_lpShapePoints[1];


	


		pArea->BuildArea(ptPoints, 3,CFOArea::AreaLine);


		


		CFOArea rgnEndpt;


		


		if (m_pLineStartObject)


		{


			m_pLineStartObject->SetLineEndPoints(ptPoints[0], ptPoints[1]);


			m_pLineStartObject->BuildArea(&rgnEndpt);


			


			pArea->CombineArea(rgnEndpt);


		}


		


		if (m_pLineEndObject)


		{


			m_pLineEndObject->SetLineEndPoints(ptPoints[2], ptPoints[1]);


			m_pLineEndObject->BuildArea(&rgnEndpt);


			


			pArea->CombineArea(rgnEndpt);


		}


		


		m_rectPosition = pArea->GetBoundingRect();


		


		CalcExtendBoundRect(m_rectPosition);


	}


}

7、覆盖OnDraw3d, OnDrawFlat等函数来完成新图形的绘制:




void CFOPCenterArrowLink::OnDrawFlat(CDC *pDC)


{


	CFODrawShape::OnDrawFlat(pDC);





	int mode = pDC->SetBkMode(TRANSPARENT);





	CPoint ptPoints[3];


	ptPoints[0] = m_lpShapePoints[0];


	ptPoints[1] = m_ptAnchor;


	ptPoints[2] = m_lpShapePoints[1];





	CFOPVisualProxy::GetInstance()->PrepareTransparent(GetTransparentValue());





	CFOPVisualProxy::GetInstance()->DoDrawPolyLine(pDC,ptPoints, 3);





	pDC->SetBkMode(mode);


	if (m_pLineStartObject)


	{


		m_pLineStartObject->SetLineEndPoints(ptPoints[0], ptPoints[1]);


		m_pLineStartObject->OnDrawLine(pDC);


	}


	


	if (m_pLineEndObject)


	{


		m_pLineEndObject->SetLineEndPoints(ptPoints[2], ptPoints[1]);


		m_pLineEndObject->OnDrawLine(pDC);


	}


	


	m_ptLabel = GetLabelPoint();


	


	CRect rcNew = GetSnapRect();


	if(!m_bWithTextWrap)


		rcNew.InflateRect(gfxData.fo_DefaultSnapLineSizPix * 20000,gfxData.fo_DefaultSnapLineSizPix * 20000,


		gfxData.fo_DefaultSnapLineSizPix * 20000,gfxData.fo_DefaultSnapLineSizPix * 20000);


	


	


	CString strCaption = GetObjectCaption();


	


	int nH;


	CSize sizeText = GetTextSize(pDC,rcNew,strCaption,nH);


	sizeText += CSize(4,4);


	


	CPoint ptCenter;


	


	ptCenter = m_ptLabel;


	


	CRect rcTextBox;


	rcTextBox.left = (ptCenter.x - sizeText.cx / 2);


	rcTextBox.right = rcTextBox.left + sizeText.cx;


	


	rcTextBox.top = (ptCenter.y - sizeText.cy / 2);


	rcTextBox.bottom = rcTextBox.top + sizeText.cy;


	


	rcLinkLabel = rcTextBox;





	if(m_bLabelBack && (!strCaption.IsEmpty()))


	{


		if(IsShowLabelBorder())


		{


			if(m_pBorderPen == NULL)


			{


				FOPenInfo penInfo;


				penInfo.lopnWidth = 1;


				penInfo.lopnColor = RGB(0,0,0);


				penInfo.lopnStyle = PS_SOLID;


				


				CFOPenObjData pImage = NULL;


				


				pImage = new CFOPenObject();


				pImage->SetPenInfo(penInfo);


				


				m_pBorderPen = CFOPenCache::GetInstance()->FindPen(pImage);


			}


			


			CPen *pCurPen = m_pBorderPen->GetPen();


			


			CPen *pOld = (CPen *)pDC->SelectObject(pCurPen);


			POINT lpTempPoints[4];


			lpTempPoints[0] = rcTextBox.TopLeft();


			lpTempPoints[1] = CPoint(rcTextBox.right,rcTextBox.top);


			lpTempPoints[2] = rcTextBox.BottomRight();


			lpTempPoints[3] = CPoint(rcTextBox.left,rcTextBox.bottom);


			


			int nAngle = GetRotateAngle();


			if (nAngle != 0)


			{


				CFOMatrix m_Mat = CFOMatrix(*GetMatrixData());


				


				double dStartX = (double)m_ptExtAnchor.x;


				double dStartY = (double)m_ptExtAnchor.y;


				m_Mat.Move(-dStartX,-dStartY);


				m_Mat.Rotate(nAngle);


				m_Mat.Move(dStartX,dStartY);


				POINT worldPoints[4];


				memcpy(&worldPoints, lpTempPoints, sizeof(POINT) * 4);


				m_Mat.ChangeValue(worldPoints, 4);


				


				FillPoly(pDC,worldPoints, 4,GetBkColor(),


					GetPatternColor(),GetBrushType());


//				delete [] worldPoints;


			}


			else


			{


				FillPoly(pDC,lpTempPoints, 4,GetBkColor(),


					GetPatternColor(),GetBrushType());


				


			}


			


			if(pOld != NULL)


			{


				pDC->SelectObject(pOld);


			}


		}


		else


		{


			CPen *pNullPen = (CPen *)pDC->SelectStockObject(NULL_PEN);


			


			POINT lpTempPoints[4];


			lpTempPoints[0] = rcTextBox.TopLeft();


			lpTempPoints[1] = CPoint(rcTextBox.right,rcTextBox.top);


			lpTempPoints[2] = rcTextBox.BottomRight();


			lpTempPoints[3] = CPoint(rcTextBox.left,rcTextBox.bottom);





			int nAngle = GetRotateAngle();


			if (nAngle != 0)


			{


				CFOMatrix m_Mat = CFOMatrix(*GetMatrixData());


			


				double dStartX = (double)m_ptExtAnchor.x;


				double dStartY = (double)m_ptExtAnchor.y;


				m_Mat.Move(-dStartX,-dStartY);


				m_Mat.Rotate(nAngle);


				m_Mat.Move(dStartX,dStartY);


				POINT worldPoints[4];


				memcpy(&worldPoints, lpTempPoints, sizeof(POINT) * 4);


				m_Mat.ChangeValue(worldPoints, 4);


				


				FillPoly(pDC,worldPoints, 4,GetBkColor(),


				GetPatternColor(),GetBrushType());


//				delete [] worldPoints;


			}


			else


			{


				FillPoly(pDC,lpTempPoints, 4,GetBkColor(),


					GetPatternColor(),GetBrushType());


			


			}


			


			if(pNullPen != NULL)


			{


				pDC->SelectObject(pNullPen);


			}


		}


	}





	rcTextBox.NormalizeRect();


	OnDrawTextAndEdit(pDC,rcTextBox,FALSE);





	DoDrawMark(pDC);





}

8、覆盖DoDrawHighLightLine虚函数,来绘制选中的连接线高亮显示:




void CFOPCenterArrowLink::DoDrawHighLightLine(CDC *pDC)


{


    if(gfxData.m_pHighLightPen == NULL)


	{


        FOPenInfo penInfo;


        penInfo.lopnWidth = GetLineWidth() + 4;


        penInfo.lopnColor = gfxData.m_crHighLight;


        penInfo.lopnStyle = PS_SOLID;


        


		CFOPenObjData pImage = NULL;


		


		pImage = new CFOPenObject();


		pImage->SetPenInfo(penInfo);


		


		gfxData.m_pHighLightPen = CFOPenCache::GetInstance()->FindPen(pImage);


		


	}





//	pDC->SetTextColor(gfxData.m_crHighLight);


	CPen *pCurPen = gfxData.m_pHighLightPen->GetPen();


	


	CPen *pOld = (CPen *)pDC->SelectObject(pCurPen);





	CPoint ptPoints[3];


	ptPoints[0] = m_lpShapePoints[0];


	ptPoints[1] = m_ptAnchor;


	ptPoints[2] = m_lpShapePoints[1];





	CFOPVisualProxy::GetInstance()->DoDrawPolyLine(pDC,ptPoints, 3);


	


	if(pOld != NULL)


	{


		pDC->SelectObject(pOld);


	}


}
9、覆盖OnDrawTrackCustom虚函数,绘制拖拉过程中的虚线:



void CFOPCenterArrowLink::OnDrawTrackCustom(CDC *pDC)


{


	int mode = pDC->SetBkMode(TRANSPARENT);





	CPoint ptPoints[3];


	ptPoints[0] = m_lpShapeTrackPoints[0];


	ptPoints[1] = m_ptTrackAnchor;


	ptPoints[2] = m_lpShapeTrackPoints[1];


	pDC->Polyline(ptPoints, 3);





	CPoint ptCenter = CFODrawHelper::GetCenterPoint(ptPoints[0], ptPoints[2]);


	pDC->MoveTo(ptCenter);


	pDC->LineTo(m_ptTrackExtAnchor);





	CPoint ptx = m_ptTrackAnchor;


	FOPRect rcX(ptx, 8);


	POINT lpTempPoints[5];


	lpTempPoints[0] = rcX.TopCenter();


	lpTempPoints[1] = rcX.RightCenter();


	lpTempPoints[2] = rcX.BottomCenter();


	lpTempPoints[3] = rcX.LeftCenter();


	lpTempPoints[4] = rcX.TopCenter();


	


	int nAngle = GetRotateAngleTrackX();


	if (nAngle != 0)


	{


		CFOMatrix m_Mat;


		


		double dStartX = (double)m_ptTrackAnchor.x;


		double dStartY = (double)m_ptTrackAnchor.y;


		m_Mat.Move(-dStartX,-dStartY);


		m_Mat.Rotate(nAngle);


		m_Mat.Move(dStartX,dStartY);


		POINT worldPoints[5];


		memcpy(&worldPoints, lpTempPoints, sizeof(POINT) * 5);


		m_Mat.ChangeValue(worldPoints, 5);


		


		pDC->Polyline(worldPoints, 5);


	}


	else


	{


		pDC->Polyline(lpTempPoints, 5);


		


	}


//	FOPRect rcMark(m_ptTrackAnchor, CSize(8,8));


//	pDC->MoveTo(rcMark.TopLeft());


//	pDC->LineTo(rcMark.TopRight());


//	pDC->LineTo(rcMark.BottomRight());


//	pDC->LineTo(rcMark.BottomLeft());


//	pDC->LineTo(rcMark.TopLeft());





	pDC->SetBkMode(mode);





	if (m_pLineStartObject)


	{


		m_pLineStartObject->SetLineEndPoints(m_lpShapeTrackPoints[0], m_lpShapeTrackPoints[1]);


		m_pLineStartObject->OnDrawTrack(pDC);


	}





	if (m_pLineEndObject)


	{


		m_pLineEndObject->SetLineEndPoints(m_lpShapeTrackPoints[m_nCompTrackPtCount-1], m_lpShapeTrackPoints[m_nCompTrackPtCount-2]);


		m_pLineEndObject->OnDrawTrack(pDC);


	}


}
五、额外控制点其他提示:
1、如何决定从E-Form++的哪个系统图形元件类进行派生?
这个问题主要还是需要查看新增加的图形元件的状态,首先需要通过外观比较,看自己需要扩展的图形同哪个图形元件类似,同那个类似,就从哪个图形元件派生。其次要比较该新图形元件的操作方式,即操作方式同哪个图形元件类似就从哪个元件派生。
2、如何编写额外控制点的代码?
通过E-Form++提供的示例程序,例如:VisioApp, Circuit, TestLine等,看看自己的需要做的额外控制点同哪个图形元件的额外控制点相类似,如果一直,就打开该图形元件的代码,通过依次比较上诉虚函数的代码的处理,来设计自己的代码很多时候,做法都是通用的,固定的。只需要将代码复制过来即可。
3、如果额外控制点超过了6个数量限制该如何办?
建议直接从CFOLineShape来进行派生,可将任何一个顶点作为一个额外控制点,由于直线的顶点想多少就多少,所以任何数量的额外控制点均可以做到。具体请向UCanCode工程师咨询。
六、小结: 

     得益与C++开发代码的高度灵活性,E-Form++通过大量虚函数的使用,为用户自由控制程序中的任何细节提供了最恰当的方式。本文只是在一些基本层面对E-Form++的 图形元件的额外控制点的创建和使用进行了剖析,后面我们还会有专门的文章针对如何创建高级的控制元件进行介绍。

 

E-Form++可视化图形组件库企业版本全功能免费评估版本(附带大量示例VC++源代码)下载地址:https://www.ucancode.com

联系 UCanCode

购买源代码或了解更多的简单方法

  • 产品询价

  • E-mail 给我们(sales@ucancode.com)