Click here to Skip to main content
15,891,597 members
Articles / Multimedia / GDI+
Tip/Trick

GDI DrawString Configurator App

Rate me:
Please Sign up or sign in to vote.
4.90/5 (16 votes)
26 Jul 2015CPOL2 min read 25.3K   1K   22   6
This app helps you with understanding and using GDI DrawString function: precise measuring, positioning, quality setting and performance tuning.

Introduction

I presume you have already encountered the GDI DrawString function - in .NET, it is represented by System.Drawing.Graphics.DrawString method. In most cases, it is easy and straightforward, but in some advanced cases, you might find yourself struggling with combinations of various flags and settings to get the desired result. This app could be your tool to do this experimenting quickly and easily.

Image 1

Using the App

This is a brief list of functionalities you might find useful in the code:

  • Basic usage of DrawString method with basic settings (font, size, style).
  • Working with font families: listing, using generic families, determining various style support by font families, observing impact of selected font to positioning.
  • Testing text clipping and trimming by resizing the window so the whole text cannot fit to the specified area
  • Comparing the result with GraphicsPath.AddString method used for drawing text outlines.
  • Basic text output measuring using MeasureString and MeasureCharacterRanges methods and observing impact of various flags to the results.
  • Measuring and computing more font metrics such as leading or ascent (text baseline).
  • Experimenting with various flags and settings of StringFormat object.
  • Impact of text-related properties of Graphics object to text rendering quality.
  • Benchmarking the rendering performance using different settings.

Points of Interest

I have discovered some interesting things during development of this app and am using it for my purposes while developing another application.

  1. DrawString method adds some padding on the left side and right side of the text. If you want the text to start precisely on some X coordinate, you need to measure this padding and adjust your start point accordingly. MeasureString doesn't help here, you need to use MeasureCharacterRanges.

    Image 2

  2. If you want to be able to position characters really precisely, just using MeasureCharacterRanges is still not good enough, because the result numbers are rounded. You need to also activate the NoClip flag to get the precise floating-point numbers.

    Image 3

  3. The characters do not hesitate to draw also outside their measured ranges. Maybe this is the reason for using text padding.

    Image 4

  4. Yes, GDI handles kerning, as you can see in the following picture:

    Image 5

  5. If you experiment with drawing text outline, you will discover it could be sometimes misaligned with standard DrawString. Sometimes, it depends solely on the font size. If the texts are misaligned, it seems that MeasureString matches with GraphicsPath.AddString method (outlined text) while MeasureCharacterRanges goes precisely with standard DrawString method.

    Image 6

  6. Clipping does not affect GraphicsPath.AddString method.

    Image 7

I am sure you will find out lot of things on your own. I hope this app will help with solving your problems. Good luck!

History

  • v 1.0 - Initial version

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior) Petr Bříza
Czech Republic Czech Republic


Freelance .NET developer from Prague, Czech republic. You have a problem - I am the solution.

Visit me at http://petr.briza.net.


Comments and Discussions

 
QuestionDraw String as seen on TextBox Multiline and single Line also RTL Or LTR For Both English And Arabic Pin
Member 384671514-Mar-24 8:25
Member 384671514-Mar-24 8:25 
I'm trying to Redraw TextBox Text
but my problem is
1- when the text exceeds the textbox width in case single line I'm still getting the first visible strings which fit the width of the textbox I want to continue drawing all the chars as on the textbox

2- for multiline I have on line missing from the top when the word length is bigger than one line in case RTL.

my code works very well with multiline LTR I Want to fix RTL And Single Line if you can help Me I will be thankful .

her is my code

