在MFC中开发新的控件——Control

         众所周知,在MFC编程模式下,我们的应用程序界面是由窗口和控件组成,实际上,控件也是窗口,是一种特殊的窗口。而MFC已经为我们准备了一些基础的控件,例如:按钮,编辑框,and so on,但是在我们的大型软件开发中,这些控件是不够的。

          本文开发一种画图控件为导向,来说明其开发方法。

          查看MFC源码,我们发现,按钮控件(Cbutton)是公有继承于 CWnd类的,如下面的代码:(只摘抄了一部分)

class CButton : public CWnd
{
	DECLARE_DYNAMIC(CButton)

// Constructors
public:
	CButton();
	virtual BOOL Create(LPCTSTR lpszCaption, DWORD dwStyle,
				const RECT& rect, CWnd* pParentWnd, UINT nID);

};

            一般我们在窗口增加控件时,最常用的是可视化操作——也就是直接用鼠标拖拽。还有一种是动态创建,也就是用上面的Create(),(不过比较繁杂,用的较少)。

 下面直接给出我开发的控件代码:

// Header Files
class CMapping2 : public CWnd
{
public:
	CMapping2();
	~CMapping2();

public:
	DECLARE_MESSAGE_MAP()
	
	afx_msg void OnDestroy();
	afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
	afx_msg void OnPaint();
	afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
	afx_msg void OnLButtonUp(UINT nFlags, CPoint point);
	afx_msg void OnRButtonDown(UINT nFlags, CPoint point);
	afx_msg void OnMouseMove(UINT nFlags, CPoint point);
	afx_msg BOOL OnMouseWheel(UINT nFlags, short zDelta, CPoint pt);
	
public:
	int SetGrainZoomRatio(int nZoomRatio);
	int GetGrainZoomRatio();
	int SetWaferSize(int nCols, int nRows);
	CPoint getWaferSize();
	int DrawSingleGrain(int nCols, int nRows, COLORREF clr, BOOL bIsRefresh = TRUE);
	int DrawBatchGrain(CPoint Start, CPoint End, COLORREF clr);
	int ClearSingleGrain(int nCols, int nRows/*, COLORREF clr*/);
	int ClearAll();
	int UpdateMapping();
	int SetFunctionFlag(int nIndex);

	int SetLegend(int nCol, int nRow);
	int SaveMapping(LPCTSTR lpszPath, BOOL bIsClip = FALSE);  // xlsx and csv format
	int LoadMapping(LPCTSTR lpszPath);
	
private:
	int DrawLegendX(int nCol, CString str);
	int DrawLegendY(int nRow, CString str);
	HWND GetParentHWnd();
	int MakeSurePathExist(CString strPath);
	int GetGrainCoordinate(const CPoint PixelCoordinates, CPoint& GrainCoordinates);

public:
	enum{BACKGROUND_GRAY=RGB(250, 250, 250), 
		BAD_COLOR=RGB(255, 0, 0), 
		GOOD_COLOR=RGB(0, 255, 0), 
		SELECT_COLOR = RGB(128, 128, 128), 
		LEGEND_BACKGROUND	= RGB(200, 200, 200)};

	enum{DRAG = 0, BATCH_SELECT};

private:
	// 0		1		2		3		4		5...
	//拖动  批量选择  
	static const int m_snMaxItem = 10;
	BOOL m_IsSelected[m_snMaxItem];
	CPoint m_StartSelect;
	CPoint m_EndSelect;

	// 选择
	CPoint m_SltStartPt;
	CPoint m_SltEndPt;

private:
	int m_nWidth; // 去除边框的有效区域
	int m_nHeight;

	int m_nMemSize;
	int m_nClipOrgX;  // 双缓冲裁剪坐标原点 左上角
	int m_nClipOrgY;

	int m_nGrainGap;
	int m_nBorder;
	int m_nZoomRatio;

	int m_nMaxZoom;

