I have drawn a graduated ellipse on my form using the paint box.
The only problem is that I have used Moveto and LineTo many times to grade the ellipse.
procedure PaintBox1Paint(Sender: TObject);
begin
paintbox1.canvas.Pen.Color := clblue;
paintbox1.canvas.Pen.Width := 1;
paintbox1.canvas.Ellipse(98,51,520,473);
paintbox1.canvas.MoveTo(515,263);
paintbox1.canvas.LineTo(526,263);
paintbox1.canvas.MoveTo(310,45);
paintbox1.canvas.LineTo(310,52);
paintbox1.canvas.MoveTo(90,263);
paintbox1.canvas.LineTo(99,263);
paintbox1.canvas.MoveTo(310,472);
paintbox1.canvas.LineTo(310,479);
paintbox1.canvas.MoveTo(512,207);
paintbox1.canvas.LineTo(518,206);
paintbox1.canvas.MoveTo(491,158);
paintbox1.canvas.LineTo(496,155);
paintbox1.canvas.MoveTo(458,114);
paintbox1.canvas.LineTo(462,110);
paintbox1.canvas.MoveTo(414,81);
paintbox1.canvas.LineTo(417,76);
paintbox1.canvas.MoveTo(364,59);
paintbox1.canvas.LineTo(365,52);
paintbox1.canvas.MoveTo(255,59);
paintbox1.canvas.LineTo(254,52);
paintbox1.canvas.MoveTo(203,78);
paintbox1.canvas.LineTo(201,73);
paintbox1.canvas.MoveTo(161,110);
paintbox1.canvas.LineTo(155,105);
paintbox1.canvas.MoveTo(127,158);
paintbox1.canvas.LineTo(119,154);
paintbox1.canvas.MoveTo(106,209);
paintbox1.canvas.LineTo(97,207);
paintbox1.canvas.MoveTo(106,317);
paintbox1.canvas.LineTo(97,319);
paintbox1.canvas.MoveTo(126,368);
paintbox1.canvas.LineTo(120,371);
paintbox1.canvas.MoveTo(160,411);
paintbox1.canvas.LineTo(153,418);
paintbox1.canvas.MoveTo(414,445);
paintbox1.canvas.LineTo(417,450);
paintbox1.canvas.MoveTo(458,411);
paintbox1.canvas.LineTo(462,415);
//========================= Outer Ring Number 0,1,2,3,4,5 ....23 ===============
paintbox1.canvas.Font.Color := clblue;
paintbox1.canvas.Font.Size := 10;
paintbox1.canvas.TextOut(530,255,' 0');
paintbox1.canvas.TextOut(524,196,' 1');
paintbox1.canvas.TextOut(501,144,' 2');
paintbox1.canvas.TextOut(465,97,' 3');
paintbox1.canvas.TextOut(417,60,' 4');
paintbox1.canvas.TextOut(361,33,' 5');
paintbox1.canvas.TextOut(303,26,' 6 ');
paintbox1.canvas.TextOut(242,37,'7 ');
paintbox1.canvas.TextOut(187,55,'8 ');
paintbox1.canvas.TextOut(139,91,'9 ');
paintbox1.canvas.TextOut(95,140,'10 ');
paintbox1.canvas.TextOut(75,195,'11 ');
paintbox1.canvas.TextOut(68,255,' 12 ');
paintbox1.canvas.TextOut(80,312,'13 ');
paintbox1.canvas.TextOut(101,365,'14 ');
paintbox1.canvas.TextOut(135,417,'15 ');
paintbox1.canvas.TextOut(186,456,'16 ');
paintbox1.canvas.TextOut(244,476,'17 ');
paintbox1.canvas.TextOut(298,480,' 18 ');
paintbox1.canvas.TextOut(360,474,' 19');
paintbox1.canvas.TextOut(415,453,' 20');
paintbox1.canvas.TextOut(464,415,' 21');
paintbox1.canvas.TextOut(497,368,' 22');
paintbox1.canvas.TextOut(520,315,' 23');
end;

Is there a way that doesn't require me to repeat these codes so many times?
You don't need to hard‑code every tick and label. You can generate them dynamically with a loop and some trigonometry.
The key is to compute the ellipse (or circle) center and radius from the TPaintBox size, then place ticks and labels relative to that. This way the drawing also automatically scales when the control is resized.
Here's a complete example that produces a 24‑ticks dial, always centered and fitting inside the TPaintBox.
This is for Delphi VCL:
uses
Math;
procedure TForm1.PaintBox1Paint(Sender: TObject);
begin
const TickLen = 10; // inward tick length
const TickCount = 24; // number of ticks
const LabelGap = 20; // outward label distance
const PaintColor = clBlue; // paint color
with PaintBox1.Canvas do
begin
Pen.Color := PaintColor;
Pen.Width := 1;
// center of paintbox
var cx := PaintBox1.ClientWidth / 2;
var cy := PaintBox1.ClientHeight / 2;
// choose radius so circle fits in both width and height
var radius := Min(PaintBox1.ClientWidth, PaintBox1.ClientHeight) / 2 - LabelGap - TickLen;
// draw circle (not stretched ellipse)
Ellipse(Round(cx - radius), Round(cy - radius),
Round(cx + radius), Round(cy + radius));
Font.Color := PaintColor;
Font.Size := 10;
// adding ticks and labels
for var I := 0 to TickCount-1 do
begin
var angle := DegToRad(-i * 15); // clockwise, 0 at right
// outer circle point
var xOuter := cx + radius * Cos(angle);
var yOuter := cy + radius * Sin(angle);
// inner tick point
var xInner := cx + (radius - TickLen) * Cos(angle);
var yInner := cy + (radius - TickLen) * Sin(angle);
MoveTo(Round(xOuter), Round(yOuter));
LineTo(Round(xInner), Round(yInner));
// label position
var tx := cx + (radius + LabelGap) * Cos(angle);
var ty := cy + (radius + LabelGap) * Sin(angle);
var s := I.ToString;
var w := TextWidth(s);
var h := TextHeight(s);
TextOut(Round(tx - w / 2), Round(ty - h / 2), s);
end;
end;
end;
And this is how it looks: