Very nice, Tomas!
The Offset is due the Stringlenght of the value (3-2 or 1 digit).
One easy way to deal with this is to use a stringShape passed to g.drawObject instead of a string passed to g.drawstring since the X coordinate of the stringShape is in the center of its width.
Here's some replacement code that looks pretty good to me (at the end of DashControl.DrawCircle - "ss" having been previously defined):
// Legende in die Mitte setzen
ss = NEW stringShape
ss.Text = cstr(Me.PercentValue) +"%"
ss.Bold = true
ss.FillColor = Me.TextColor
ss.TextSize = (BG.Height) - (BG.Height/1.23)
ss.TextFont = g.TextFont
// move the text slightly to the right to account for the perceived width of "%"
ss.x = ss.TextSize/10
// move the text down because y=0 will center the baseline
ss.y = ss.TextSize/2.5
g.DrawObject(ss, self.Width/2, self.Height/2 )
// g.DrawString(cstr(Me.PercentValue) +"%", (self.Width)-(BG.Width/2.5) - (cstr(Me.ArcValue).Len+1)*(BG.Width/12), (self.Height)-(BG.Height/2.35))
(Note that I added an adjustment to the stringShape's X and Y properties to account for the fact that the 0,0 point is at the baseline of the text and that the "%" glyph is visually narrower than its mathematical width. Those adjustments may need to be changed for different fonts and/or radically different text sizes.)