65.9K
CodeProject 正在变化。 阅读更多。
Home

日志列表框控件

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.80/5 (9投票s)

2000年5月17日

viewsIcon

178709

downloadIcon

3053

了解如何使用类似 printf 的功能来调试您的 GUI 应用程序。

  • 下载源文件 - 46 Kb

    引言

    我从从控制台类型应用程序转向 GUI 世界的程序员那里听到的最常见问题之一是“我如何使用 printf 进行调试输出?”十年前,在我的第一个 Windows 应用程序中,我问了自己同样的问题,并得出了这个答案。我在这篇文章和随附的代码下载中展示的是十年磨砺出世界上最伟大的(至少在我的愚见中)printf 等价物的结果。我在随附的关于图形开发环境的文章中提到了这一点,但这是一个功能齐全的控件,包含了所有我似乎需要的功能。

    除了包含跟踪日志之外,这个示例还演示了 MFC 中的许多其他有趣技术。这包括:

    • 对列表中的项目进行选择性删除
    • 对列表中的项目进行选择性剪切
    • 对列表中的项目进行选择性复制
    • 打印 CListBox
    • 一种通用的行打印机制,在随附的文章中描述。
    • CListBox 进行选择性打印
    • 从全局环境与 CFormView 的元素进行交互
    • 通过除新建菜单项之外的方式创建视图
    • 拥有一个可以创建或弹出视图的菜单项
    • 一个很好的 CFormView 示例
    • 使用具有可变参数列表的构造函数
    • 在具有可变参数列表的函数中格式化字符串的能力。

    这是一个 CListBox 派生类,需要一些字符串资源才能完整。因此,您将不得不从示例项目中复制一组字符串资源。IDS_TRACELOG_ 消息位于 5000 范围,IDS_WSA Winsock 错误消息位于 10000 范围。

    显示在 MDI 下使用这些类的完整项目可以通过点击页面顶部的链接下载。它是在 VC++ 6.0 下编译的。


    TraceEvent 类

    类声明

    事件日志系统的核心是一个所有者绘制的列表框。它绘制从抽象 TraceEvent 类派生的项目。

    class TraceEvent : public CObject {
        DECLARE_DYNAMIC(TraceEvent)
    protected:
        static UINT counter;
        TraceEvent(UINT id) { time = CTime::GetCurrentTime(); 
        			  filterID = id; 
    			  threadID = ::GetCurrentThreadId(); 
    			  seq = counter++;
    			}
    public:
        enum {None = -1};
        virtual ~TraceEvent() { }
        virtual COLORREF textcolor() = 0;
        virtual CString show();
        virtual CString showID();
        virtual CString showfile();
        virtual CString showThread();
        virtual int displayHeight() { return 1; } 
        static CString cvtext(BYTE ch); // useful for various subclasses
        static CString cvtext(const CString &s); // useful for various subclasses 
    protected:
        CTime time;
        UINT  filterID;
        DWORD threadID;
        UINT  seq;
    };
    

    请注意,textcolor 方法被指定为 = 0,这意味着无法实例化 TraceEvent 类的元素。相反,您子类化此项并创建新类。

    我将此D类从 CObject 派生,因为我希望能够在处理的某些点进行 IsKindOf

    一个典型的子类是 TraceComment,其形式为

    /****************************************************************************
    *                           class TraceComment
    * Inputs:
    *	UINT id: connection ID, or TraceEvent::None
    *	CString s: String to display
    * Inputs:
    *	UINT id: connection ID, or TraceEvent::None
    *	UINT u: IDS_ index of string to display
    ****************************************************************************/
    
    class TraceComment: public TraceEvent 
    {
        DECLARE_DYNAMIC(TraceComment)
    public:
        TraceComment(UINT id, const CString &s) : TraceEvent(id) {comment = s;}
        TraceComment(UINT id, UINT u) : TraceEvent(id)
        { 
            CString s; s.LoadString(u); comment = s; 
        }
        virtual ~TraceComment() { }
        virtual COLORREF textcolor() { return RGB(0, 128, 0); }
        virtual CString show();
    protected:
        CString comment;
    };
    

    这是一个典型的子类。它为显示提供颜色,一个用于格式化构成显示的值的 show 方法,以及预期的构造函数和析构函数。另一个有用的类是

    /****************************************************************************
    *                           class TraceFormatMessage
    * Inputs:
    *         UINT id: connection ID, or TraceEvent::None
    *         DWORD error: Error code from GetLastError()
    ****************************************************************************/
    
    class TraceFormatMessage: public TraceError {
    DECLARE_DYNAMIC(TraceFormatMessage)
    public:
    TraceFormatMessage(UINT id, DWORD error);
    virtual ~TraceFormatMessage() { }
    };
    

    使用该类

    我们很快将介绍 Event Log 类,但基本用法大致如下所示。请注意,在我上面显示的显示中,“连接 ID”在除一个消息之外的所有消息的左列中打印。任何与连接相关的消息都会打印其连接 ID,但与连接无关的事件使用特殊代码 TraceEvent::None 来指示不应打印。您对这些想法的解释可能需要不同类型的表示法,并且您的参数可能有所不同。

    c_Log.AddString(new TraceComment(TraceEvent::None, IDS_SAW_SOMETHING));
    

    或者,当您遇到 API 故障时,您可以执行类似以下操作:

    BOOL result = SomeAPIcall(...);
    if (!result)
    { /* failed */
        DWORD err = ::GetLastError();
        c_Log.AddString(new TraceError(TraceEvent::None, IDS_WHATEVER_FAILED));
        c_Log.AddString(new TraceFormatMessage(TraceEvent::None, err));
    } /* failed */
    

    类实现

    因为该类是 CObject 派生的,我必须进行适当的声明

    IMPLEMENT_DYNAMIC(TraceComment, TraceEvent)
    IMPLEMENT_DYNAMIC(TraceError, TraceEvent)
    IMPLEMENT_DYNAMIC(TraceFormatError, TraceError)
    

    然后我必须声明方法。最有趣的一个将是 show 方法。基类 TraceEvent 显示时间。

    CString TraceEvent::show()
    {
        CString s = time.Format(_T("%H:%M:%S  "));
        return s;
    }
    

    我使用一个单独的方法来显示线程 ID,原因我们将在查看显示逻辑时看到。

    每个方法然后将格式化其数据并将其附加到基本的 show 结果中

    CString TraceComment::show()
    {
        return TraceEvent::show() + comment;
    }
    

    (我不会显示所有细节,因为您可以在示例代码中自行阅读它们)。

    也许最有用的方法是 TraceFormatMessage 构造函数,它很好地显示了 API 的错误代码。它有一个很好的扩展,我也用它来报告 WinSock 错误。它使用 ::FormatMessage 调用来处理大多数错误,但并非 ::GetLastError 返回的所有值都可以通过 ::FormatMessage 解码。它只真正知道如何格式化几种消息。如果您的系统有一个提供错误字符串的 DLL,您可以扩展它来使用它。我做的扩展是,如果 ::FormatMessage 失败,我将错误代码用作字符串表索引。对于 WinSock,我只是将字符串与 WinSock 错误号一起放入字符串表中。当然,这里有一个风险:如果错误号恰好与表中已有的某个字符串对应怎么办?我通过要求所有此类错误消息都以一个特殊字符开头来处理这个问题,该字符不会作为普通文本中的第一个字符出现。我选择了“®”。

    TraceFormatMessage::TraceFormatMessage(UINT id, DWORD err) : TraceError(id)
    {
        LPTSTR s;
        if(::FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
    			FORMAT_MESSAGE_FROM_SYSTEM,
    			NULL,
    			err,
    			0,
    			(LPTSTR)&s,
    			0,
    			NULL) == 0)
        { /* failed */
    	    // See if it is a known error code
            CString t;
            t.LoadString(err); 
            if(t.GetLength() == 0 || t[0] != _T('®')) 
    	    { /* other error */ 
                CString fmt; 
                fmt.LoadString(IDS_UNKNOWN_ERROR); 
                t.Format(fmt, err, err);
            } /* other error */  
            else 
                if(t.GetLength() > 0 && t[0] == _T('®')) 
                { /* drop prefix */  
                    t = t.Mid(1); 
                } /* drop prefix */ <
            Error = t;
    	} /* failed */
        else
        { /* success */
            LPTSTR p = _tcschr(s, _T('\r'));
            if(p != NULL)
            { /* lose CRLF */
                *p = _T('\0');
            } /* lose CRLF */
            Error = s;
            ::LocalFree(s);
        } /* success */
    }
    

    跟踪列表类

    Trace List 类是 CListBox 的子类。要使用它,请在您的项目中放置一个 CListBox 控件,将其设置为所有者绘制变量,取消选中 Has Strings,并确保取消选中 Sort。选中 Horizontal Scroll 以支持滚动。选中 No Integral Height。请注意,类的 PreCreateWindow 方法将确保精确设置这些样式位。但是,如果您将控件放置在 CDialogCFormView 派生类中,则不会调用 PreCreateWindow,因为控件在子类化之前已创建。

    所有者绘制列表框部分属于“您的里程数可能有所不同”类别。绘制的细节取决于您。我提供的类包括 ID、线程 ID、时间戳和一些显示文本。它为整行设置单一颜色。但是一旦您看到基本结构,您应该能够快速进行主题和变体。

    使这个类比简单的 CListBox 更复杂的是保存文本的能力、项目数量的限制以及花式滚动。


    记录到磁盘

    磁盘保存可以在两种模式下工作。在第一种模式下,您发出 Save 命令,CListBox 的全部内容将保存到文件中。请注意,如果您设置了限制,例如 100 项,那么已从列表框中滚出的任何项都将永远丢失。在另一种模式下,您设置一个标志,导致每行一写入文件就立即保存。当然,这效率较低,但它有两个优点:

    • 所有字符串都出现在文件中,即使它们已从列表框中滚出
    • 如果程序崩溃,或者更糟的是,系统崩溃,您仍然可以查看日志中的内容,因为文件始终处于稳定状态。
    CTraceList::setToDisk(BOOL v)
    这会设置保存到磁盘的标志。如果设置为 TRUE,则写入日志的每一行都将写入文件。当设置为 FALSE 时,日志记录停止。请注意,在执行过程中,您应该只将其设置为 TRUE 一次;每次设置为 TRUE 时,它都会提示输入文件名并覆盖任何现有文件的内容。在调用它时,如果 TRUE,则列表框中已存在的任何内容都将写入文件。

    CTraceList::doSave(BOOL saveAs)
    这将强制执行保存到磁盘操作。控件会提示输入文件名,然后将控件的全部内容写入文件。如果 saveAsTRUE,则会提示输入文件名;如果 saveAsFALSE,则会覆盖之前写入的文件(或者如果之前没有文件,则会提示,这是常见的保存语义)。


    花式滚动

    这种控件的滚动很棘手。如果你正在观察消息的出现,你希望它们向上滚动,所以你有一个明显的跟踪输出例程。但是如果你已经滚动回去查看一条消息,你就不希望每当有新消息到来时控件就跳回到末尾。这个控件实现了花式滚动。如果新行添加之前最后一行是可见的,并且新行添加之后不可见或部分被遮挡,则控件会自动向上滚动。如果最后一行不可见,则添加新行后不会发生滚动。

    此外,此类别还支持到顶部、到底部和搜索操作,这些操作在左侧显示的控件所展示的对话框中实现

    第一个控件调用 void CTraceList::toTop(),它使列表框的第一行可见。您可以使用谓词 BOOL CTraceList::canTop() 来决定是否应该启用此按钮。最后一个控件调用 void CTraceList::toEnd(),它使控件的最后一行可见,并且 BOOL CTraceList::canEnd() 将告诉您是否正在显示最后一行。

    “<”和“>”按钮调用函数 int CTraceList::findNext(int start)int CTraceList::findPrev(int start) 以在日志中查找 TraceError 事件的下一个实例。返回的值是当前位置,并将传递给下一个调用。如果未在所需方向找到事件,则两个调用都返回 LB_ERR。更复杂的日志记录系统可能会采用要搜索的事件类型的 RUNTIME_CLASS;在此版本中,我已将 TraceError 连接起来。


    添加事件

    我们已经展示了添加事件的方式。CTraceList::AddString 方法重写了 CListBox::AddString。它不接受 LPCTSTR 参数,而是接受 TraceEvent * 参数。它实现了所有智能滚动。请注意,我们可以通过提供 CString 引用或字符串资源中的字符串的 UINT 来添加字符串。该函数还处理设置水平范围以允许水平滚动。

    请注意,一个事件可以显示在多行上。因此,您可以有一个事件显示在两行或三行上。这是基于虚拟方法 int displayHeight(),在 TraceEvent 中它将返回 1,但可以被任何需要多行的事件子类重写。

    c_Log.AddString(new TraceError(TraceEvent::None, IDS_WHATEVER_FAILED));
    c_Log.AddString(new TraceFormatMessage(TraceEvent::None, err));
    

    DrawItem 处理程序

    此方法利用了已提供的虚拟方法。它将其 ItemData 值转换为 TraceEvent * 事件,然后调用该类的虚拟方法以获取文本颜色和显示文本。为了创建更复杂的绘图方案,您可能希望向该类添加更多虚拟方法。以下是一些代码片段,可以帮助您了解这些虚拟方法的使用方式。

    void CTraceList::DrawItem(LPDRAWITEMSTRUCT dis) 
    {
        CDC * dc = CDC::FromHandle(dis->hDC);
        COLORREF bkcolor;
        COLORREF txcolor;
        ...
        int saved = dc->SaveDC();
        ...
        if(dis->itemState & ODS_SELECTED)
        { /* selected */
            if(::GetFocus() == m_hWnd)
            { /* has focus */
                bkcolor = ::GetSysColor(COLOR_HIGHLIGHT);
                txcolor = ::GetSysColor(COLOR_HIGHLIGHTTEXT);
            } /* has focus */
            else
            { /* no focus */
                bkcolor = ::GetSysColor(COLOR_BTNFACE);
                txcolor = ::GetSysColor(COLOR_BTNTEXT);
            } /* no focus */
        } /* selected */
        else
        { /* not selected */
            txcolor = e->textcolor();
            bkcolor = ::GetSysColor(COLOR_WINDOW);
        } /* not selected */
        ...
        CString id = e->showID();
        dc->TextOut(dis->rcItem.left, dis->rcItem.top, id);
    
        CString s = e->showThread();
        dc->TextOut(dis->rcItem.left + indent1, dis->rcItem.top, s);
    
        s = e->show();
        dc->TextOut(dis->rcItem.left + indent1 + indent2, dis->rcItem.top, s);
    
        if(dis->itemState & ODS_FOCUS)
            dc->DrawFocusRect(&dis->rcItem);
    
        dc->RestoreDC(saved);
    }
    

    变量 indent1indent2 的用法可以从示例代码中看出;假设它们表示制表位并已正确计算。虚拟方法在上面的示例中以粗体显示。请注意,此代码保持高亮可见,但如果控件没有焦点,则将其从正常背景更改为名义上的灰色背景。


    在 MDI 应用程序中使用它

    在 MDI 应用程序中使用它需要大量细致的编码问题。CTraceList 窗口是 CFormView 的成员,而 CFormViewCMDIChildWnd 的子窗口,它位于 MDI 客户端窗口内,而 MDI 客户端窗口又位于主框架内。这引入了一些额外的复杂性。我所做的是将这些内容很好地打包在一个示例中,该示例完成了所有复杂的工作。要查看我在 CWinApp 类中添加了什么,请在该文件中查找我的首字母缩写(JMN)。我还向 CMainFrame 类添加了一些示例测试代码。运行此代码并执行一些测试的结果如下所示。

    这是一个完全配置的实现,支持从标准菜单项调用的复制、剪切、删除、打印、保存和另存为操作。它还可以在打印机上打印数据;如果打印机是彩色打印机,您将看到与显示器相同的颜色输出。


    Tracer 参考手册

    CTraceEvent

    CTraceEvent::CTraceEvent(UINT id)
    此函数由子类作为其构造函数的一部分调用。它将出现在最左侧列的 id 值与事件关联。当用作 id 时,值 TraceEvent::None 将使最左侧列为空白。

    static CString TraceEvent::cvtext(CString s)
    这会将字符串转换为可打印形式。特别是,小于 ASCII“空格”且大于或等于 127 的字符将被转换为“\x”表示法。在 Unicode 模式下,如果高位 Unicode 字节不为零,它将转换字符串,以十六进制显示高位 Unicode 字节。由于这是一个静态方法,因此可以随时方便地调用它来转换字符串。

    static CString TraceEvent::cvtext(TCHAR ch)
    这会将单个字符转换为可打印形式。它由 cvtext 的字符串形式重复调用以转换每个字节。由于这是一个静态方法,因此可以随时方便地调用它来转换字符。

    virtual int TraceEvent::displayHeight()
    此方法仅针对需要多行显示的类进行重写。基类实现返回的默认值为 1,用于单行显示。该值始终表示条目的整数行数。

    virtual CFont * TraceEvent::getFont();
    此方法可以由子类重写,以允许为显示目的使用不同的字体。此功能目前未实现,并且始终返回 NULL

    virtual UINT TraceEvent::getID()
    此方法返回条目的 id 值。

    TraceEvent::None
    这用作构造函数的第一个参数,表示没有 id 值,并且显示的最左侧列应为空白。

    virtual CString TraceEvent::show()
    此方法由每个子类重写,以返回格式化的显示字符串。此方法在基类中返回一个空字符串。它可以选择修改以返回时间戳值。

    virtual CString TraceEvent::showfile()
    此方法返回一个字符串,用于计算剪贴板或写入文件的值。此格式包括一个格式化为 6 位十进制数字的序列号。这很有用,因为该数字是在创建条目时分配的,因此序列中的空白将表示删除。它通常不会被子类重写,但可以。

    virtual CString TraceEvent::showID()
    此方法通常不会被重写;它将 id 值格式化为十进制数字返回。如果 idTraceEvent::None,它会返回一个与它本应返回的十进制数字大小相同的空白字符串。

    virtual CString TraceEvent::showThread()
    此方法通常不会被重写;它返回一个格式化线程句柄。

    virtual COLORREF TraceEvent::textcolor()
    此方法必须由子类重写,以提供该子类所需的显示颜色。


    TraceComment

    TraceComment::TraceComment(UINT id, const CString & s)
    构造一个 TraceComment,其 id 如指定,内容由字符串 s 指定。

    TraceComment::TraceComment(UINT id, const UINT strid)
    构造一个 TraceComment,其 id 如指定,内容由 ID 为 strid 的字符串资源指定。


    TraceData

    TraceData::TraceData(UINT id, LPBYTE data, UINT length)
    创建一个 TraceData,其 id 如指定,内容是二进制数据字节,由 datalength 指定。数据将以空格分隔的十六进制字节形式显示。

    TraceData::TraceData(UINT id, LPCTSTR data)
    创建一个 TraceData,其 id 如指定,内容是由 data 指定的以 NUL 结尾的字符串。数据将使用 TraceData::cvtext 以文本数据形式显示。

    TraceData::TraceData(UINT id, const CString & data)
    创建一个 TraceData,其 id 如指定,内容是指定的 CString。数据将使用 TraceData::cvtext 以文本数据形式显示。


    TraceError

    TraceError::TraceError(UINT id, const CString & s)
    构造一个 TraceError,其 id 如指定,内容由字符串 s 指定。

    TraceError::TraceError(UINT id, UINT strid)
    构造一个 TraceError,其 id 如指定,内容由 ID 为 strid 的字符串资源指定。
    另请参阅: TraceFormatError


    TraceFormatComment

    TraceFormatComment::TraceFormatComment(UINT id, const CString & fmt, ...)
    构造一个 TraceComment,其 id 如指定,内容表示根据字符串 fmt 格式化的一组参数。

    TraceFormatComment::TraceFormatConnment(UINT id, UINT fmtid, ...)
    构造一个 TraceComment,其 id 如指定,内容是一组根据由 fmtid 表示的字符串资源格式化的参数。


    TraceFormatError

    TraceFormatError::TraceFormatError(UINT id, const CString & fmt, ...)
    构造一个 TraceError,其 id 如指定,内容表示根据字符串 fmt 格式化的一组参数。

    TraceFormatError::TraceFormatError(UINT id, UINT fmtid, ...)
    构造一个 TraceError,其 id 如指定,内容是一组根据由 fmtid 表示的字符串资源格式化的参数。


    TraceFormatMessage

    TraceFormatMessage::TraceFormatMessage(UINT id, err)
    构造一个 CTraceError,其 id 如指定,内容表示根据 ::FormatMessage(..., FORMAT_MESSAGE_FROM_SYSTEM, ...) 格式化的错误代码 err


    CTraceList

    int CTraceList::AddString(TraceEvent * event)
    TraceEvent 附加到日志窗口。如果这将导致日志窗口中的项目数超出 CTraceList::setLimit 设置的限制,则将删除从 boundary 值之后(请参阅 CTraceList::setBoundary)开始的若干项目,直到有空间插入新项目。

    BOOL CTraceList::canClearAll()
    如果列表中有任何元素,则返回 TRUE。您可以在子类中重写此方法以始终返回 FALSE 以支持只读列表。

    BOOL CTraceList::canCopy()
    如果可以执行复制操作,则返回 TRUE。为此,列表中必须至少选择一个项目。

    BOOL CTraceList::canCut()
    如果可以执行剪切操作,则返回 TRUE。为此,列表中必须至少选择一个项目。您可以在子类中重写此方法,通过始终返回 FALSE 来支持只读控件。

    BOOL CTraceList::canDelete()
    如果可以执行删除操作,则返回 TRUE。您可以在子类中重写此方法,以始终返回 FALSE 来支持只读控件。

    BOOL CTraceList::canEnd()
    如果列表中有多个项目,则返回 TRUE。通常用于确定是否应启用“到末尾”按钮。

    BOOL CTraceList::canNext()
    如果光标所在项目后面有 CTraceError 项目,则返回 TRUE,否则返回 FALSE。通常用于确定是否应启用“下一个错误”按钮。

    BOOL CTraceList::canPrev()
    如果光标所在项目前面有 CTraceError 项目,则返回 TRUE,否则返回 FALSE。通常用于确定是否应启用“上一个错误”按钮。

    BOOL CTraceList::canPrint()
    如果列表不为空,则返回 TRUE

    BOOL CTraceList::canSave()
    如果列表不为空且自上次保存以来已修改,则返回 TRUE

    BOOL CTraceList::canSaveAs()
    如果列表不为空,则返回 TRUE

    BOOL CTraceList::canSelectAll()
    如果列表不为空,则返回 TRUE

    BOOL CTraceList::canTop()
    如果列表中有多个项目,则返回 TRUE。通常用于确定是否应启用“到开头”按钮。

    BOOL CTraceList::Copy()
    如果选择了任何项目,它们将根据 CTraceEvent::showfile() 方法进行格式化,并以 CF_TEXT 格式放置到剪贴板中。如果未选择任何项目或无法访问剪贴板,则返回 FALSE。通常由视图中的 OnCopy 处理程序调用。

    BOOL CTraceList::Cut()
    如果选择了任何项目,它们将像在 CTraceList::Copy 中一样复制到剪贴板,然后被删除。如果任何操作未能成功将它们放置到剪贴板上,则它们不会被删除。通常由视图中的 OnCut 处理程序调用。

    BOOL CTraceList::Delete()
    如果选择了任何项目,它们将被删除。如果删除了任何项目,则返回 TRUE。通常由 Del 键或清除菜单项的处理程序调用。

    int CTraceList::DeleteString(int n)
    从列表中删除元素 n。请注意,n 不限于在 boundary 之上(请参阅 CTraceList::setBounary)。

    BOOL CTraceList::doSave(BOOL saveas)
    如果 saveas 参数为 TRUE,或者没有先前保存的文件名,则提示用户输入文件名。如果 saveas 参数为 FALSE 并且存在先前的保存操作,则重新写入文件。文件总是被覆盖,而不是附加。控件的内容将根据虚拟 CTraceEvent::fileshow() 方法写入文件。

    void CTraceList::enableLimit(BOOL enable)
    启用限制。这将强制删除超出限制的任何元素,直到计数符合限制。限制始终是超出 boundary 的某个最小大小。

    void CTraceList::Print()
    提示用户选择打印机,然后根据用户选择的操作,打印选定的项目或整个列表。

    void CTraceList::SelectAll()
    选择列表的所有元素。通常由视图的全选/Ctrl+A处理程序调用。

    void CTraceList::setBoundary(int boundary)
    设置一个边界,在该边界以下不会删除任何元素以在超出指定限制时为其他元素腾出空间。通常,这用于记录基本参数且不应丢失的启动之后。默认边界为 -1,表示没有边界,列表中的第一个元素将被删除以腾出空间。

    void CTraceList::SetFont(CFont * font)
    这将为整个显示设置默认字体。单独的跟踪项目可以提供自己的字体(此功能未实现)。在没有指定字体的情况下,将使用此默认字体进行显示。

    void CTraceList::setLimit(int limit)
    设置列表框的限制,使其包含不超过 limit 个元素。尝试将超过 limit 个元素放入列表将导致删除较早的一个元素。如果未设置 boundary(请参阅 setBoundary),则将删除列表的第一个元素以腾出空间。如果设置了 boundary,则将删除 boundary+1st 个元素以腾出空间。如果 limit 的值为负,则禁用限制但限制编号不变(这与调用 enableLimit(FALSE) 相同)。如果限制为正,则设置它并执行 enableLimit(TRUE)。但是,在新元素添加之前不会删除元素。调用 enableLimit(TRUE) 将强制执行限制。限制不能设置得“太小”。有一个最小大小;如果设置了边界,则限制是超出边界的最小量。

    void CTraceList::setShowThreadID(BOOL show)
    如果使用 FALSE 调用此函数,则不会显示线程 ID。默认情况下会显示线程 ID。

    BOOL CTraceList::setToDisk(BOOL enable)
    启用直接到磁盘的日志记录。在此模式下,每个 AddString 操作都会立即将项目写入磁盘文件。每次调用此函数,当 enableFALSE 变为 TRUE 时,都会提示用户输入文件名。指定的文件名将替换任何已存在的文件。如果用户取消文件对话框或打开文件时出错,此函数将返回 FALSE。在此模式下,文件会反复打开和关闭,从而确保(在底层系统文件缓存允许的范围内)即使程序崩溃甚至系统崩溃,文件内容也会在磁盘上并且保持一致。

    BOOL CTraceList::toEnd()
    移动到列表中的最后一个项目,使其可见并选择它。所有现有选择都将被删除。结果为 TRUE。通常由“转到末尾”按钮的处理程序调用。

    BOOL CTraceList::toNext()
    从当前插入符位置向前移动到列表中的下一个 TraceError 对象,使其可见并选择它。任何现有选择都将被删除。如果找到这样的条目,则结果为 TRUE,如果找不到这样的条目,则结果为 FALSE。通常由“下一个错误”按钮的处理程序调用。

    BOOL CTraceList::toPrev()
    从当前插入符位置向后移动到列表中的上一个 TraceError 对象,使其可见并选择它。任何现有选择都将被删除。如果找到这样的条目,则结果为 TRUE,如果找不到这样的条目,则结果为 FALSE。通常由“上一个错误”按钮的处理程序调用。

    BOOL CTraceList::toTop()
    移动到列表中的第一个项目,使其可见并选择它。所有现有选择都将被删除。结果为 TRUE。通常由“转到开头”按钮的处理程序调用。


    代码下载

    一个完整的项目,展示了这些类在 MDI 下的使用,可以下载。它是在 VC++ 6.0 下编译的。


    这些文章中表达的观点是作者的观点,不代表,也不被微软认可。

    发送邮件至newcomer@flounder.com提出关于本文的问题或评论。
    版权所有 © 2000,CompanyLongName保留所有权利
    www.flounder.com/mvp_tips.htm
  • © . All rights reserved.