小技巧:如何令Edit Control滚动到最新的输出行

来源:互联网 发布:pop默认端口 编辑:程序博客网 时间:2024/06/03 01:51

最近在做一个测试程序时,需要将程序的一些信息输出到Edit Control之中,以便于观察被测试程序的运行状态。在输出信息过程中发现,Edit Control默认是不会自动滚动,并将最新的信息显示到Edit Control中。

     由于无法看到最新的显示信息,造成了我在第一时间没有办法看到程序的运行状态,所以我决定仿造VS的输出窗口,在输出信息时让滚动条自动滚动,以便我能够在第一时间看到程序的运行状态。

     首先,为了使Edit Control能够接收多行文本,你必须使用代码或,者使用VS的界面设计器将Edit Control的Multline属性设置为true,为了使Edit Control更接近于VS的输出窗口,你还可以将Read Only以及Vertical Scroll属性设置为true。

     下面就是实现自动滚动功能了,于是我打开MSDN,发现了EM_LINESCROLL消息能够让我们将Edit Control水平滚动多少字符或者垂直滚动多少行。当我兴奋的觉得问题解决了的时候,发现我还需要知道Edit Control中有多少行信息,才能让Edit Control滚动到新信息的出现位置。再次查询MSDN,发现EM_GETLINECOUNT可以获取到Edit Control中所包含字符的行数。让Edit Control滚动的具体的代码如下所示:

[cpp] view plaincopy
  1. ::SetDlgItemText(hwndDlg/*包含Edit Control主窗口的句柄*/, iEditItem/*Edit Control资源的编号*/, pszOutput/*要输出的信息*/);  
  2. int iLine = ::SendMessage(hwndEdit/*Edit Control的句柄*/, EM_GETLINECOUNT, 0/*忽略*/, 0/*忽略*/);  
  3. ::SendMessage(hwndEdit, EM_LINESCROLL, 0/*水平滚动的字符个数*/, iLine/*垂直滚动的行数*/);  
 

     现在Edit Control已经能够自动滚动了。不过等一等,如果你仔细的观察一下的话,会发现垂直滚动条会先跳跃到顶部,然后在跳跃到底部。这是由于在调用了SetDlgItemText后,垂直滚动条会首先按照默认的方式首先滚动到顶部,然后在向Edit Control发送了EM_LINESCROLL消息后,滚动条就会滚动到底部。为了使我们在观察输出信息时,不至于被不停跳动的滚动条闪花我们的双眼(囧)。我们需要在调用SetDlgItemText之前关闭Edit Control的重绘,然后在向Edit Control发送EM_LINESCROLL消息后,重新打开重绘。新的代码如下所示:

[cpp] view plaincopy
  1. ::SendMessage(hwndEdit, WM_SETREDRAW, FALSE/*关闭重绘*/, 0);  
  2.   
  3. ::SetDlgItemText(hwndDlg/*包含Edit Control主窗口的句柄*/, iEditItem/*Edit Control资源的编号*/, pszOutput/*要输出的信息*/);  
  4. int iLine = ::SendMessage(hwndEdit/*Edit Control的句柄*/, EM_GETLINECOUNT, 0/*忽略*/, 0/*忽略*/);  
  5. ::SendMessage(hwndEdit, EM_LINESCROLL, 0/*水平滚动的字符个数*/, iLine/*垂直滚动的行数*/);  
  6.   
  7. ::SendMessage(hwndEdit, WM_SETREDRAW, TRUE/*打开重绘*/,  0);  

     好了,现在滚动条不会在跳来跳去了。正当我准备搞定收工的时候,发现用上述代码实现的滚动功能,比起VS的输出窗口来说还是有一定的缺陷,那就是用上述代码虽然实现了滚动,但是Edit Control中的光标始终在第一行,而VS的输出窗口的光标会自动移动到最新输出信息的下一行。哎,所谓好事多磨吧,我只好再次的打开MSDN进行查找,这次我使用的是EM_SETSEL这个消息。当向Edit Control发送这个消息的时候,Edit Control会移动光标,选中Edit Control中的文本。所以只要我们所选择的开始字符和结束字符是同一个位置,光标就会移动到那个字符之后。而由于我们在每一行文本后面都加了"/r/n",所以光标就会移动到最新输出文本的下一行。修改后的代码如下:

[cpp] view plaincopy
  1. ::SendMessage(hwndEdit, WM_SETREDRAW, FALSE/*关闭重绘*/, 0);  
  2.   
  3. ::SetDlgItemText(hwndDlg/*包含Edit Control主窗口的句柄*/, iEditItem/*Edit Control资源的编号*/, pszOutput/*要输出的信息*/);  
  4.   
  5. int iLine = ::SendMessage(hwndEdit/*Edit Control的句柄*/, EM_GETLINECOUNT, 0/*忽略*/, 0/*忽略*/);  
  6. ::SendMessage(hwndEdit, EM_LINESCROLL, 0/*水平滚动的字符个数*/, iLine/*垂直滚动的行数*/);  
  7.   
  8. int iOutputlen = _tcslen(pszOutput);  
  9. ::SendMessage(hwndEdit, EM_SETSEL,iOutputLen/*要选中字符的起始位置*/, iOutputLen/*要选中字符的结束位置*/);  
  10.   
  11. ::SendMessage(hwndEdit, WM_SETREDRAW, TRUE/*打开重绘*/,  0);  

     为了方便大家使用,我结合上面所介绍的方法并加上了一些错误处理,写了一个函数OutputWithScroll函数,函数的代码如下:

