更改绘图代码(ATL 教程,第 4 部分)

默认情况下,控件的绘图代码会显示一个正方形和文本 PolyCtl。 在此步骤中,你将更改代码以显示更有趣的内容。 涉及以下任务:

  • 修改头文件

  • 修改 OnDraw 函数

  • 添加用于计算多边形点的方法

  • 初始化填充颜色

修改头文件

首先添加对数学函数 sincos(用于计算多边形点)的支持,并创建一个数组来存储位置。

修改头文件

  1. #include <math.h> 行添加到 PolyCtl.h 的顶部。 该文件的顶部应如下所示:

    #include <math.h>
    #include "resource.h"       // main symbols
    
  2. 通过将以下代码添加到 PolyCtl.h,实现 IProvideClassInfo 接口以提供控件的方法信息。 在 CPolyCtl 类中,将以下行:

    public CComControl<CPolyCtl>
    

    替换为

    public CComControl<CPolyCtl>,
    public IProvideClassInfo2Impl<&CLSID_PolyCtl, &DIID__IPolyCtlEvents, &LIBID_PolygonLib>
    

    BEGIN_COM_MAP(CPolyCtl) 中添加以下行:

    COM_INTERFACE_ENTRY(IProvideClassInfo)
    COM_INTERFACE_ENTRY(IProvideClassInfo2)
    
  3. 计算多边形点后,它们将存储在 POINT 类型的数组中,因此请在 PolyCtl.h 中的定义语句 short m_nSides; 之后添加该数组:

    POINT m_arrPoint[100];
    

修改 OnDraw 方法

现在应该修改 PolyCtl.h 中的 OnDraw 方法。 要添加的代码将创建用于绘制多边形的新笔和画笔,然后调用 EllipsePolygon Win32 API 函数来执行实际绘制。

修改 OnDraw 函数

  1. 将 PolyCtl.h 中的现有 OnDraw 方法替换为以下代码:

    HRESULT CPolyCtl::OnDraw(ATL_DRAWINFO& di)
    {
       RECT& rc = *(RECT*)di.prcBounds;
       HDC hdc  = di.hdcDraw;
    
       COLORREF    colFore;
       HBRUSH      hOldBrush, hBrush;
       HPEN        hOldPen, hPen;
    
       // Translate m_colFore into a COLORREF type
       OleTranslateColor(m_clrFillColor, NULL, &colFore);
    
       // Create and select the colors to draw the circle
       hPen = (HPEN)GetStockObject(BLACK_PEN);
       hOldPen = (HPEN)SelectObject(hdc, hPen);
       hBrush = (HBRUSH)GetStockObject(WHITE_BRUSH);
       hOldBrush = (HBRUSH)SelectObject(hdc, hBrush);
    
       Ellipse(hdc, rc.left, rc.top, rc.right, rc.bottom);
    
       // Create and select the brush that will be used to fill the polygon
       hBrush    = CreateSolidBrush(colFore);
       SelectObject(hdc, hBrush);
    
       CalcPoints(rc);
       Polygon(hdc, &m_arrPoint[0], m_nSides);
    
       // Select back the old pen and brush and delete the brush we created
       SelectObject(hdc, hOldPen);
       SelectObject(hdc, hOldBrush);
       DeleteObject(hBrush);
    
       return S_OK;
    }
    

添加用于计算多边形点的方法

添加名为 CalcPoints 的方法,用于计算构成多边形周长的点的坐标。 这些计算基于传递给函数的 RECT 变量。

添加 CalcPoints 方法

  1. CalcPoints 的声明添加到 PolyCtl.h 中 CPolyCtl 类的 IPolyCtl 公共节:

    void CalcPoints(const RECT& rc);
    

    CPolyCtl 类的公共节的最后一部分如下所示:

       void FinalRelease()
       {
       }
    public:
       void CalcPoints(const RECT& rc);
    
  2. CalcPoints 函数的此实现添加到 PolyCtl.cpp 的末尾:

    void CPolyCtl::CalcPoints(const RECT& rc)
    {
       const double pi = 3.14159265358979;
       POINT   ptCenter;
       double  dblRadiusx = (rc.right - rc.left) / 2;
       double  dblRadiusy = (rc.bottom - rc.top) / 2;
       double  dblAngle = 3 * pi / 2;          // Start at the top
       double  dblDiff  = 2 * pi / m_nSides;   // Angle each side will make
       ptCenter.x = (rc.left + rc.right) / 2;
       ptCenter.y = (rc.top + rc.bottom) / 2;
    
       // Calculate the points for each side
       for (int i = 0; i < m_nSides; i++)
       {
          m_arrPoint[i].x = (long)(dblRadiusx * cos(dblAngle) + ptCenter.x + 0.5);
          m_arrPoint[i].y = (long)(dblRadiusy * sin(dblAngle) + ptCenter.y + 0.5);
          dblAngle += dblDiff;
       }
    }
    

初始化填充颜色

使用默认颜色初始化 m_clrFillColor

初始化填充颜色

  1. 通过将以下行添加到 PolyCtl.h 中的 CPolyCtl 构造函数,使用绿色作为默认颜色:

    m_clrFillColor = RGB(0, 0xFF, 0);
    

该构造函数现在如下所示:

CPolyCtl()
{
   m_nSides = 3;
   m_clrFillColor = RGB(0, 0xFF, 0);
}

生成和测试控件

重新生成控件。 如果 PolyCtl.htm 文件仍处于打开状态,请确保将它关闭,然后在“生成”菜单中单击“生成多边形”。 可以从 PolyCtl.htm 页再次查看该控件,但这次请使用 ActiveX 控件测试容器。

使用 ActiveX 控件测试容器

  1. 生成并启动 ActiveX 控件测试容器。 可以在 GitHub 上找到 TSTCON 示例:ActiveX 控件测试容器

    注意

    对于涉及 ATL::CW2AEX 的错误,请在 Script.Cpp 中将行 TRACE( "XActiveScriptSite::GetItemInfo( %s )\n", pszNameT ); 替换为 TRACE( "XActiveScriptSite::GetItemInfo( %s )\n", pszNameT.m_psz );,将行 TRACE( "Source Text: %s\n", COLE2CT( bstrSourceLineText ) ); 替换为 TRACE( "Source Text: %s\n", bstrSourceLineText );
    对于涉及 HMONITOR 的错误,请在 TCProps 项目中打开 StdAfx.h,并将:

    #ifndef WINVER
    #define WINVER 0x0400
    #endif
    

    替换为

    #ifndef WINVER
    #define WINVER 0x0500
    #define _WIN32_WINNT 0x0500
    #endif
    
  2. 在“测试容器”的“编辑”菜单中,单击“插入新控件”

  3. 找到名为 PolyCtl class 的控件,然后单击“确定”。 你将看到一个包含在圆圈中的绿色三角形。

尝试遵循以下过程更改边数。 若要从“测试容器”内部修改双重接口的属性,请使用“调用方法”

从测试容器内部修改控件的属性

  1. 在“测试容器”的“控件”菜单中单击“调用方法”

    此时会显示“调用方法”对话框

  2. 从“方法名称”下拉列表框中选择“边”属性的“PropPut”版本

  3. 在“参数值”框中键入 5,单击“设置值”,然后单击“调用”

请注意,控件不会更改。 尽管通过设置 m_nSides 变量在内部更改了边数,但这不会导致控件重绘。 如果切换到另一应用程序,然后再切换回“测试容器”,你会发现,控件已重绘并具有正确的边数

若要更正此问题,请在设置边数后,添加对 IViewObjectExImpl 中定义的 FireViewChange 函数的调用。 如果控件在其自身的窗口中运行,FireViewChange 将直接调用 InvalidateRect 方法。 如果控件以无窗口的形式运行,则会对容器的站点接口调用 InvalidateRect 方法。 这会强制控件重绘自身。

添加对 FireViewChange 的调用

  1. 通过将 FireViewChange 调用添加到 put_Sides 方法来更新 PolyCtl.cpp。 完成后,put_Sides 方法应如下所示:

    STDMETHODIMP CPolyCtl::put_Sides(short newVal)
    {
       if (2 < newVal && newVal < 101)
       {
          m_nSides = newVal;
          FireViewChange();
          return S_OK;
       }
       else
       {
          return Error(_T("Shape must have between 3 and 100 sides"));
       }
    }
    

添加 FireViewChange 后,在 ActiveX 控件测试容器中重新生成并再次尝试打开该控件。 这一次,当你更改边数并单击 Invoke 时,应会立即看到控件的变化。

在下一步骤中,你将添加一个事件。

返回步骤 3 | 继续执行步骤 5

另请参阅

教程
使用测试容器测试属性和事件