Changing the Drawing Code (ATL Tutorial, Part 4)
By default, the control's drawing code displays a square and the text PolyCtl. In this step, you will change the code to display something more interesting. The following tasks are involved:
Modifying the Header File
Modifying the OnDraw Function
Adding a Method to Calculate the Polygon Points
Initializing the Fill Color
Modifying the Header File
Start by adding support for the math functions sin and cos, which will be used calculate the polygon points, and by creating an array to store positions.
To modify the header file
Add the line #include <math.h> to the top of PolyCtl.h. The top of the file should look like this:
#include <math.h> #include "resource.h" // main symbols
Once the polygon points are calculated, they will be stored in an array of type POINT, so add the array after the definition of m_nSides in PolyCtl.h:
POINT m_arrPoint[100];
Modifying the OnDraw Method
Now you should modify the OnDraw method in PolyCtl.h. The code you will add creates a new pen and brush with which to draw your polygon, and then calls the Ellipse and Polygon Win32 API functions to perform the actual drawing.
To modify the OnDraw function
Replace the existing OnDraw method in PolyCtl.h with the following code:
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; }
Adding a Method to Calculate the Polygon Points
Add a method, called CalcPoints, that will calculate the coordinates of the points that make up the perimeter of the polygon. These calculations will be based on the RECT variable that is passed into the function.
To add the CalcPoints method
Add the declaration of CalcPoints to the IPolyCtl public section of the CPolyCtl class in PolyCtl.h:
void CalcPoints(const RECT& rc);
The last part of the public section of the CPolyCtl class will look like this:
void FinalRelease() { } public: STDMETHOD(get_Sides)(short* pVal); STDMETHOD(put_Sides)(short newVal); void CalcPoints(const RECT& rc);
Add this implementation of the CalcPoints function to the end of 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; } }
Initializing the Fill Color
Initialize m_clrFillColor with a default color.
To initialize the fill color
Use green as the default color by adding 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);
}
Building and Testing the Control
Rebuild the control. Make sure the PolyCtl.htm file is closed if it is still open, and then click Build Polygon on the Build menu. You could view the control once again from the PolyCtl.htm page, but this time use the ActiveX Control Test Container.
To use the ActiveX Control Test Container
Build and start the ActiveX Control Test Container. For more information, see TSTCON Sample: ActiveX Control Test Container.
In Test Container, on the Edit menu, click Insert New Control.
Locate your control, which will be called PolyCtl Class, and click OK. You will see a green triangle within a circle.
Try changing the number of sides by following the next procedure. To modify properties on a dual interface from within Test Container, use Invoke Methods.
To modify a control's property from within the Test Container
In Test Container, click Invoke Methods on the Control menu.
The Invoke Method dialog box is displayed.
Select the PropPut version of the Sides property from the Method Name drop-down list box.
Type 5 in the Parameter Value box, click Set Value, and click Invoke.
Note that the control does not change. Although you changed the number of sides internally by setting the m_nSides variable, this did not cause the control to repaint. If you switch to another application and then switch back to Test Container, you will find that the control has repainted and has the correct number of sides.
To correct this problem, add a call to the FireViewChange function, defined in IViewObjectExImpl, after you set the number of sides. If the control is running in its own window, FireViewChange will call the InvalidateRect method 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.
To add a call to FireViewChange
Update PolyCtl.cpp by adding the call to FireViewChange to the put_Sides method. When you have finished, the put_Sides method should look like this:
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")); } }
After adding FireViewChange, rebuild and try the control again in the ActiveX Control Test Container. This time when you change the number of sides and click Invoke, you should see the control change immediately.
In the next step, you will add an event.
See Also
Tasks
Testing Properties and Events with Test Container