	int m_nDieCols;
	int m_nDieRows;

	int m_nJump;

private:
	// Mapping
	CBitmap  m_bitmap;     
	CBitmap *m_pOldBitmap;  
	CDC		*m_pDC;
	CDC		 m_MemDC; 
	CRect    m_rect;  
	// top
	CBitmap  m_Topbitmap;     
	CDC		 m_TopMemDC;
	CFont    *pTopOldFont;
	// right
	CBitmap  m_Rightbitmap;     
	CDC		 m_RightMemDC;
	CFont    *pRightOldFont;

private:
	BOOL m_bLBtnIsDown;
	BOOL m_bIsDrag;
	CPoint m_StartPt;

private:
	CMainFrame *m_pMainFrame;
};


// Source Files
CMapping2::CMapping2()
{
	m_nWidth = 0;
	m_nHeight = 0;
	m_nGrainGap = 2; 
	m_nBorder = 20;
	m_nZoomRatio = 10;
	m_nMaxZoom = 15;
	m_nMemSize = 2000; // 2000x2000 像素点

	m_nClipOrgX = 1000;
	m_nClipOrgY = 1000;

	m_nJump = 10;

	m_bLBtnIsDown = FALSE;
	m_bIsDrag = FALSE;

	// 这些功能选择都是互斥项
	for(int i = 0; i != m_snMaxItem; i++)
	{
		m_IsSelected[i] = FALSE;
	}
	// 默认开启拖动功能
	m_IsSelected[DRAG] = TRUE;
}

CMapping2::~CMapping2()
{

}

BEGIN_MESSAGE_MAP(CMapping2, CWnd)
	ON_WM_LBUTTONDOWN()
	ON_WM_LBUTTONUP()
	ON_WM_RBUTTONDOWN()
	ON_WM_MOUSEMOVE()
	ON_WM_MOUSEWHEEL()
	ON_WM_PAINT()
	ON_WM_DESTROY()
	ON_WM_CREATE()
END_MESSAGE_MAP()


void CMapping2::OnPaint()
{
	CPaintDC dc(this);
	dc.BitBlt(m_nBorder, m_nBorder, m_nWidth, m_nHeight, &m_MemDC, m_nClipOrgX, m_nClipOrgY, SRCCOPY); 
	
	dc.BitBlt(m_nBorder, 0, m_nWidth, m_nBorder, &m_TopMemDC, m_nClipOrgX, 0, SRCCOPY); 
	dc.BitBlt(m_nWidth + m_nBorder, m_nBorder, m_nBorder, m_nHeight, &m_RightMemDC, 0, m_nClipOrgY, SRCCOPY);
}


void CMapping2::OnLButtonDown(UINT nFlags, CPoint point)
{
	// TODO: 在此添加消息处理程序代码和/或调用默认值

	CRect rect;
	GetClientRect(rect);
	rect.left += 10;
	rect.right -= 10;
	rect.top += 10;
	rect.bottom -= 10;

	if(!rect.PtInRect(point))
	{
		//	ReleaseCapture();
		return;
	}
	

	SetCapture();

	// 拖动需要
	m_bLBtnIsDown = TRUE;
	m_StartPt = point;

	CPoint MemPt, RetPt;
	//MemPt.x = m_nClipOrgX + (point.x - m_nBorder);
	//MemPt.y = m_nClipOrgY + (point.y - m_nBorder);

	//RetPt.x = (MemPt.x - m_nMemSize / 2) / (m_nZoomRatio + m_nGrainGap);
	//RetPt.y = (MemPt.y - m_nMemSize / 2) / (m_nZoomRatio + m_nGrainGap);

	//RetPt.x = (MemPt.x - m_nMemSize / 2) >= 0  ? RetPt.x : RetPt.x - 1;
	//RetPt.y = (MemPt.y - m_nMemSize / 2) >= 0  ? RetPt.y : RetPt.y - 1;

	GetGrainCoordinate(point, RetPt);


	m_pMainFrame = (CMainFrame*)AfxGetMainWnd();
	::PostMessage(m_pMainFrame->GetSafeHwnd(), WM_RETGRAINCOORDINATE, RetPt.x, RetPt.y);
                                        
	if(m_IsSelected[BATCH_SELECT])
	{
		m_SltStartPt = RetPt;
	}


#ifdef _DEBUG
	TRACE(_T("hit.x = %d, hit.y = %d\n"), RetPt.x, RetPt.y);
#endif

	CWnd::OnLButtonDown(nFlags, point);
}

