Chapter 30 Exercises: Drawing, Graphics, and Custom Controls
Part A: Conceptual Understanding
A.1. Why must all drawing be done inside the OnPaint event handler? What happens if you draw on a canvas in a button click handler instead?
Guidance
The operating system can request a repaint at any time — when the window is uncovered, resized, or when another window moves away. During a repaint, the control is cleared and OnPaint is called. If you draw in a button click handler, your drawing appears temporarily but is erased on the next repaint because OnPaint does not reproduce it. The rule: drawing that should persist must be done in OnPaint, with the data driving the drawing stored in variables.A.2. Explain the difference between Invalidate and drawing directly on the canvas. Why is Invalidate preferred?
Guidance
Invalidate marks the control as needing a repaint and lets the event loop call OnPaint at the right time. Drawing directly on the canvas produces immediate output but it may be overwritten by a pending repaint, and it bypasses double-buffering. Invalidate also batches multiple changes — if you change 5 properties and call Invalidate once, the control repaints once (not 5 times).
A.3. What is double buffering and why is it important for animation? Describe what the user sees without it.
Guidance
Without double buffering, each frame clears the screen (white flash) and then draws the new content. The user sees the blank state between frames, creating a flicker effect. Double buffering draws the frame on an off-screen bitmap first, then copies the complete bitmap to the screen in one operation. The user never sees the intermediate blank state, resulting in smooth animation.Part B: Exploration and Analysis
B.1. Create a TPaintBox and draw the following in its OnPaint handler: a coordinate system with axes, grid lines, and labels. The X-axis should go from 0 to 10, and the Y-axis should go from 0 to 100. Draw a function like y = x^2 as a smooth curve. This exercises coordinate transformation (mapping data coordinates to pixel coordinates).
B.2. Explore all pen styles (psSolid, psDash, psDot, psDashDot, psDashDotDot) and brush styles (bsSolid, bsClear, bsHorizontal, bsVertical, bsFDiagonal, bsBDiagonal, bsCross, bsDiagCross) by drawing a grid of rectangles, each with a different combination. Label each one.
B.3. Load a PNG image into a TBitmap and draw it on a TPaintBox at different scales (50%, 100%, 200%) using StretchDraw. What happens to image quality at 200%? What happens at 50%?
Part C: Coding Challenges
C.1. Color Gradient (Easy) Draw a horizontal color gradient from blue to red across a TPaintBox. For each column of pixels, calculate the interpolated RGB values and draw a vertical line. The left edge should be pure blue (0, 0, 255) and the right edge should be pure red (255, 0, 0).
Hints
For column X in a PaintBox of width W:R := Round(255 * X / W); B := Round(255 * (W - X) / W); G := 0;. Draw vertical lines with Canvas.Pen.Color := RGBToColor(R, G, B); Canvas.MoveTo(X, 0); Canvas.LineTo(X, Height);.
C.2. Analog Clock (Intermediate) Draw an analog clock face with hour markers, minute markers, and three hands (hour, minute, second). Use a TTimer (1-second interval) to update the second hand. The hour and minute hands should also move smoothly.
Hints
The angle for the second hand isSecondAngle := (Second * 6) - 90 degrees (offset by -90 because 0 degrees is at 3 o'clock, but 12 o'clock should be the top). Convert to radians. Calculate hand endpoints with X := CenterX + Round(Length * Cos(Angle)); Y := CenterY + Round(Length * Sin(Angle)). Draw markers at 5-degree intervals around the circle.
C.3. Simple Paint Program (Intermediate) Build a paint application with a TPaintBox. The user draws by holding the mouse button and moving (handle OnMouseDown, OnMouseMove, OnMouseUp). Add a color palette (row of colored panels), line width slider (TTrackBar), and a Clear button. Store the drawn strokes in a list so they persist across repaints.
Hints
Store each stroke as a record: color, width, and a dynamic array of TPoint. In OnMouseDown, start a new stroke. In OnMouseMove (when mouse is down), append points. In OnPaint, replay all strokes by iterating the list and drawing lines between consecutive points.C.4. Horizontal Stacked Bar Chart (Advanced) Extend the bar chart from the chapter to support stacked bars — each bar is divided into colored segments showing the contribution of each category. Apply this to PennyWise: each month's bar shows the breakdown by category.
C.5. PennyWise Dashboard (Advanced) Create a comprehensive dashboard for PennyWise with four panels: pie chart (by category), bar chart (by month), line chart (running daily total), and a summary panel (total, average per day, top category, number of expenses). All four panels should update when expenses change.
Part M: Metacognitive Exercises
M.1. Canvas drawing requires thinking about coordinate systems, scaling, and pixel-level positioning. Was this harder or easier than you expected compared to placing pre-built controls? What was the biggest challenge?
M.2. The chapter emphasized drawing inside OnPaint and using Invalidate. Did you make the mistake of drawing outside OnPaint? What happened?
M.3. Look at the pie chart code. How would you test it? Can you write a console program that creates a TBitmap, draws a pie chart on it, and saves it as a PNG — no form needed? What does this tell you about separating drawing logic from UI?