E-STOP Button-Control: Text um Kreis herum zeichnen

  • C#

Es gibt 9 Antworten in diesem Thema. Der letzte Beitrag () ist von Murdersquad.

    E-STOP Button-Control: Text um Kreis herum zeichnen

    Hallo liebe Community,

    ich möchte ein Control programmieren, dass so ähnlich aussieht wie ein NOT-AUS-Schalter:



    Mal abgesehen von dem Text AUS, der unten steht, und den weißen Pfeilen in der mitte, sieht mein Steuerelement bisher so aus:


    Das Zeichnen von Kreisen (Ellipsen) ist auch gar nicht das Problem, vor dem ich stehe, sondern das Zeichnen des Textes um den Kreis herum.

    Hier der Quellcode von dem Steuerelement:

    C#-Quellcode

    1. public class EstopButton : Control
    2. {
    3. #region Konstruktor
    4. public EstopButton() : base()
    5. {
    6. SetStyle( ControlStyles.AllPaintingInWmPaint |
    7. ControlStyles.OptimizedDoubleBuffer |
    8. ControlStyles.ResizeRedraw |
    9. ControlStyles.SupportsTransparentBackColor |
    10. ControlStyles.UserPaint
    11. , true);
    12. }
    13. #endregion
    14. #region Funktionen
    15. private void DrawCurvedText(Graphics graphics, string text, Point centre, float distanceFromCentreToBaseOfText, float radiansToTextCentre, Font font, Brush brush)
    16. {
    17. // Circumference for use later
    18. var circleCircumference = (float)(Math.PI * 2 * distanceFromCentreToBaseOfText);
    19. // Get the width of each character
    20. var characterWidths = GetCharacterWidths(graphics, text, font).ToArray();
    21. // The overall height of the string
    22. var characterHeight = graphics.MeasureString(text, font).Height;
    23. var textLength = characterWidths.Sum();
    24. // The string length above is the arc length we'll use for rendering the string. Work out the starting angle required to
    25. // centre the text across the radiansToTextCentre.
    26. float fractionOfCircumference = textLength / circleCircumference;
    27. float currentCharacterRadians = radiansToTextCentre + (float)(Math.PI * fractionOfCircumference);
    28. for (int characterIndex = 0; characterIndex < text.Length; characterIndex++)
    29. {
    30. char @char = text[characterIndex];
    31. // Polar to cartesian
    32. float x = (float)(distanceFromCentreToBaseOfText * Math.Sin(currentCharacterRadians));
    33. float y = -(float)(distanceFromCentreToBaseOfText * Math.Cos(currentCharacterRadians));
    34. using (GraphicsPath characterPath = new GraphicsPath())
    35. {
    36. characterPath.AddString(@char.ToString(), font.FontFamily, (int)font.Style, font.Size, Point.Empty,
    37. StringFormat.GenericTypographic);
    38. var pathBounds = characterPath.GetBounds();
    39. // Transformation matrix to move the character to the correct location.
    40. // Note that all actions on the Matrix class are prepended, so we apply them in reverse.
    41. var transform = new Matrix();
    42. // Translate to the final position
    43. transform.Translate(centre.X + x, centre.Y + y);
    44. // Rotate the character
    45. var rotationAngleDegrees = currentCharacterRadians * 180F / (float)Math.PI - 180F;
    46. transform.Rotate(rotationAngleDegrees);
    47. // Translate the character so the centre of its base is over the origin
    48. transform.Translate(-pathBounds.Width / 2F, -characterHeight);
    49. characterPath.Transform(transform);
    50. // Draw the character
    51. graphics.FillPath(brush, characterPath);
    52. }
    53. if (characterIndex != text.Length - 1)
    54. {
    55. // Move "currentCharacterRadians" on to the next character
    56. var distanceToNextChar = (characterWidths[characterIndex] + characterWidths[characterIndex + 1]) / 2F;
    57. float charFractionOfCircumference = distanceToNextChar / circleCircumference;
    58. currentCharacterRadians -= charFractionOfCircumference * (float)(2F * Math.PI);
    59. }
    60. }
    61. }
    62. private IEnumerable<float> GetCharacterWidths(Graphics graphics, string text, Font font)
    63. {
    64. // The length of a space. Necessary because a space measured using StringFormat.GenericTypographic has no width.
    65. // We can't use StringFormat.GenericDefault for the characters themselves, as it adds unwanted spacing.
    66. var spaceLength = graphics.MeasureString(" ", font, Point.Empty, StringFormat.GenericDefault).Width;
    67. return text.Select(c => c == ' ' ? spaceLength : graphics.MeasureString(c.ToString(), font, Point.Empty, StringFormat.GenericTypographic).Width);
    68. }
    69. #endregion
    70. #region Callbacks
    71. protected override void OnPaint(PaintEventArgs e)
    72. {
    73. base.OnPaint(e);
    74. e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
    75. #region gelber Kreis
    76. // Leinwand zum Zeichnen definieren
    77. var squareClientRectYellow = ClientRectangle;
    78. squareClientRectYellow.Width = squareClientRectYellow.Width > squareClientRectYellow.Height ? squareClientRectYellow.Height : squareClientRectYellow.Width;
    79. squareClientRectYellow.Height = squareClientRectYellow.Height > squareClientRectYellow.Width ? squareClientRectYellow.Width : squareClientRectYellow.Height;
    80. // Quadrat zentrieren
    81. squareClientRectYellow.X = (int)(Width / 2f - squareClientRectYellow.Width / 2f);
    82. squareClientRectYellow.Y = (int)(Height / 2f - squareClientRectYellow.Height / 2f);
    83. // Gelben Kreis zeichnen
    84. e.Graphics.FillEllipse(new SolidBrush(Color.FromArgb(255, 192, 0)), squareClientRectYellow);
    85. #endregion
    86. #region roter Kreis
    87. // Leinwand zum Zeichnen definieren
    88. int percentage = 75;
    89. var squareClientRectRed = new Rectangle(0,0, (int)(squareClientRectYellow.Width / 100f * percentage), (int)(squareClientRectYellow.Height / 100f * percentage));
    90. // Quadrat zentrieren
    91. squareClientRectRed.X = (int)((Width / 2f) - (squareClientRectRed.Width / 2f));
    92. squareClientRectRed.Y = (int)((Height / 2f) - (squareClientRectRed.Height / 2f));
    93. // Roten Kreis zeichnen
    94. e.Graphics.FillEllipse(new SolidBrush(Color.FromArgb(214, 79, 46)), squareClientRectRed);
    95. #endregion
    96. DrawCurvedText(e.Graphics, Text, new Point(squareClientRectYellow.Width, squareClientRectYellow.Height), squareClientRectYellow.Height/2, squareClientRectYellow.Height, Font, new SolidBrush(Color.Black));
    97. }
    98. protected override void OnTextChanged(EventArgs e)
    99. {
    100. base.OnTextChanged(e);
    101. Invalidate();
    102. }
    103. #endregion
    104. }


    Die Funktion DrawCurvedText habe ich im Internet gefunden und macht auch etwas in die Richtung, allerdings weiß ich nicht, welche Parameter ich übergeben muss, damit der Text oben mittig im gelben Bereich gezeichnet wird...

    Jemand von Euch eine Ahnung?

    Vielen Dank im vorraus :rolleyes: ...
    Ohne genau analysiert zu haben, sehen wir uns diese 3 Argumente mal an.

    Point centre ist der Mittelpunkt, also ein Point mit den Werten Width / 2, height /2.

    float distanceFromCentreToBaseOfText scheint entweder der Abstand zwischen den Zeichen zu sein, oder wie bei DrawArc ein SweepAngle.

    float radiansToTextCentre würde ich denken, das das die Distanz vom Mittelpunkt zur Mitte(vertikal) vom Text ist.

    TRiViUM schrieb:

    Die Funktion DrawCurvedText habe ich im Internet gefunden
    Wie wäre es, wenn Du uns mal die Quelle dafür zur Verfügung stellst, mein Spekulatius-Bedarf für heute ist gedeckt.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    @BitBröselnullJo, davon bin ich auch ausgegangen.
    Allerdings macht mir das Control zur Designzeit komische Sachen, wenn ich die Größe verändere (Animation, bitte anklicken):


    Folgende Parameter habe ich beim Aufruf der Funktion DrawCurvedText verwendet:

    C#-Quellcode

    1. var rad = Height / 2f;
    2. DrawCurvedText(e.Graphics, Text, new Point((int)(Width/2f), (int)(Height/2f)), (int)(Height/2f), rad, Font, new SolidBrush(Color.Black));



    Wie genau müsste ich den distanceFromCentreToBaseOfText (sweepAngle) berechnen?

    RodFromGermany schrieb:

    die Quelle dafür

    Natürlich: stackoverflow.com/questions/28…curved-text-into-a-bitmap

    EDIT:
    Demo-Projekt angehangen
    Dateien
    • Demo.zip

      (11,82 kB, 51 mal heruntergeladen, zuletzt: )

    Dieser Beitrag wurde bereits 3 mal editiert, zuletzt von „TRiViUM“ ()

    @TRiViUM Dein Parameter rad macht, dass der Text rotiert.
    Setz da mal feste Werte ein (30, 60, 90, 120) und spiele ein wenig rum.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Also eben reingeschaut, passt doch alles. Du sollte dich einmal mit simpler Geometrie beschäftigen, wenn man das richtig versteht, kann man diese Funktion ohne lange probieren zu müssen anwenden.

    So ist der Text oben in etwa Mittig von dem gelben.

    C#-Quellcode

    1. DrawCurvedText(e.Graphics, Text, new Point((int)(Width/2f), (int)(Height/2f)), (int)(Height * 0.4), 0, Font, new SolidBrush(Color.Black));

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „BitBrösel“ ()

    RodFromGermany schrieb:

    macht, dass der Text rotiert

    Ach verdammt...Du hast natürlich Recht.

    Ich hab da eine Variable (Height/2) angegeben, natürlich dreht er sich dann ständig, wenn man die Größe des Controls ändert :S
    Hab scheinbar gedacht, ich hätte immer einen anderen Wert dort übergeben, hab ich dann tatsächlich nicht gesehen.

    Hab jetzt selbst nochmal gründlich über den Code geschaut, so sieht jetzt das Ergebnis aus:


    BitBrösel schrieb:

    in etwa Mittig von dem gelben

    Das "in etwa" hatte mir noch nicht gereicht, hab's dann berechnet...

    Im Anhang das aktuelle Control mit Beispielprojekt.

    Danke! :thumbsup:
    Dateien
    • Demo.zip

      (12,1 kB, 52 mal heruntergeladen, zuletzt: )

    Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von „TRiViUM“ ()

    @TRiViUM Gib mal dem Control im Designer .Dock = Fill und füge folgenden Code ein:

    C#-Quellcode

    1. protected override void OnClientSizeChanged(EventArgs e)
    2. {
    3. base.OnClientSizeChanged(e);
    4. float size = Math.Min(this.ClientRectangle.Height, this.ClientRectangle.Width) * 0.1F;
    5. this.Font = new Font(this.Font.Name, size, FontStyle.Bold);
    6. }
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!