Share via


Step 4: Changing the Drawing Code

In the drawing code you will use sin and cos functions to calculate the polygon points, so add include math.h at the top of PolyCtl.h:

#include <math.h>
#include "resource.h"       // main symbols

Note for Release builds only   When the ATL COM AppWizard generates the default project, it defines the macro _ATL_MIN_CRT. This macro is defined so that you don't bring the C Run-Time Library into your code if you don't need it. The polygon control needs the C Run-Time Library start-up code to initialize the floating-point functions. Therefore, you need to remove the _ATL_MIN_CRT macro if you want to build a Release version. To remove the macro, click Settings on the Project menu. In the Settings For: drop-down list, choose Multiple Configurations. In the Select project configuration(s) to modify dialog box that appears, click the check boxes for all four Release versions, then click OK. On the C/C++ tab, choose the General category, then remove _ATL_MIN_CRT from the Preprocessor definitions edit box.

Once the polygon points are calculated, you store the points by adding an array of type POINT to the end of the class definition in PolyCtl.h:

   OLE_COLOR m_clrFillColor;
   short m_nSides;
   POINT m_arrPoint[100];

Now change the OnDraw function in PolyCtl.h to match the one below. Note that you remove the calls to the Rectangle and DrawText functions. You also explicitly get and select a black pen and white brush. You need to do this in case your control is running windowless. If you don't have your own window, you can't make assumptions about the device context you'll be drawing in.

The completed OnDraw looks like this:

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;
}

You now need to add a function, called CalcPoints, that will calculate the coordinates of line intersections.  These calculations will be based on the RECT variable that is passed into the function.  First you should add the definition of CalcPoints to the public section for the CPolyCtl class in PolyCtl.h:

void    CalcPoints(const RECT& rc);

The public section of the CPolyCtl class should now look like this:

// IPolyCtl
public:
   STDMETHOD(get_Sides)(/*[out, retval]*/ short *newVal);
   STDMETHOD(put_Sides)(/*[in]*/ short newVal);
   void    CalcPoints(const RECT& rc);

Next, add the implementation of the CalcPoints function to the end of the PolyCtl.cpp file:

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;
   }
}

Now, initialize m_clrFillColor. Choose green as the default color and add this line to the CPolyCtl constructor in PolyCtl.h:

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

The constructor now looks like this:

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

Now rebuild the control and try it again. Open ActiveX Control Test Container and insert the control. You should see a green triangle within a circle. Try changing the number of sides by following the steps below. To modify properties on a dual interface from within Test Container, use Invoke Methods:

  1. In Test Container, click Invoke Methods on the Control menu.
    The Invoke Method dialog box is displayed.

  2. Click the PropPut version of the Sides property from the Method Name.

  3. Type 5 in the Parameter Value edit box, click Set Value and click Invoke.

Notice that the control doesn't change. What is wrong? Although you changed the number of sides internally by setting the m_nSides variable, you didn't cause the control to repaint. If you switch to another application and then switch back to Test Container you will find that the control is repainted and now has the correct number of sides.

To correct this problem, you need to add a call to the FireViewChange function, which is defined in IViewObjectExImpl, after you set the number of sides. If the control is running in its own window, FireViewChange will call the InvalidateRect API directly. If the control is running windowless, the InvalidateRect method will be called on the container's site interface. This forces the control to repaint itself.

The new put_Sides method is as follows:

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

After you've added FireViewChange, rebuild and try the control again. This time when you change the number of sides and click Invoke, you should see the control change immediately.

Next, you will add an event to the control.

Back to Step 3On to Step 5