C#
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace Microsoft_TextBox
{
    public partial class Form9 : Form
    {
        private System.Windows.Forms.TextBox textbox1;
        private System.Windows.Forms.Panel panel1;
        private System.Windows.Forms.TextBox textBox2;
        private System.Windows.Forms.RadioButton radioButton5;
        private System.Windows.Forms.RadioButton radioButton4;
        private System.Windows.Forms.RadioButton radioButton3;
        private System.Windows.Forms.RadioButton radioButton2;
        private System.Windows.Forms.RadioButton radioButton1;
        private System.Windows.Forms.TextBox textBox3;
        private System.Windows.Forms.TextBox textBox4;
        private System.Windows.Forms.TextBox textBox5;
        private System.Windows.Forms.TextBox textBox6;
        private System.Windows.Forms.Button button1;
        public Form9()
        {
            InitializeComponent();
        }
        private void panel1_Paint(object sender, PaintEventArgs e)
        {
            using (StringFormat sf = new StringFormat())
            {

                // Get the font and brush for drawing
                Font font = textbox1.Font;

                //Check if text direction is right - to - left
                bool isRTL = textbox1.RightToLeft == RightToLeft.Yes;
                Brush brush = new SolidBrush(textbox1.ForeColor);


                // تحويل PointF إلى Point
                Point startPoint = new Point((int)0, -(int)0);
                Point endPoint = new Point((int)panel1.Width, (int)(panel1.Height));

                // Calculate the number of visible lines
                int visibleLines = panel1.Height / font.Height;

                // Get the starting point for drawing

                // Get the index of the first visible character
                int startIndex = textbox1.GetCharIndexFromPosition(startPoint);

                // Get the index of the last visible character
                int endIndex = textbox1.GetCharIndexFromPosition(endPoint);

                // Declare variables to hold line numbers
                int startLine, endLine;

                int charIndex = textbox1.SelectionStart;
                int lineNumber = textbox1.GetLineFromCharIndex(charIndex);
                textBox2.Text = lineNumber.ToString();
                textBox3.Text = startIndex.ToString();
                textBox4.Text = (textbox1.GetCharIndexFromPosition(new Point(0, panel1.Height / visibleLines / 4))).ToString();
                textBox6.Text = endIndex.ToString();

                int lastVisibleLine = textbox1.GetLineFromCharIndex(endIndex);




                if (isRTL)
                {

                    // Adjust start line based on whether the window reached the top
                    //if (lineNumber >= visibleLines)
                    try
                    {


                        if ((textbox1.GetCharIndexFromPosition(new Point(0, panel1.Height / visibleLines / 4)) - textbox1.Lines[0].Length) <= 0)

                        {
                            startLine = textbox1.GetLineFromCharIndex(startIndex);
                            endLine = textbox1.GetLineFromCharIndex(endIndex);
                            textBox5.Text = "";
                            textBox5.Text = "hello we are here";
                        }

                        else
                        {
                            startLine = textbox1.GetLineFromCharIndex(startIndex) + 1;
                            endLine = textbox1.GetLineFromCharIndex(endIndex);
                            textBox5.Text = "";
                            textBox5.Text = "hello we are not here";
                        }
                    }
                    catch
                    {
                        startLine = textbox1.GetLineFromCharIndex(startIndex);
                        endLine = textbox1.GetLineFromCharIndex(endIndex);
                        textBox5.Text = "";
                    }

                }
                else
                {

                    // Adjust start line based on whether the window reached the top
                    if (textbox1.GetCharIndexFromPosition(new Point(0, panel1.Height / visibleLines / 4)) == 0)
                    {
                        startLine = textbox1.GetLineFromCharIndex(startIndex);
                        endLine = textbox1.GetLineFromCharIndex(endIndex);

                    }
                    //else if (lineNumber< visibleLines )
                    // {

                    //     startLine = lineNumber;
                    //     endLine = textbox1.GetLineFromCharIndex(endIndex);
                    // }
                    else
                    {
                        startLine = textbox1.GetLineFromCharIndex(startIndex) + 1;
                        endLine = textbox1.GetLineFromCharIndex(endIndex);
                    }
                }


                // Draw each visible line onto panel1
                for (int line = startLine; line <= endLine; line++)
                {
                    // Get the start and end index of the current line
                    int lineStartIndex = textbox1.GetFirstCharIndexFromLine(line);
                    int lineEndIndex = textbox1.GetFirstCharIndexFromLine(line + 1);
                    if (lineEndIndex == -1)
                        lineEndIndex = textbox1.Text.Length;

                    // Get the text of the current line
                    string lineText = textbox1.Text.Substring(lineStartIndex, lineEndIndex - lineStartIndex);



                    // Check if the current character is a newline character
                    if (textbox1.Text == "\n" || textbox1.Text == "\r" || textbox1.Text == "\t" || textbox1.Text == "\f")
                    {
                        continue; // Skip drawing for newline characters
                    }



                    if (isRTL)
                    {
                        // Set the format flags to RightToLeft
                        sf.FormatFlags |= StringFormatFlags.DirectionRightToLeft;
                    }
                    else
                    {
                        // Set the format flags to LeftToRight (default behavior)
                        sf.FormatFlags &= ~StringFormatFlags.DirectionRightToLeft; // Clear the RightToLeft flag
                    }

                    // Calculate the width of the line
                    SizeF textSize = e.Graphics.MeasureString(lineText, font);

                    //// Adjust startPoint for RTL text
                    //if (isRTL)
                    //{
                    //    // Calculate the X-coordinate for the right alignment
                    //    startPoint.X = panel1.Width - (int)textSize.Width;
                    //}


                    // Calculate the X coordinate for the alignment
                    float xCoordinate = startPoint.X;
                    switch (textbox1.TextAlign)
                    {
                        case HorizontalAlignment.Center:
                            if (textbox1.RightToLeft == RightToLeft.Yes)
                            {
                                //xCoordinate = (panel1.Width + textSize.Width) / 2;   /// for DrawString
                                xCoordinate = (panel1.Width - textSize.Width) / 2;   /// for TextRenderer.DrawText
                            }
                            else
                            {
                                xCoordinate = (panel1.Width - textSize.Width) / 2;
                            }
                            break;
                        case HorizontalAlignment.Right:
                            if (textbox1.RightToLeft == RightToLeft.Yes)
                            {
                                // xCoordinate = textSize.Width;       /// for DrawString
                                xCoordinate = 0;        /// for TextRenderer.DrawText
                            }
                            else
                            {
                                xCoordinate = panel1.Width - textSize.Width;
                            }
                            break;
                        case HorizontalAlignment.Left:
                            if (textbox1.RightToLeft == RightToLeft.Yes)
                            {
                                //  xCoordinate = panel1.Width;   /// for DrawString
                                xCoordinate = panel1.Width - textSize.Width;    /// for TextRenderer.DrawText
                            }
                            else
                            {
                                // No need to adjust xCoordinate for left alignment

                            }
                            break;
                        default:
                            break;
                    }
                    // Draw the line
                    // e.Graphics.DrawString(lineText, new Font(textbox1.Font.FontFamily, textbox1.Font.Size, textbox1.Font.Style), brush, xCoordinate, startPoint.Y, sf);

                    // Convert StringFormatFlags to TextFormatFlags
                    TextFormatFlags flags = (TextFormatFlags)StringFormatFlags.NoWrap;
                    if (isRTL)
                    {
                        flags |= TextFormatFlags.RightToLeft;
                    }
                    TextRenderer.DrawText(e.Graphics, lineText, font, new Point((int)xCoordinate, startPoint.Y), textbox1.ForeColor, flags /*(TextFormatFlags)StringFormatFlags.DirectionRightToLeft*/);





                    //// Draw the line
                    //e.Graphics.DrawString(lineText, font, brush, startPoint,sf);

                    // Move to the next line
                    startPoint.Y += font.Height;
                }




            }
        }


        private void textbox1_TextChanged(object sender, EventArgs e)
        {
            panel1.Invalidate();

        }
        private void textbox1_KeyDown(object sender, KeyEventArgs e)
        {
            panel1.Invalidate();
        }

        private void radioButton1_CheckedChanged(object sender, EventArgs e)
        {
            textbox1.TextAlign = HorizontalAlignment.Left;
            //radioButton2.Checked = false;
            //radioButton3.Checked = false;
            panel1.Invalidate();
        }

        private void radioButton2_CheckedChanged(object sender, EventArgs e)
        {
            textbox1.TextAlign = HorizontalAlignment.Center;
            //radioButton1.Checked = false;
            //radioButton3.Checked = false;
            panel1.Invalidate();
        }

        private void radioButton3_CheckedChanged(object sender, EventArgs e)
        {
            textbox1.TextAlign = HorizontalAlignment.Right;
            //radioButton1.Checked = false;
            //radioButton2.Checked = false;
            panel1.Invalidate();
        }

        private void radioButton4_CheckedChanged(object sender, EventArgs e)
        {
            textbox1.RightToLeft = RightToLeft.Yes;
            //radioButton5.Checked = false;
            panel1.Invalidate();
        }

        private void radioButton5_CheckedChanged(object sender, EventArgs e)
        {
            textbox1.RightToLeft = RightToLeft.No;
            //radioButton4.Checked = false;
            panel1.Invalidate();

        }

        private void transparentRichTextBox1_TextChanged(object sender, EventArgs e)
        {
            //textbox1.Text = transparentRichTextBox1.Text;
        }



        private void InitializeComponent()
        {
            System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(Form9));
            this.textbox1 = new System.Windows.Forms.TextBox();
            this.panel1 = new System.Windows.Forms.Panel();
            this.textBox2 = new System.Windows.Forms.TextBox();
            this.radioButton5 = new System.Windows.Forms.RadioButton();
            this.radioButton4 = new System.Windows.Forms.RadioButton();
            this.radioButton3 = new System.Windows.Forms.RadioButton();
            this.radioButton2 = new System.Windows.Forms.RadioButton();
            this.radioButton1 = new System.Windows.Forms.RadioButton();
            this.textBox3 = new System.Windows.Forms.TextBox();
            this.textBox4 = new System.Windows.Forms.TextBox();
            this.textBox5 = new System.Windows.Forms.TextBox();
            this.textBox6 = new System.Windows.Forms.TextBox();
            this.button1 = new System.Windows.Forms.Button();
            this.SuspendLayout();
            // 
            // textbox1
            // 
            this.textbox1.BorderStyle = System.Windows.Forms.BorderStyle.None;
            this.textbox1.Font = new System.Drawing.Font("Tahoma", 12F, System.Drawing.FontStyle.Underline);
            this.textbox1.ForeColor = System.Drawing.Color.YellowGreen;
            this.textbox1.Location = new System.Drawing.Point(194, 71);
            this.textbox1.Multiline = true;
            this.textbox1.Name = "textbox1";
            this.textbox1.RightToLeft = System.Windows.Forms.RightToLeft.Yes;
            this.textbox1.Size = new System.Drawing.Size(246, 104);
            this.textbox1.TabIndex = 0;
            this.textbox1.Text = "1111111111111111111111222222222222222222222233333333333333333333334444444444444444444444555555555555555555555566666666666666666666667777777777777777777777888888888888888888888899999999999999999999990000000000000000000000";
            this.textbox1.TextAlign = System.Windows.Forms.HorizontalAlignment.Right;
            this.textbox1.TextChanged += new System.EventHandler(this.textbox1_TextChanged);
            this.textbox1.KeyDown += new System.Windows.Forms.KeyEventHandler(this.textbox1_KeyDown);
            // 
            // panel1
            // 
            this.panel1.BackColor = System.Drawing.Color.White;
            this.panel1.Location = new System.Drawing.Point(194, 197);
            this.panel1.Name = "panel1";
            this.panel1.Size = new System.Drawing.Size(246, 104);
            this.panel1.TabIndex = 1;
            this.panel1.Paint += new System.Windows.Forms.PaintEventHandler(this.panel1_Paint);
            // 
            // textBox2
            // 
            this.textBox2.Location = new System.Drawing.Point(194, 340);
            this.textBox2.Name = "textBox2";
            this.textBox2.Size = new System.Drawing.Size(213, 24);
            this.textBox2.TabIndex = 2;
            // 
            // radioButton5
            // 
            this.radioButton5.AutoSize = true;
            this.radioButton5.Location = new System.Drawing.Point(598, 280);
            this.radioButton5.Name = "radioButton5";
            this.radioButton5.Size = new System.Drawing.Size(141, 21);
            this.radioButton5.TabIndex = 10;
            this.radioButton5.TabStop = true;
            this.radioButton5.Text = "Right To Left Flase";
            this.radioButton5.UseVisualStyleBackColor = true;
            this.radioButton5.CheckedChanged += new System.EventHandler(this.radioButton5_CheckedChanged);
            // 
            // radioButton4
            // 
            this.radioButton4.AutoSize = true;
            this.radioButton4.Location = new System.Drawing.Point(598, 242);
            this.radioButton4.Name = "radioButton4";
            this.radioButton4.Size = new System.Drawing.Size(140, 21);
            this.radioButton4.TabIndex = 9;
            this.radioButton4.TabStop = true;
            this.radioButton4.Text = "Right To Left True";
            this.radioButton4.UseVisualStyleBackColor = true;
            this.radioButton4.CheckedChanged += new System.EventHandler(this.radioButton4_CheckedChanged);
            // 
            // radioButton3
            // 
            this.radioButton3.AutoSize = true;
            this.radioButton3.Location = new System.Drawing.Point(598, 204);
            this.radioButton3.Name = "radioButton3";
            this.radioButton3.Size = new System.Drawing.Size(61, 21);
            this.radioButton3.TabIndex = 8;
            this.radioButton3.TabStop = true;
            this.radioButton3.Text = "Right";
            this.radioButton3.UseVisualStyleBackColor = true;
            this.radioButton3.CheckedChanged += new System.EventHandler(this.radioButton3_CheckedChanged);
            // 
            // radioButton2
            // 
            this.radioButton2.AutoSize = true;
            this.radioButton2.Location = new System.Drawing.Point(598, 166);
            this.radioButton2.Name = "radioButton2";
            this.radioButton2.Size = new System.Drawing.Size(70, 21);
            this.radioButton2.TabIndex = 7;
            this.radioButton2.TabStop = true;
            this.radioButton2.Text = "Center";
            this.radioButton2.UseVisualStyleBackColor = true;
            this.radioButton2.CheckedChanged += new System.EventHandler(this.radioButton2_CheckedChanged);
            // 
            // radioButton1
            // 
            this.radioButton1.AutoSize = true;
            this.radioButton1.Location = new System.Drawing.Point(598, 128);
            this.radioButton1.Name = "radioButton1";
            this.radioButton1.Size = new System.Drawing.Size(52, 21);
            this.radioButton1.TabIndex = 6;
            this.radioButton1.TabStop = true;
            this.radioButton1.Text = "Left";
            this.radioButton1.UseVisualStyleBackColor = true;
            this.radioButton1.CheckedChanged += new System.EventHandler(this.radioButton1_CheckedChanged);
            // 
            // textBox3
            // 
            this.textBox3.Location = new System.Drawing.Point(194, 370);
            this.textBox3.Name = "textBox3";
            this.textBox3.Size = new System.Drawing.Size(213, 24);
            this.textBox3.TabIndex = 12;
            // 
            // textBox4
            // 
            this.textBox4.Location = new System.Drawing.Point(194, 400);
            this.textBox4.Name = "textBox4";
            this.textBox4.Size = new System.Drawing.Size(213, 24);
            this.textBox4.TabIndex = 13;
            // 
            // textBox5
            // 
            this.textBox5.Font = new System.Drawing.Font("Tahoma", 12F);
            this.textBox5.ForeColor = System.Drawing.Color.YellowGreen;
            this.textBox5.Location = new System.Drawing.Point(580, 333);
            this.textBox5.Multiline = true;
            this.textBox5.Name = "textBox5";
            this.textBox5.RightToLeft = System.Windows.Forms.RightToLeft.Yes;
            this.textBox5.Size = new System.Drawing.Size(326, 105);
            this.textBox5.TabIndex = 14;
            //this.textBox5.Text = resources.GetString("textBox5.Text");
            this.textBox5.TextAlign = System.Windows.Forms.HorizontalAlignment.Right;
            // 
            // textBox6
            // 
            this.textBox6.Location = new System.Drawing.Point(413, 370);
            this.textBox6.Name = "textBox6";
            this.textBox6.Size = new System.Drawing.Size(100, 24);
            this.textBox6.TabIndex = 15;
            // 
            // button1
            // 
            this.button1.Location = new System.Drawing.Point(102, 351);
            this.button1.Name = "button1";
            this.button1.Size = new System.Drawing.Size(75, 23);
            this.button1.TabIndex = 16;
            this.button1.Text = "button1";
            this.button1.UseVisualStyleBackColor = true;
            // 
            // Form9
            // 
            this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 16F);
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
            //this.BackgroundImage = ((System.Drawing.Image)(resources.GetObject("$this.BackgroundImage")));
            this.BackgroundImageLayout = System.Windows.Forms.ImageLayout.Stretch;
            this.ClientSize = new System.Drawing.Size(800, 477);
            this.Controls.Add(this.button1);
            this.Controls.Add(this.textBox6);
            this.Controls.Add(this.textBox5);
            this.Controls.Add(this.textBox4);
            this.Controls.Add(this.textBox3);
            this.Controls.Add(this.radioButton5);
            this.Controls.Add(this.radioButton4);
            this.Controls.Add(this.radioButton3);
            this.Controls.Add(this.radioButton2);
            this.Controls.Add(this.radioButton1);
            this.Controls.Add(this.textBox2);
            this.Controls.Add(this.panel1);
            this.Controls.Add(this.textbox1);
            this.Name = "Form9";
            this.Text = "Form9";
            this.ResumeLayout(false);
            this.PerformLayout();

        }
        /// <summary>
        /// Required designer variable.
        /// </summary>
        private System.ComponentModel.IContainer components = null;

        /// <summary>
        /// Clean up any resources being used.
        /// </summary>
        /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
        protected override void Dispose(bool disposing)
        {
            if (disposing && (components != null))
            {
                components.Dispose();
            }
            base.Dispose(disposing);
        }

    }
}

Suggestionto bypass ranges count limitation Pin
Dev Tools4-Mar-19 4:53
Dev Tools4-Mar-19 4:53 
Generalres Pin
Member 1182388517-Nov-17 19:11
Member 1182388517-Nov-17 19:11 
QuestionstringFormat settings and graphics settings Pin
Alex M.H.20-Jan-16 0:13
Alex M.H.20-Jan-16 0:13 
QuestionVS Incompatible... Pin
AchLog28-Jul-15 2:41
AchLog28-Jul-15 2:41 
Generalthanks Pin
Hooman_Kh26-Jul-15 11:09
Hooman_Kh26-Jul-15 11:09 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.