void CMapping2::OnLButtonUp(UINT nFlags, CPoint point)
{
	// TODO: 在此添加消息处理程序代码和/或调用默认值

	m_bLBtnIsDown = FALSE;
	m_bIsDrag = FALSE;
	ReleaseCapture();

	Invalidate(FALSE);
	CWnd::OnLButtonDown(nFlags, point);
}


void CMapping2::OnRButtonDown(UINT nFlags, CPoint point)
{
	// TODO: 在此添加消息处理程序代码和/或调用默认值

	//if(是否在Mapping上点击)

	CRect rect;
	GetClientRect(rect);
	rect.left += m_nBorder;
	rect.right -= m_nBorder;
	rect.top += m_nBorder;
	rect.bottom -= m_nBorder;

	if(!rect.PtInRect(point))
	{
		//	ReleaseCapture();
		return;
	}

	HWND hParent = GetParentHWnd();
	::PostMessage(hParent, WM_RIGHTHITMAPPING, point.x, point.y);


	CWnd::OnRButtonDown(nFlags, point);
}


void CMapping2::OnMouseMove(UINT nFlags, CPoint point)
{
	// TODO: 在此添加消息处理程序代码和/或调用默认值

	CRect rect;
	GetClientRect(rect);
	rect.left += m_nBorder;
	rect.right -= m_nBorder;
	rect.top += m_nBorder;
	rect.bottom -= m_nBorder;

	if(!rect.PtInRect(point))
	{
		return;
	}

	if(m_bLBtnIsDown)
	{
		int nValid = DRAG;
		for(int i = 0; i != m_snMaxItem; i++)
		{
			if(m_IsSelected[i])
			{
				nValid = i;
				break;
			}
		}

		switch(nValid)
		{
		case DRAG:
			{
				m_bIsDrag = TRUE;

				CPoint OffSet;
				OffSet = point - m_StartPt;

				m_nClipOrgX -= OffSet.x;
				m_nClipOrgY -= OffSet.y;


				if(m_nClipOrgX < 0) 
					m_nClipOrgX = 0;
				else if(m_nClipOrgX  > m_nMemSize - m_nWidth)
					m_nClipOrgX = m_nMemSize - m_nWidth;

				if(m_nClipOrgY < 0) 
					m_nClipOrgY = 0;
				else if(m_nClipOrgY  > m_nMemSize - m_nHeight)
					m_nClipOrgY = m_nMemSize - m_nHeight;

				m_StartPt = point;

				// 发送 WM_PAINT
				Invalidate(FALSE);
			}
			break;

		case BATCH_SELECT:
			{
				// 发信息给父窗口,执行操作
				// 恢复之前的颜色
				// 重新画处目前的颜色
				//DrawBatchGrain()
			}
			break;

		default:
			{
				
			}

		}
	}

	CPoint RetPt;
	GetGrainCoordinate(point, RetPt);
	m_pMainFrame = (CMainFrame*)AfxGetMainWnd();
	::PostMessage(m_pMainFrame->m_hWnd, WM_MOUSEMOVEINMAPPING, RetPt.x, RetPt.y);

	CWnd::OnMouseMove(nFlags, point);
}


