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

最近在做一个测试程序时,需要将程序的一些信息输出到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 plain copy
  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 plain copy
  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 plain copy
  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 plain copy
  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 plain copy
  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. }  

MFC(Microsoft Foundation Classes)是微软提供的一套C++类库,它封装了Windows API,用于简化Windows应用程序的开发。使用MFC开发一个简单的计算器程序,可以实现基本的四则运算功能,并将结果输出到日志文件中。下面是一个简单的流程介绍: 1. **创建MFC应用程序**:首先,你需要使用Visual Studio创建一个MFC应用程序框架。这通常涉及选择一个适合的项目模板,比如MFC应用程序向导来开始。 2. **界面设计**:在设计界面时,你需要添加一些控件,例如按钮(用于数字和运算符),编辑框(用于输入和显示结果)。 3. **响应用户输入**:为按钮添加事件处理函数,当用户点击按钮时,这些函数会被触发。你需要编写代码来处理用户的输入,执计算,并更新编辑框中的显示结果。 4. **输出到日志文件**:在计算结果后,你可以在代码中添加一段写入日志文件的代码。这通常涉及到打开或创建一个文本文件,并将结果格式化后写入该文件。可以使用C++的文件流(例如`std::ofstream`)来完成这项工作。 5. **错误处理和验证**:在实际的应用中,还需要添加适当的错误处理机制,比如验证输入是否合法,处理除以零的情况等。 一个简单的示例代码段可能如下所示: ```cpp void CYourCalculationDialog::OnCalculate() { // 假设m_editResult是与编辑框关联的成员变量 CString strResult = m_editResult; // 这里可以添加计算逻辑,并将结果更新到strResult中 // 将结果显示在编辑框中 m_editResult.SetWindowText(strResult); // 输出到日志文件 CStdioFile logFile; if (logFile.Open(_T("log.txt"), CFile::modeCreate | CFile::modeWrite | CFile::typeText)) { logFile.WriteString(strResult); // 写入结果 logFile.Close(); // 关闭文件 } else { AfxMessageBox(_T("无法打开日志文件")); } } ``` 在上述示例中,`OnCalculate`函数是处理计算逻辑和写入日志的示例。实际情况下,你需要根据实际的计算逻辑来更新`strResult`。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值