TextOut (gdi32)
Last changed: -


C# Signature:

[DllImport("gdi32.dll", CharSet = CharSet.Auto)]
static extern bool TextOut(IntPtr hdc, int nXStart, int nYStart,
   string lpString, int cbString); (Please see note for CharSet in tips)

VB.NET Signature:

    <DllImport("gdi32")> _
    Shared Function TextOut(ByVal hdc As IntPtr, ByVal x As Integer, _
      ByVal y As Integer, ByVal textstring As String, _
      ByVal charCount As Integer) As Boolean
    End Function

Tips & Tricks:

TextOut, unlike the Text property, have to be called every time Windows asks for a control to repaint. By using the Graphics.CopyFromScreen method one can copy the output of a Textout command and use that for repainting.

Unlike graphics.drawstring, Textout can work with fonts other than True Type, Open Type, and PostScript fonts. If you have a font that's supported by Windows but not .net you can most likely use this command to draw it.

[CharSet = CharSet.Auto] According to my experience this parameter should be used when using a LOGFONT structure and the CreateFontIndirect fuction, otherwise the selected font will not be displayed. All flags will be done (lfItalic, lfUnderline ...) but not the lfFaceName string.

Sample Code:

   // Paste this at the beginning of your file if not present:
   using System.Drawing;
   using System.Runtime.InteropServices;

   //Create a class that subclass Control and paste these methods into it.
   //Then place the new control on a form.

   static extern bool TextOut(IntPtr hdc, int nXStart, int nYStart,
       string lpString, int cbString);
   static extern bool GetTextExtentPoint(IntPtr hdc, string lpString,
       int cbString, ref Size lpSize);
   public static extern IntPtr SelectObject(IntPtr hdc, IntPtr hgdiobj);
   public static extern bool DeleteObject(IntPtr objectHandle);

    #region Font

   protected override void OnKeyPress(KeyPressEventArgs e)
       Text += e.KeyChar.ToString();

       //Note, painting should not be done in the OnKeyPress method, instead:

       //You may want to meassure the string's size for layout purposes.
       using(Graphics g = base.CreateGraphics())

       //Do the GetHdc(), GetTextExtentPoint, etc, here. Remember that you have
       //to delete related objects such as fonts and call ReleaseHdc on graphics
       //afterwards. If you for some reason don't want to do this you can use a
       //SafeHandle (needs to be subclassed) which does this for you.

       //Remember to use HandleRef when appropriate. For instance if you create
       //a color that you set through SelectObject the .net garbage collector will
       //happily delete the color unless you refere to it after the drawing call or
       //use a HandleRef.

       //In this example we used the base class font and therefore need not worry
       //about the GC deleting the Font in mid drawing.    

       //Also keep in mind that all graphics objects _you_ create must be disposed,
       //which is done through the using clause in this example. It can also be done
       //by calling g.Dispose() in a finally clause (to guarantee that it's executed)

   protected override void OnPaint(PaintEventArgs e)
    IntPtr HDC = e.Graphics.GetHdc();

    //We have to use SelectObject to set the font, color and other properties.
    IntPtr last_font = SelectObject(HDC, Font.ToHfont());

    Size MeasureSize = new Size(0, 0);
    int y_pos = 0;
    int x_pos = 2;

    //Tasks such as these should not be done in the drawing loop as they
    //slow down the drawing.
    String[] lines = Text.Split(new char[] { '\n' });        

        //We draw out each line of text.
        for(int c=0; c < lines.Length; c++)
             String line = lines[c];
            TextOut(HDC, x_pos, y_pos, line, line.Length);

            //Unless you change the font, the line height never
            //changes so measuring the text at each draw call is
            //inefficient. Also it measure empty lines at 0 height
            //so pressing multiple enters will not move the text down.
             GetTextExtentPoint(HDC, line, line.Length, ref MeasureSize);

             y_pos += MeasureSize.Height;

            //You can use the MeasureSize.Width property to implement
            //word wrapping.
        //Put here to make sure we don't get a memory leak.

        DeleteObject(SelectObject(HDC, last_font));
        //Don't call dispose() on the Graphics object unless you createded it.

Vb.net Sample:

Imports System.Runtime.InteropServices

Public Class Form1

    Declare Function SetTextCharacterExtra Lib "gdi32" Alias "SetTextCharacterExtra" (ByVal hDC As Integer, ByVal nCharExtra As Integer) As Integer
    <DllImport("gdi32")> _
    Private Shared Function TextOut(ByVal hdc As IntPtr, ByVal x As Integer, ByVal y As Integer, ByVal textstring As String, ByVal charCount As Integer) As Boolean
    End Function
    <DllImport("gdi32")> _
    Private Shared Function SelectObject(ByVal hdc As IntPtr, ByVal hgdiobj As IntPtr) As IntPtr
    End Function
    <DllImport("gdi32")> _
    Private Shared Function DeleteObject(ByVal objectHandle As IntPtr) As Boolean
    End Function
    <DllImport("gdi32")> _
    Private Shared Function SetBkColor(ByVal hdc As IntPtr, ByVal crColor As Integer) As UInt32
    End Function
    <DllImport("gdi32")> _
    Private Shared Function SetTextColor(ByVal hdc As IntPtr, ByVal crColor As Integer) As UInt32
    End Function

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
    Using G = Graphics.FromHwnd(Me.Handle)
        Using myFont As New System.Drawing.Font("Arial", 20, FontStyle.Regular, GraphicsUnit.Pixel)
        'Regular Way
        Dim LeftEdge = 20
        G.DrawString("Hello", myFont, Brushes.Red, LeftEdge, 40)

        'If you want kerning
        Dim Kerning As Integer = 6 'I think this is twips
        Dim Hdc As IntPtr
        Dim FontPtr As IntPtr
            'Grab the Graphic object's handle
            Hdc = G.GetHdc()
            'Set the current GDI font
            FontPtr = SelectObject(Hdc, myFont.ToHfont())
            'Set the drawing surface background color
            SetBkColor(Hdc, ColorTranslator.ToWin32(Me.BackColor))
            'Set the text color
            SetTextColor(Hdc, ColorTranslator.ToWin32(Color.Red))
            'Set the kerning
            SetTextCharacterExtra(Hdc, Kerning)
            Dim Text = "Hello"
            'Draw the text at (20,60), Kerning will be applied so reset the left edge to half of kerning
            TextOut(Hdc, LeftEdge + (Kerning \ 2), 60, Text, Text.Length)
        Catch ex As Exception

            'Release the font
            'Release the handle on the graphics object
        End Try
        End Using
    End Using
    End Sub

End Class

Alternative Managed API:


TextOut on MSDN