BOOL CMapping2::OnMouseWheel(UINT nFlags, short zDelta, CPoint pt)
{
	// TODO: 在此添加消息处理程序代码和/或调用默认值

	// 发送 WM_PAINT
	Invalidate(FALSE);
	TRACE(_T("nFlags = %d, zDelta = %d, ptX = %d, ptY = %d\n"), nFlags, zDelta, pt.x, pt.y);

	return CWnd::OnMouseWheel(nFlags, zDelta, pt);
}


void CMapping2::OnDestroy()
{
	CWnd::OnDestroy();
	// TODO: 在此处添加消息处理程序代码

	m_MemDC.SelectObject(&m_pOldBitmap);
	if(NULL != m_pDC)
	{
		ReleaseDC(m_pDC);
		m_pDC = NULL;
	}

	m_MemDC.DeleteDC();
	//m_pMemDC = NULL;

	// top 位图环境没有恢复
	m_TopMemDC.SelectObject(pTopOldFont);
	m_TopMemDC.DeleteDC();

	// Right 位图环境没有恢复
	m_RightMemDC.SelectObject(pRightOldFont);
	m_RightMemDC.DeleteDC();
}


int CMapping2::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
	if (CWnd::OnCreate(lpCreateStruct) == -1)
		return -1;

	// TODO:  在此添加您专用的创建代码
	CRect rect;
	GetClientRect(rect);
	m_nWidth = rect.Width() -  2 * m_nBorder;
	m_nHeight = rect.Height() - 2 * m_nBorder;

	m_pDC = GetDC();
	m_MemDC.CreateCompatibleDC(NULL);
	m_bitmap.CreateCompatibleBitmap(m_pDC, m_nMemSize, m_nMemSize);
	m_pOldBitmap = m_MemDC.SelectObject(&m_bitmap); 
	m_MemDC.FillSolidRect(0, 0, m_nMemSize, m_nMemSize, BACKGROUND_GRAY);

	//
	CFont font;
	int nFontHeight = m_nZoomRatio + m_nGrainGap;
	font.CreateFont(nFontHeight,  
		nFontHeight/3, 				
		0,	 
		0, 
		FW_THIN,
		0,
		0,  
		0,
		DEFAULT_CHARSET,   
		OUT_CHARACTER_PRECIS, 
		CLIP_CHARACTER_PRECIS,  
		DEFAULT_QUALITY,  
		DEFAULT_PITCH | FF_DONTCARE, 
		/* _T("Arial")*/_T("Courier New")); 

	// Top
	m_TopMemDC.CreateCompatibleDC(NULL);
	m_Topbitmap.CreateCompatibleBitmap(m_pDC, m_nMemSize, m_nBorder);
	m_TopMemDC.SelectObject(&m_Topbitmap);
	pTopOldFont = m_TopMemDC.SelectObject(&font);
	m_TopMemDC.FillSolidRect(0, 0, m_nMemSize, m_nBorder, LEGEND_BACKGROUND);

	//Right 
	m_RightMemDC.CreateCompatibleDC(NULL);
	m_Rightbitmap.CreateCompatibleBitmap(m_pDC, m_nBorder, m_nMemSize);
	m_RightMemDC.SelectObject(&m_Rightbitmap);
	pRightOldFont = m_RightMemDC.SelectObject(&font);
	m_RightMemDC.FillSolidRect(0, 0, m_nBorder, m_nMemSize, LEGEND_BACKGROUND);

	return 0;
}

int CMapping2::SetGrainZoomRatio(int nZoomRatio)
{
	if(nZoomRatio < 1 || nZoomRatio > m_nMaxZoom)
		return FALSE;

	m_nZoomRatio = nZoomRatio;
	return TRUE;
}

int CMapping2::GetGrainZoomRatio()
{
	return m_nZoomRatio;
}

int CMapping2::SetWaferSize(int nCols, int nRows)
{
	if(0 == nCols || 0 == nRows)
		return FALSE;

	m_nDieCols = nCols;
	m_nDieRows = nRows;

	CRect rect;
	GetClientRect(rect);
	m_nMaxZoom = (int)min((m_nMemSize - m_nBorder) / m_nDieCols, (m_nMemSize - m_nBorder) / m_nDieRows);
	m_nMaxZoom -= m_nGrainGap;

	if(m_nZoomRatio > m_nMaxZoom)
		m_nZoomRatio = m_nMaxZoom;
	if (m_nZoomRatio <= 1)
		m_nZoomRatio = 1;
	
	return TRUE;
}

CPoint CMapping2::getWaferSize()
{
	CPoint pt;
	pt.x = m_nDieCols;
	pt.y = m_nDieRows;

	return pt;
}

int CMapping2::DrawSingleGrain(int nCols, int nRows, COLORREF clr, BOOL bIsRefresh)
{
	int nx = nCols*(m_nZoomRatio + m_nGrainGap) + m_nMemSize / 2;
	int ny = nRows*(m_nZoomRatio + m_nGrainGap) + m_nMemSize / 2;
	m_MemDC.FillSolidRect(nx, ny, m_nZoomRatio, m_nZoomRatio, clr);

	/*CRect rect(m_nClipOrgX, m_nClipOrgY, m_nClipOrgX + m_nWidth, m_nClipOrgY + m_nHeight);

	if(!rect.PtInRect(CPoint(x, y)))
	{
	if(x) 
	}
	*/
	 
	if(!m_bIsDrag)
	{
		// 超出裁剪区域时,主动改变裁剪坐标
		if(nx > m_nClipOrgX + m_nWidth) 
			m_nClipOrgX = min(m_nMemSize - m_nWidth, m_nClipOrgX + m_nJump * m_nZoomRatio);
		if(nx < m_nClipOrgX)
			m_nClipOrgX = max(0, m_nClipOrgX - m_nJump * m_nZoomRatio);

		if(ny > m_nClipOrgY + m_nHeight) 
			m_nClipOrgY = min(m_nMemSize - m_nHeight, m_nClipOrgY + m_nJump * m_nZoomRatio);
		if(ny < m_nClipOrgY)
			m_nClipOrgY = max(0, m_nClipOrgY - m_nJump * m_nZoomRatio);
	}
	
	if(bIsRefresh)
	{
		Invalidate(FALSE);  // 不擦除背景,直接画
	}
	
	return TRUE;
}

int CMapping2::DrawBatchGrain(CPoint Start, CPoint End, COLORREF clr)
{
	int nStartX = 0, nEndX = 0;
	int nStartY = 0, nEndY = 0;

	nStartX = Start.x;
	nStartY = Start.y;
	nEndX = End.x;
	nEndY = End.y;

	// 特殊情况考虑 如: 起始点 = 右下角  结束点 = 左上角
	if(nStartX >= nEndX)
		swap(nStartX, nEndX);
	if(nStartY >= nEndY)
		swap(nStartY, nEndY);

	for(int i = nStartY; i != nEndY; i++)
	{
		for(int j = nStartX; j != nEndX; j++)
		{
			DrawSingleGrain(j, i, SELECT_COLOR);
		}

	}

	return TRUE;
}


int CMapping2::ClearSingleGrain(int nCols, int nRows/*, COLORREF clr*/)
{
	if (0 >= nCols ||0 >= nRows)
		return FALSE;

	DrawSingleGrain(nCols, nRows, BACKGROUND_GRAY);
	return TRUE;
}


int CMapping2::ClearAll()
{
	// Mapping 背景色
	m_MemDC.FillSolidRect(0, 0, m_nMemSize, m_nMemSize, BACKGROUND_GRAY);
	Invalidate(FALSE);
	return TRUE;
}

int CMapping2::UpdateMapping()
{
	Invalidate(FALSE);
	return TRUE;
}

int CMapping2::SetLegend(int nCol, int nRow)
{
	if (0 >= nCol || 0 >= nRow)
	{
		return FALSE;
	}

	m_TopMemDC.FillSolidRect(0, 0, m_nMemSize, m_nBorder, LEGEND_BACKGROUND);
	m_RightMemDC.FillSolidRect(0, 0, m_nBorder, m_nMemSize, LEGEND_BACKGROUND);

	BOOL bIsOdd = nCol%2;
	for(int i = -nCol/2; i <= nCol/2; i++)
	{
		if ((!bIsOdd) && i >= 0)
		{
			if(1 <= i)
			{
				CString str;
				str.Format(_T("%d"), i);
				DrawLegendX(i - 1, str);
			}
		}
		else
		{
			CString str;
			str.Format(_T("%d"), i);
			DrawLegendX(i, str);
		}
	}

	bIsOdd = nRow%2;
	for(int i = -nRow/2; i <= nRow/2; i++)
	{
		if ((!bIsOdd) && i >= 0)
		{
			if(1 <= i)
			{
				CString str;
				str.Format(_T("%d"), i);
				DrawLegendY(i - 1, str);
			}
		}
		else
		{
			CString str;
			str.Format(_T("%d"), i);
			DrawLegendY(i, str);
		}
	}

	Invalidate(FALSE);
	return TRUE;
}

int CMapping2::SetFunctionFlag(int nIndex)
{
	if(nIndex < 0 || nIndex >= m_snMaxItem)
		return FALSE;

	for(int i = 0; i != m_snMaxItem; i++)
	{
		m_IsSelected[i] = (i == nIndex) ? TRUE : FALSE;
	}

	return TRUE;
}

int CMapping2::DrawLegendX(int nCol, CString str)
{
	if(_T("") == str)
		return FALSE;

	int nx = nCol*(m_nZoomRatio + m_nGrainGap) + m_nMemSize / 2;
	int ny = 0;

	m_TopMemDC.TextOut(nx, ny, str);
	return TRUE;
}

int CMapping2::DrawLegendY(int nRow, CString str)
{
	if(_T("") == str)
		return FALSE;

	int nx = 3;
	int ny = nRow*(m_nZoomRatio + m_nGrainGap) + m_nMemSize / 2;

	m_RightMemDC.TextOut(nx, ny, str);
	return TRUE;
}
	
int CMapping2::SaveMapping(LPCTSTR lpszPath, BOOL bIsClip)
{
	CImage image;
	int nBoder = 10;
	if(bIsClip)
	{
		int nWidth = m_nDieCols*(m_nZoomRatio + m_nGrainGap);
		int nHeight = m_nDieRows*(m_nZoomRatio + m_nGrainGap);
		image.Create(nWidth , nHeight , 32);
		::BitBlt(image.GetDC() ,0 ,0 ,nWidth ,nHeight ,m_MemDC.m_hDC ,(m_nMemSize- nWidth)/2 - nBoder, (m_nMemSize- nHeight)/2 - nBoder, SRCCOPY);
	}
	else
	{
		HBITMAP hBitMap = HBITMAP(m_bitmap);
		CImage image;
		image.Attach(hBitMap);
	}

	MakeSurePathExist(lpszPath);
	HRESULT hr = image.Save(lpszPath);
	return (hr == S_OK);
}


int CMapping2::LoadMapping(LPCTSTR lpszPath)
{
	return TRUE;
}

HWND CMapping2::GetParentHWnd()
{
	m_pMainFrame = (CMainFrame*)AfxGetMainWnd();
	return m_pMainFrame->m_wndBottomBar.m_hWnd;
}

int CMapping2::MakeSurePathExist(CString strPath)
{
	int nSize = strPath.GetLength() * 2 + 1; 

	wchar_t *pWchar_t = strPath.GetBuffer();
	char *pChar = new char[nSize];

	if(NULL == pChar)
		return FALSE;

	memset(pChar, 0, (size_t)nSize);
	wcstombs(pChar, pWchar_t, nSize);  

	if (!MakeSureDirectoryPathExists(pChar))
		return FALSE;

	delete []pChar;
	return TRUE;
}

