当前位置: 首页 > 工具软件 > RichEdit > 使用案例 >

RichEdit 增加 行号和 位置Pointer

汲品
2023-12-01

照此 添加链接描述 博客上代码:
发现有几个问题
1.行号和行数不一定能够对齐.
绘制文字时候的有可能出现偏差,所以造成行号和行数无法对齐。
2.当RichEdit存在滚动时,重绘速度会很慢.
行号是整页全部都绘制,修改为可见部分的绘制,其他部分当前不绘制。
3.使用时间长了之后,会发现行号会消失,原因是 hdcCpb = CreateCompatibleDC(hdcEdit) 失败.
在绘制行号底色使用了CBrush ,但是结束后没有DeleteObject,猜测可能造成释放 HDC hdcCpb HBITMAP hdcBmp 失败!

//此处为绘制行号的代码

int GetMaxLineNumber(HWND hEdit, HDC hdc)
{
	RECT richEditRect;

	SendMessage(hEdit, EM_GETRECT, 0, (LPARAM)&richEditRect);
	int iRectHeight = richEditRect.bottom - richEditRect.top;

	TEXTMETRIC tm;
	GetTextMetrics(hdc, &tm);

	return (iRectHeight / tm.tmHeight) + 1;
}

void ShowLineNum(HWND hEdit)
{
	/*
	功能:显示文本的总行数
	参数:
	hEdit:要显示行号的文本框,普通的Edit控件没有测试过,这里只用RichEdit

	返回值:没有。
	--------------------------------------------------------------------------------
	*/
	RECT     ClientRect;     //RichEdit的客户区大小
	HDC      hdcEdit;         //RichEdit的Dc(设备环境)
	HDC      hdcCpb;          //与RichEdit兼容的Dc
	HBITMAP  hdcBmp;         //RichEdit兼容的位图dc
//	int      CharHeight;      //字符的高度
//	int      chHeight;        //字符的高度,常量 
	HBITMAP old_hBmp;
	int      FirstLine;       //文本框中的第一个可见行的行号。
	int      ClientHeight;    //RichEdit的客户区高度 
	int      LineCount;       //文本的总行数 
//	char     countBuf[10];   //显示行号的缓冲区
//	CHARFORMAT     CharFmt;  //RichEdit中的一个结构,用于获取字符的一系列信息,这里只用它来获取字符高度
	CFont    Oldfont;


		//设置字体大小
	CFont font;
	font.CreateFont(12,   // nHeight  
		0,                         // nWidth  
		0,                         // nEscapement  
		0,                         // nOrientation  
		FW_NORMAL,                 // nWeight  
		FALSE,                     // bItalic  
		FALSE,                     // bUnderline  
		0,                         // cStrikeOut  
		ANSI_CHARSET,              // nCharSet  
		OUT_DEFAULT_PRECIS,        // nOutPrecision  
		CLIP_DEFAULT_PRECIS,       // nClipPrecision  
		DEFAULT_QUALITY,           // nQuality  
		DEFAULT_PITCH | FF_SWISS,  // nPitchAndFamily  
		_T("新宋体"));


	//获取RichEdit的Dc
	hdcEdit = GetDC(hEdit);

	//TEXTMETRIC tm;
	//GetTextMetrics(hdcEdit, &tm);
	GetClientRect(hEdit, &ClientRect);
	//获取RichEdit的客户区高度
	ClientHeight = ClientRect.bottom - ClientRect.top;
	ClientRect.right = _DEF_LINE_WIDE;
	//创建与RichEdit兼容的Dc
	hdcCpb = CreateCompatibleDC(hdcEdit);

	//创建与RichEdit兼容的位图Dc,我们用来显示行号用的。
	hdcBmp = CreateCompatibleBitmap(hdcEdit, _DEF_LINE_WIDE, ClientHeight);

	//将位图dc选入RichEdit环境中

	//SaveDC(hdcEdit);
	old_hBmp = (HBITMAP)SelectObject(hdcCpb, hdcBmp);

	 SelectObject(hdcCpb, font);
	//填充显示行号dc的背景颜色。大家可以试试其它颜色
	//FillRect(hdcCpb, &ClientRect, CreateSolidBrush(0x8080ff));
	//FillRect(hdcCpb, &ClientRect, CreateSolidBrush(0xB4D1C9));
	//FillRect(hdcCpb, &ClientRect, CreateSolidBrush(0xFFFFFF));

	CBrush newBrush;
	newBrush.CreateSolidBrush(0xB4D1C9);
	FillRect(hdcCpb, &ClientRect, newBrush);
	SetBkMode(hdcCpb, TRANSPARENT);
	获取当前RichEdit文本中的总行数
	LineCount = SendMessage(hEdit, EM_GETLINECOUNT, 0, 0);

	//设置显示行号的前景色
	//SetTextColor(hdcCpb, 0x000000);
	//获取文本框中第一个可见的行的行号,没有这个行号,显示不会跟着文本的滚动而滚动。

	FirstLine = SendMessage(hEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
	//FirstLine++;
	//在位图dc中循环输出行号
	int iLineChar = 0;
	LineCount = min(LineCount, FirstLine + GetMaxLineNumber(hEdit, hdcEdit));

	POINTL pl;
	RECT tmpRect;
	CString csText;

	while (FirstLine < LineCount)
	{

		iLineChar = SendMessage(hEdit, EM_LINEINDEX, FirstLine, 0);
		FirstLine++;

		SendMessage(hEdit, EM_POSFROMCHAR, (WPARAM)&pl, (LPARAM)iLineChar);

		tmpRect.right  = _DEF_LINE_WIDE - 2; //行数位置应该比边框少2   
		tmpRect.left   = 0;			  //left border is flush with edge of window
		tmpRect.top    = pl.y+2;
		tmpRect.bottom = pl.y+20;        //bottom is same as rich edit controls bottom
		csText.Format("%d", FirstLine);
		DrawText(hdcCpb, csText, csText.GetLength(), &tmpRect, DT_RIGHT);

		
	}
	//将已"画好"的位图真正"贴"到RichEdit中
	BitBlt(hdcEdit, 0, 0, _DEF_LINE_WIDE, ClientHeight, hdcCpb, 0, 0, SRCCOPY);


	//::RestoreDC(hdcCpb, -1);
	font.DeleteObject();
	newBrush.DeleteObject();
	::ReleaseDC(hEdit, hdcEdit);
	SelectObject(hdcCpb, old_hBmp);
	::DeleteObject(hdcBmp);
	::DeleteDC(hdcCpb);
}

//此处为位置Pointer,以及修改Pointer和跟随 的代码

void ShowPositon(HWND hEdit, int iScriptPosition)
{

	RECT     ClientRect;     //RichEdit的客户区大小
	HDC      hdcEdit;         //RichEdit的Dc(设备环境)
	HDC      hdcCpb;          //与RichEdit兼容的Dc
	HBITMAP  hdcBmp;      //RichEdit兼容的位图dc
	HBITMAP old_hBmp;
	int      FirstLine;       //文本框中的第一个可见行的行号。
	int      ClientHeight;    //RichEdit的客户区高度 
	int      LineCount;       //文本的总行数 


	BOOL bRet = FALSE;

	//获取RichEdit的Dc
	hdcEdit = GetDC(hEdit);

	//TEXTMETRIC tm;
	//GetTextMetrics(hdcEdit, &tm);

	GetClientRect(hEdit, &ClientRect);
	//获取RichEdit的客户区高度
	ClientHeight = ClientRect.bottom - ClientRect.top;

	ClientRect.right = _DEF_POS_WIDE;
	//ClientRect.left  = _DEF_LINE_WIDE;
	//ClientRect.right = _DEF_LINE_WIDE + 15;

	//创建与RichEdit兼容的Dc
	hdcCpb = CreateCompatibleDC(hdcEdit);


	//创建与RichEdit兼容的位图Dc,我们用来显示行号用的。
	hdcBmp = CreateCompatibleBitmap(hdcEdit, _DEF_POS_WIDE, ClientHeight);

	SaveDC(hdcEdit);
	//将位图dc选入RichEdit环境中
	

	old_hBmp = (HBITMAP)SelectObject(hdcCpb, hdcBmp);

	//填充显示行号dc的背景颜色。大家可以试试其它颜色
	CBrush newBrush;
	newBrush.CreateSolidBrush(0xD4F1D5);
	int iRet = FillRect(hdcCpb, &ClientRect, newBrush);
	SetBkMode(hdcCpb, TRANSPARENT);


	//设置显示行号的前景色
	SetTextColor(hdcCpb, 0x000000);
	//获取文本框中第一个可见的行的行号,没有这个行号,显示不会跟着文本的滚动而滚动。
	FirstLine = SendMessage(hEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
	//FirstLine++;

	//获取当前RichEdit文本中的总行数
	//LineCount = SendMessage(hEdit, EM_GETLINECOUNT, 0, 0);
	LineCount = FirstLine + GetMaxLineNumber(hEdit, hdcEdit);

	HICON hIcon = (HICON)::LoadImage(AfxGetInstanceHandle(), MAKEINTRESOURCE(IDI_ArrowInEdit), IMAGE_ICON, 0, 0, LR_DEFAULTCOLOR);
	POINTL pl;
	//在位图dc中循环输出行号
	int iLineChar = 0;
	while (FirstLine <= LineCount)
	{
		FirstLine++;
		if (iScriptPosition != FirstLine)	
			continue;

		iLineChar = SendMessage(hEdit, EM_LINEINDEX, (FirstLine - 1), 0);
		SendMessage(hEdit, EM_POSFROMCHAR, (WPARAM)&pl, (LPARAM)iLineChar);
		if (hIcon) DrawIconEx(hdcCpb, 4, pl.y+2, hIcon, 12, 12, 0, NULL, DI_NORMAL);	

		break;
	}
	//将已"画好"的位图真正"贴"到RichEdit中
	BitBlt(hdcEdit, _DEF_LINE_WIDE, 0, _DEF_POS_WIDE, ClientHeight, hdcCpb, 0, 0, SRCCOPY);
	
	::DestroyIcon(hIcon);


	//::RestoreDC(hdcCpb, -1);
	newBrush.DeleteObject();
	::ReleaseDC(hEdit, hdcEdit);
  	SelectObject(hdcCpb, old_hBmp);
	::DeleteObject(hdcBmp);
	::DeleteDC(hdcCpb);




}

int CLineRichEditCtrl::GetScirptPosition()
{
	return iScriptPosition;
}

void CLineRichEditCtrl::SetScirptPosition(int iPos)
{
	iScriptPosition = iPos;
	__DisplayScirpitPosition();
	//SetFocus();
	
	//RedrawWindow();
	//CRichEditCtrl::RedrawWindow(TRUE);
	//SetCaretPos(iPos - 1);
	//SetCareIndex(iPos - 1);
}

void CLineRichEditCtrl::__DisplayScirpitPosition(BOOL bMod)
{
	if (iScriptPosition < 1)
		return;

	int iMin, iMax;
	GetScrollRange(SB_VERT, &iMin, &iMax);//获取滚动条的范围
	int iLimit = GetScrollLimit(SB_VERT); //获取Limit范围
	int iCount = GetLineCount();          //Edit的行数
	int iPagePos = iMax - iLimit;         //单元可以显示的范围
	int iPos = (iScriptPosition-1) * iMax / iCount; //计算当前条,为定格记录时候的Pos
	if (iPos > iLimit)iPos = iLimit; //如果超出限制,应该为限制内数值
	if (bMod == 01)
		if ((iPos % iPagePos) != 0) iPos = (iPos / iPagePos) * iPagePos;//如果不是每次都跟随,只需要在当前条修改ipos则可以

	SendMessage(WM_VSCROLL, MAKEWPARAM(SB_THUMBPOSITION, iPos), 0);

}

int  CLineRichEditCtrl::GetPositonText(CString & csText)
{


	int iLineIndex = CRichEditCtrl::LineIndex(iScriptPosition - 1);
	int iLen       = CRichEditCtrl::LineLength(iLineIndex);

	CRichEditCtrl::SetSel(iLineIndex, iLineIndex + iLen);
	csText = CRichEditCtrl::GetSelText();
	return iLen;
}

 类似资料: