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

CStatusGraphCtrl - CWnd 的 C++ 包装类,用于以图形方式显示状态

starIconstarIconstarIconstarIconemptyStarIcon

4.00/5 (9投票s)

2004年5月2日

4分钟阅读

viewsIcon

105095

downloadIcon

4814

C++ 源文件,用于创建一个控件,该控件以图形方式显示应用程序变量的当前状态。

引言

本文档解释了如何将我的图形状态显示控件 (CStatusGraphCtrl) 集成到您的应用程序中。许多实时应用程序都有一个需求,即向用户更新与该应用程序相关的一些关键参数的当前值;CStatusGraphCtrl 不仅显示值,还可以将参数变化的过程显示为图形。目前,它支持条形图和折线图显示。如果需要,您可以自定义代码以支持您自己的图形显示方式。该控件与您在 Windows 任务管理器中看到的显示 CPU 和内存使用情况的控件完全相同。

Top - Bar Graph///Bottom - Line Graph

使用代码

CStatusGraphCtrl 源文件 (zip) 将包含以下文件

  • CGraphData.h
  • CGraphData.cpp
  • CStatusGraphCtrl.h
  • CStatusGraphCtrl.cpp

这四个文件是该控件的核心文件。将这四个文件添加到您的 VC++ 应用程序项目中。

CGraphData 类充当控件视口中当前数据的保存者。数据以线性单向链表的形式存储。

CStatusGraphCtrl 类是核心控件类,它派生自 CWnd MFC 类。要在对话框中创建 CStatusGraphCtrl 控件,请在对话框的 CDialog::OnCreate 中调用 CStatusGraphCtrl::Create,如下所示:

m_ctrlStatusGraph.Create("Demo Control",/* Caption - ofcourse not used */
            WS_CHILD|WS_VISIBLE, /* Windows styles */
            CRect(10,10,350,200), /* Position, size */
            static_cast<CWnd*>(this), /* Parent */ 
            GRAPH_CTRL_ID /* ID that u assign for this control */
            );

这将根据所需位置创建一个 StatusGraphCtrl。控件创建后,您需要使用以下方法指定控件的几个属性值:

  • SetMinValue
  • SetMaxValue
  • SetSamplingInterval

调用 SetMinValue 来指定控件视口中最底部的线的值。调用 SetMaxValue 来指定控件视口中最顶部的线的值。调用 SetSamplingInterval 来指定连续绘图点之间的水平距离(以像素为单位)。一切就绪后,调用 CStatusGraphCtrl::StartUpdate 使控件开始运行。调用 StartUpdate 后,任何对 CStatusGraphCtrl::SetCurrentValue 的调用都将反映在控件视口中看到的图形上。

图形从右向左滚动。每次滚动的延迟可以通过调用 CStatusGraphCtrl::SetRefreshDelay 并将所需的延迟(以毫秒为单位)作为参数传入来控制。

以下代码块摘自我的演示应用程序,将提供一个简单的代码来在您的应用程序中使用 CStatusGraphCtrl 控件。

void DataGenerator(LPVOID param)
{
    CStatusGraphCtrl* pCtrl = (CStatusGraphCtrl*)param;
    while(true)
    {
        int nRand = rand();
        while(nRand > pCtrl->GetMaxValue())
            nRand /= 10;
        pCtrl->SetCurrentValue(nRand); 
        /* this value will be reflected in 
        the graph in control's viewport */

        Sleep(50);
    }
}

int CDemoDialog::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
    // m_ctrlStatusGraph is a member of type CStatusGraphCtrl

    m_ctrlStatusGraph.Create("Demo Control",
        WS_CHILD|WS_VISIBLE,CRect(10,10,350,200),
        static_cast<CWnd*>(this),GRAPH_CTRL_ID);
    m_ctrlStatusGraph.SetMaxValue(100);
    m_ctrlStatusGraph.SetMinValue(0);
    m_ctrlStatusGraph.StartUpdate();
    AfxBeginThread((AFX_THREADPROC)DataGenerator,
                      (LPVOID)&m_ctrlStatusGraph);
    return CDialog::OnCreate(lpCreateStruct);
}

自定义 CStatusGraphCtrl

CStatusGraphCtrl 的几乎所有方面都可以自定义。以下是您可以根据需要自定义的属性列表:

  • SamplingInterval - 调用 SetSamplingInterval(short) - 连续绘图点之间的水平距离(以像素为单位)
  • RefreshDelay - 调用 SetRefreshDelay(int) - 连续滚动之间的延迟(以毫秒为单位)
  • 背景颜色 - 访问公共成员 COLORREF m_BackColor
  • 前景颜色 - 访问公共成员 COLORREF m_ForeColor

添加自定义 GraphModes

要添加您自己的 GraphMode,您只需要两个步骤。更新 CStatusGraphCtrl.h 头文件中的 enum StatusGraphType 以添加您新的 GraphType 条目。然后,在 CStatusGraphCtrl.cpp 文件中的 DrawPoint 函数中添加绘制图形的代码。请看这段代码片段:

void DrawPoint(CStatusGraphCtrl* TheCtrl,long int cury,bool Update)
{
    // control specific code will be here...
    // ..
    // ..

    switch(TheCtrl->GetGraphMode())
    {
    case BAR_GRAPH:
        pdc->MoveTo(rcBounds.right-sSamplingInterval,rcBounds.bottom);
        pdc->LineTo(rcBounds.right-sSamplingInterval,rcBounds.bottom-cury);
        break;
    case LINE_GRAPH:
        pdc->MoveTo(rcBounds.right-2*sSamplingInterval,
                rcBounds.bottom-TheCtrl->m_lPreviousY);
        pdc->LineTo(rcBounds.right-sSamplingInterval,rcBounds.bottom-cury);
        break;
    //**************************************************************************//
    // TODO:
    // Update the enum type in the header (CStatusGraphCtrl.h) file accordingly
    //    Add additional case blocks to support your own graph modes
    //    cury - represents the current value to plot
    //    TheCtrl->m_lPreviousY - represents the previous point plotted.
    //    rcBounds - represent the bounds of the CStatusGraphCtrl Control.
    //*************************************************************************//

    case CUSTOM_GRAPH:
        // do something with cury,TheCtrl->m_lPreviousY,rcBounds as required.
        break;
    }

    // control specific code will be here...
    // ..
    // ..
}

事件

CStatusGraphCtrl 的编写方式是在鼠标单击控件时发送 WM_COMMAND 消息。应用程序可以根据需要利用这一点。例如,在我的演示应用程序中,我在控件的 ON_COMMAND 处理程序中添加了更改 CStatusGraphCtrl 的图形模式的功能。请参阅处理程序代码:

BEGIN_MESSAGE_MAP(CDemoDialog, CDialog)
    // .. other message maps..
    // ..
    ON_COMMAND(GRAPH_CTRL_ID,OnClickGraph)
END_MESSAGE_MAP()

BOOL CDemoDialog::OnClickGraph()
{
    // toggles the graph mode between BAR_GRAPH (0) and LINE_GRAPH (1)
    m_ctrlStatusGraph.SetGraphMode((StatusGraphType)
              !(int)m_ctrlStatusGraph.GetGraphMode());
    return TRUE;
}

关注点

我将告诉您我是如何编写这段代码的。我当时正在编写一个网络实用程序,用于监控网络流量并控制来自单个源的过多流量,在此过程中我需要一个这样的控件。我从 Button 的 BS_OWNERDRAW 功能开始,并愉快地使用 CDC 来满足需求。此代码使用 CDC::BitBlt 来执行图形滚动,因此速度非常快且无闪烁。我几乎完成了,并检查了控件的各种功能。直到那时,我还没有尝试在我的应用程序运行时切换应用程序。问题就出在我切换回我的应用程序时,整个图形都消失了!!!!糟糕,我的内存中没有必要的信息来重绘图形。那时我添加了一个新类 CGraphData,它存储与控件当前视口相关的数据。数据结构仅存储点序列,而不存储整个视口像素矩阵。实际上,滚动也发生在数据结构中。每当右侧有新值时,左侧的值就会被删除,从而保持窗口大小。

感谢 CodeProject 的成员:在我提交这篇文章的第一天,我收到了关于我的代码的反馈;从这些反馈中,我感觉到使用 CButton 来实现这个控件根本没有必要;我立即修改了代码,并在那里发布了。现在,该控件不再使用 CButton。它只是一个窗口。

历史

  • CStatusGraphCtrl 的第一个版本是从 CButton 派生的。
  • 根据 CodeProject 成员的反馈,我修改了代码,使其派生自 CWnd
© . All rights reserved.