int CMapping2::GetGrainCoordinate(const CPoint PixelCoordinates, CPoint& GrainCoordinates)
{
	CPoint MemPt, RetPt;
	MemPt.x = m_nClipOrgX + (PixelCoordinates.x - m_nBorder);
	MemPt.y = m_nClipOrgY + (PixelCoordinates.y - m_nBorder);

	RetPt.x = (MemPt.x - m_nMemSize / 2) / (m_nZoomRatio + m_nGrainGap);
	RetPt.y = (MemPt.y - m_nMemSize / 2) / (m_nZoomRatio + m_nGrainGap);

	RetPt.x = (MemPt.x - m_nMemSize / 2) >= 0  ? RetPt.x : RetPt.x - 1;
	RetPt.y = (MemPt.y - m_nMemSize / 2) >= 0  ? RetPt.y : RetPt.y - 1;

	GrainCoordinates = RetPt;

	return TRUE;
} 

   看起来程序比较多,但大部分是增加功能的,对本文讲述没有影响,我给出三点总结:

  1. 模仿MFC自带的控件,如:按钮CButton, 继承CWnd类。

  2. 如果要在控件中绘图的话,一定要在onPaint()消息响应中,因为在标准客户区绘图,窗口刷新时,图案不消失,在        WM_PAINT下回调onPaint().

  3. 我们开发的控件,也只能用动态方式创建:使用方式如下:

	CRect rect;
	CWnd *pWnd = GetDlgItem(IDC_STATIC_MAPPING);
	pWnd->GetClientRect(rect);
	m_Mapping.Create(NULL, NULL, WS_CHILD|WS_VISIBLE|SS_CENTER, rect, pWnd, 100081);

IDC_STATIC_MAPPING是一个静态文本控件,主要是用来设置开发控件的尺寸大小。

 

完成上面三个主要的步骤,就可以绘图了,下面是我的demo演示:

使用代码演示:

       m_Mapping.ClearAll();
 
	int nCol = 50, nRow = 51;
	int nColGap = 2+5; int nRowGap = 2+5;

	int nR = 175;
	for(int i = 0; i != nRow/2; i++)
	{
		if(!(nCol%2))
		{
			int L = sqrt(double(nR*nR - (i*1)*nRowGap*(i*1)*nRowGap));
			int N = (L - 6)/nColGap;
			for(int j = 0; j != N; j++)
			{
				m_Mapping.DrawSingleGrain(j, i+1, RGB(rand()%255, rand()%255, rand()%255));
				m_Mapping.DrawSingleGrain(-(j+1), i+1, RGB(rand()%255, rand()%255, rand()%255));

				m_Mapping.DrawSingleGrain(j, -(i+1), RGB(rand()%255, rand()%255, rand()%255));
				m_Mapping.DrawSingleGrain(-(j+1), -(i+1), RGB(rand()%255, rand()%255, rand()%255));
			}
		}
	}

	for(int i = 0; i != nCol/2; i++)
	{
		m_Mapping.DrawSingleGrain(i, 0, RGB(rand()%255, rand()%255, rand()%255));
		m_Mapping.DrawSingleGrain(-i, 0, RGB(rand()%255, rand()%255, rand()%255));
	}

窗口演示效果:

     

       本文是开发新控件的一种方式,还有一种是开发成  “ActiceX”  格式的,比如 tree Chart绘图控件,就是这样式儿的,不过后者相对前者比较繁杂,一般开发功能性不是很复杂的控件,用本文介绍的方式,周期短,性价高。

        好了,以上就是我开发控件的流程,时间仓促,文中难免有什么错误之处,还请大家谅解。

 

 

 

 

 

 

 

 

 

 

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值