照此 添加链接描述 博客上代码:
发现有几个问题
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;
}