[cpp] view plaincopy
  1. ////////////////////////////////////////////////////////////////////////////////////////////////////  
  2. /// /fn BOOL OutputWithScroll(const HWND hwndDlg,   
  3. ///     const int iEditItem,   const TCHAR *pszNewText,   
  4. ///     const int iOutputSize, TCHAR *pszOutput)  
  5. ///  
  6. /// /brief  将新信息添加到Edit Control的最后,并自动滚动到新信息。  
  7. ///  
  8. /// /param [in] hwndDlg     Edit Control的父窗口句柄。  
  9. /// /param [in] iEditItem   Edit Control的资源编号。  
  10. /// /param [in] pszNewText  Edit 要添加到Edit Control中的新信息,如果新信息不是以/r/n结尾,函数会在输出信息  
  11. ///                         中自动添加。  
  12. /// /param [in] iOutputSize 缓冲区的大小。该缓冲区用来获取Edit Control中已经存在的字符。  
  13. /// /param [in] pszOutput   缓冲区指针。  
  14. ///  
  15. /// /return TRUE表示成功,FALSE表示失败。  
  16. ////////////////////////////////////////////////////////////////////////////////////////////////////  
  17. BOOL OutputWithScroll(const HWND hwndDlg,   
  18.     const int iEditItem,   const TCHAR *pszNewText,   
  19.     const int iOutputSize, TCHAR *pszOutput)  
  20. {  
  21.     if ((NULL == hwndDlg) || (NULL == pszNewText) ||   
  22.         (NULL == pszOutput))  
  23.     {  
  24.         return FALSE;  
  25.     }  
  26.   
  27.     HWND hwndEdit = ::GetDlgItem(hwndDlg, iEditItem);  
  28.     if (NULL == hwndEdit)  
  29.     {  
  30.         return FALSE;  
  31.     }  
  32.   
  33.     int iEditLen = 0;  
  34.     iEditLen = ::GetDlgItemText(hwndDlg, iEditItem, pszOutput, iOutputSize);  
  35.   
  36.     int iNewTextLen = _tcslen(pszNewText);  
  37.     int iOutputLen  = iEditLen + iNewTextLen;  
  38.     if (iOutputSize <= (iOutputLen + 2))  
  39.     {  
  40.         return FALSE;  
  41.     }  
  42.   
  43.     _tcscat_s(pszOutput, iOutputSize, pszNewText);  
  44.     if ((0 == iNewTextLen) ||   
  45.         (('/r' != pszNewText[iNewTextLen - 2]) || ('/n' != pszNewText[iNewTextLen - 1])))  
  46.     {  
  47.         _tcscat_s(pszOutput, iOutputSize, _T("/r/n"));  
  48.         iOutputLen += 2;  
  49.     }  
  50.   
  51.     ::SendMessage(hwndEdit, WM_SETREDRAW, FALSE, 0);  
  52.    
  53.     if (!::SetDlgItemText(hwndDlg, iEditItem, pszOutput))  
  54.     {  
  55.         return FALSE;  
  56.     }  
  57.   
  58.     int iLine = ::SendMessage(hwndEdit, EM_GETLINECOUNT,  
  59.         0, 0);  
  60.     ::SendMessage(hwndEdit, EM_LINESCROLL,  
  61.         0, iLine);  
  62.     ::SendMessage(hwndEdit, EM_SETSEL,  
  63.         iOutputLen, iOutputLen);  
  64.   
  65.     ::SendMessage(hwndEdit, WM_SETREDRAW, TRUE,  0);  
  66.   
  67.     return TRUE;  
  68. }  

     上面的代码感觉实在太多了,下面再给大家一个使用MFC的版本,由于使用MFC的CEdit需要做的错误处理更少,所以代码要简洁得多:

[cpp] view plaincopy
  1. ////////////////////////////////////////////////////////////////////////////////////////////////////  
  2. /// /fn void OutputWithScroll(const CString &strNewText,   
  3. ///     CEdit &edtOutput)  
  4. ///  
  5. /// /brief  将新信息添加到CEdit的最后,并自动滚动到新信息。  
  6. ///  
  7. /// /param [in]         strNewText  要添加到CEdit中的新信息,如果新信息不是以/r/n结尾,函数会在输出信息  
  8. ///                                 中自动添加。  
  9. /// /param [in, out]    edtOutput   要添加信息的CEdit。  
  10. ////////////////////////////////////////////////////////////////////////////////////////////////////  
  11. void OutputWithScroll(const CString &strNewText,  
  12.     CEdit &edtOutput)  
  13. {  
  14.     CString strOutput;  
  15.     edtOutput.GetWindowText(strOutput);  
  16.     strOutput += strNewText;  
  17.     if ("/r/n" != strOutput.Right(2))  
  18.     {  
  19.         strOutput += "/r/n";  
  20.     }  
  21.   
  22.     int iCount = strOutput.GetLength();  
  23.   
  24.     edtOutput.SetRedraw(FALSE);  
  25.     edtOutput.SetWindowText(strOutput);  
  26.     int iLine = edtOutput.GetLineCount();  
  27.     edtOutput.LineScroll(iLine, 0);  
  28.     edtOutput.SetSel(iCount, iCount);  
  29.     edtOutput.SetRedraw(TRUE);  
  30. }  

原创粉丝点击