Formatting Types

Microsoft Silverlight will reach end of support after October 2021. Learn more.

Formatting is the process of converting an object or enumeration value to its string representation, typically so that the resulting string can be displayed to users. This conversion often poses a number of challenges:

  • The way that particular values are stored internally does not necessarily reflect the way that users want to view them. For example, a customer number such as 637 might be displayed as 0000-0637.

  • Sometimes the conversion of an object to its string representation is not intuitive. For example, it is not clear what the string representation of a Temperature object or a Person object would look like.

  • Values often require formatting that is culture-sensitive. For example, in an application that uses numbers to reflect monetary values, numeric strings should include the current culture’s currency symbol, group separator (which in most cultures is the thousands separator), and decimal symbol.

  • An application may have to display the same value in different ways. For example, an application may represent an enumeration member by displaying a string representation of its name or by displaying its underlying value.

The .NET Framework for Silverlight provides rich formatting support that enables developers to address these requirements. The basic mechanism for formatting is the default implementation of the ToString method, which is defined by the System.Object class. However, the .NET Framework provides several ways to modify and extend its default operation. These include the following:

  • Overriding the Object.ToString method to define a custom string representation for an object’s value.

  • Defining format specifiers that enable the string representation of an object’s value to take multiple forms. For example, the "C" format specifier in the following statement formats an integer as a currency value.

    Dim currencyValue As String = integerValue.ToString("C")
    
    string currencyValue = integerValue.ToString("C");
    

    The "X" format specifier in the following statement formats an integer as a hexadecimal value.

    Dim hexValue As String = integerValue.ToString("X")
    
    string hexValue = integerValue.ToString("X");
    
  • Using format providers to take advantage of the formatting conventions of a specific culture or profession. For example, the following statement formats a currency value by using the formatting conventions of the en-US culture.

    Dim value As String = cost.ToString("C", New CultureInfo("en-US"))
    
    string value = cost.ToString("C", new CultureInfo("en-US"));
    

    The IFormattable interface supports formatting using both format specifiers and format providers.

  • Using composite formatting to include the string representation of a value in a larger string.

  • Implementing ICustomFormatter and IFormatProvider to provide a complete custom formatting solution.

The following sections examine these methods for converting an object to its string representation.

Default Formatting Using the ToString Method

Every type derived from System.Object automatically inherits a parameterless ToString method, which returns the name of the type by default. The following example illustrates the default ToString method. It defines a class named Automobile that has no implementation. When the class is instantiated and its ToString method is called, it displays its type name.

Public Class Automobile
End Class

Module Example
   Public Sub Demo(outputBlock As System.Windows.Controls.TextBlock)
      Dim auto As New Automobile()
      outputBlock.Text += auto.ToString() + vbCrLf
   End Sub
End Module
' This example displays the following output:
'     SilverlightApplication.Automobile
using System;

class Automobile {}

public class Example
{
   public static void Demo(System.Windows.Controls.TextBlock outputBlock)
   {
      Automobile auto = new Automobile();
      outputBlock.Text += auto.ToString() + "\n";
   }
}
// This example displays the following output:
//     Automobile

Overriding the ToString Method

Because displaying the name of a type is often of limited use, you can override the ToString method to provide a more useful representation of an object’s value. The following example illustrates how to override the ToString method. It defines a Temperature object and overrides its ToString method to display the temperature in degrees Celsius.

Public Class Temperature
   Private m_Temp As Decimal

   Public Sub New(temperature As Decimal)
      Me.m_Temp = temperature
   End Sub

   Public Overrides Function ToString() As String
      Return m_Temp.ToString("N2") & " °C"
   End Function
End Class

Module Example
   Public Sub Demo(outputBlock As System.Windows.Controls.TextBlock)
      Dim temp1 As New Temperature(0d)
      outputBlock.Text += temp1.ToString() + vbCrLf
      Dim temp2 As New Temperature(-40d)
      outputBlock.Text += temp2.ToString() + vbCrLf
      Dim temp3 As New Temperature(16d)
      outputBlock.Text += temp3.ToString() + vbCrLf
   End Sub
End Module
' The example displays the following output:
'       0.00 °C
'       -40.00 °C
'       16.00 °C
using System;

public class Temperature
{
   private decimal m_Temp;

   public Temperature(decimal temperature)
   {
      this.m_Temp = temperature;
   }

   public override string ToString()
   {
      return m_Temp.ToString("N2") + " °C";
   }
}

public class Example
{
   public static void Demo(System.Windows.Controls.TextBlock outputBlock)
   {
      Temperature temp1 = new Temperature(0);
      outputBlock.Text += temp1.ToString() + "\n";
      Temperature temp2 = new Temperature(-40);
      outputBlock.Text += temp2.ToString() + "\n";
      Temperature temp3 = new Temperature(16);
      outputBlock.Text += temp3.ToString() + "\n";
   }
}
// The example displays the following output:
//       0.00 °C
//       -40.00 °C
//       16.00 °C

In the .NET Framework, the ToString method of each primitive value type has been overridden to display the object’s value instead of its name. The following table shows how each primitive type has been overridden. Note that most of the overridden methods call another overload of the ToString method and pass it the "G" format specifier and an IFormatProvider object that represents the current culture.

Type

Override

Boolean

Returns either TrueString or FalseString.

Byte

Calls Byte.ToString("G", NumberFormatInfo.CurrentInfo) to format the Byte value for the current culture.

Char

Returns the character as a string.

DateTime

Calls DateTime.ToString("G", DatetimeFormatInfo.CurrentInfo) to format the date and time value for the current culture.

Decimal

Calls Decimal.ToString("G", NumberFormatInfo.CurrentInfo) to format the Decimal value for the current culture.

Double

Calls Double.ToString("G", NumberFormatInfo.CurrentInfo) to format the Double value for the current culture.

Int16

Calls Int16.ToString("G", NumberFormatInfo.CurrentInfo) to format the Int16 value for the current culture.

Int32

Calls Int32.ToString("G", NumberFormatInfo.CurrentInfo) to format the Int32 value for the current culture.

Int64

Calls Int64.ToString("G", NumberFormatInfo.CurrentInfo) to format the Int64 value for the current culture.

SByte

Calls SByte.ToString("G", NumberFormatInfo.CurrentInfo) to format the SByte value for the current culture.

Single

Calls Single.ToString("G", NumberFormatInfo.CurrentInfo) to format the Single value for the current culture.

UInt16

Calls UInt16.ToString("G", NumberFormatInfo.CurrentInfo) to format the UInt16 value for the current culture.

UInt32

Calls UInt32.ToString("G", NumberFormatInfo.CurrentInfo) to format the UInt32 value for the current culture.

UInt64

Calls UInt64.ToString("G", NumberFormatInfo.CurrentInfo) to format the UInt64 value for the current culture.

The ToString Method and Format Strings

Relying on the default ToString method or overriding ToString is appropriate when an object has a single string representation. However, the value of an object often has multiple representations. For example, a temperature can be expressed in degrees Fahrenheit, degrees Celsius, or kelvins. Similarly, the integer value 10 can be represented as 10, 10.0, 1.0e01, or $10.00.

To enable a single value to have multiple string representations, the .NET Framework for Silverlight uses format strings. A format string is a string that contains one or more predefined format specifiers, which are single characters or groups of characters that define how the ToString method should format its output. The format string is then passed as a parameter to the object's ToString method and determines how the string representation of that object's value should appear.

All numeric types, enumeration values, and the DateTime type support a predefined set of format specifiers. However, support for format strings is not limited to the .NET Framework's primitive data types. You can also use format strings to define multiple string representations of your application-defined data types.

Standard Format Strings

A standard format string includes a single format specifier that defines the string representation of an object. In some cases, the correspondence between a standard format string and an object’s string representation is direct. This is true of the standard format strings supported by enumeration members. Using a standard format string is also the most common method for implementing application-defined format strings.

Format strings for enumeration types and for most application-defined types directly control the string representation of a value. The format strings passed to an enumeration value’s ToString method determine whether the value is displayed using its string name (the "G" and "F" format specifiers), its underlying integral value (the "D" format specifier), or its hexadecimal value (the "X" format specifier). The following example illustrates the use of standard format strings to format a DayOfWeek value.

Module Example
   Public Sub Demo(outputBlock As System.Windows.Controls.TextBlock)
      Dim thisDay As DayOfWeek = DayOfWeek.Monday
      Dim formatStrings() As String = {"G", "F", "D", "X"}

      For Each formatString As String In formatStrings
         outputBlock.Text += thisDay.ToString(formatString) + vbCrLf
      Next
   End Sub
End Module
' The example displays the following output:
'       Monday
'       Monday
'       1
'       00000001
using System;

public class Example
{
   public static void Demo(System.Windows.Controls.TextBlock outputBlock)
   {
      DayOfWeek thisDay = DayOfWeek.Monday;
      string[] formatStrings = {"G", "F", "D", "X"};

      foreach (string formatString in formatStrings)
         outputBlock.Text += thisDay.ToString(formatString) + "\n";
   }
}
// The example displays the following output:
//       Monday
//       Monday
//       1
//       00000001

For date and time values and all numeric values, a standard format string is in some cases an alias for a set of property values. For example, the "C" format specifier formats a number as a currency value. When you call the ToString method with the "C" format specifier as the only parameter, the following property values from the current culture’s NumberFormatInfo object are used to define the string representation of the numeric value:

  • The CurrencySymbol property, which returns a string that contains the current culture’s currency symbol.

  • The CurrencyNegativePattern or CurrencyPositivePattern property, which returns integers that determine the following:

    • The placement of the currency symbol.

    • Whether negative values are indicated by a leading negative sign, a trailing negative sign, or parentheses.

    • Whether a space appears between the numeric value and the currency symbol.

  • The CurrencyDecimalDigits property, which defines the set of decimal digits.

  • The CurrencyDecimalSeparator property, which defines the decimal separator symbol.

  • The CurrencyGroupSeparator property, which defines the group separator symbol.

  • The CurrencyGroupSizes property, which defines the number of digits in each group to the left of the decimal.

Other standard format strings for numeric values or date and time values are aliases for custom format strings stored by a property. For example, calling the ToString method of a date and time value with the "D" format specifier displays the date and time by using the custom format string stored in the current culture’s DateTimeFormatInfo.LongDatePattern property. The following example illustrates the relationship between the custom format string and the string representation of a date.

Imports System.Globalization

Module Example
   Public Sub Demo(outputBlock As System.Windows.Controls.TextBlock)
      Dim date1 As Date = #6/30/2009#
      outputBlock.Text += String.Format("'{0}' format string: {1}", _
                          CultureInfo.CurrentCulture.DateTimeFormat.LongDatePattern, _
                          date1.ToString("D")) + vbCrLf
   End Sub
End Module
' The example displays the following output when run on a system whose
' current culture is en-US:
'      'dddd, MMMM dd, yyyy' format string: Tuesday, June 30, 2009
using System;
using System.Globalization;

public class Example
{
   public static void Demo(System.Windows.Controls.TextBlock outputBlock)
   {
      DateTime date1 = new DateTime(2009, 6, 30);
      outputBlock.Text += String.Format("'{0}' format string: {1}\n", 
                          CultureInfo.CurrentCulture.DateTimeFormat.LongDatePattern, 
                          date1.ToString("D"));
   }
}
// The example displays the following output when run on a system whose
// current culture is en-US:
//      'dddd, MMMM dd, yyyy' format string: Tuesday, June 30, 2009

For information about standard numeric formatting strings, see Standard Numeric Format Strings. For information about standard date and time format strings, see Standard Date and Time Format Strings. For information about standard TimeSpan format strings, see Standard TimeSpan Format Strings.

You can also use standard format strings to define the string representation of an application-defined object that is produced by the object’s ToString method. For example, a Temperature class can internally store the temperature in degrees Celsius and use format specifiers to represent the value of the Temperature object in degrees Celsius, degrees Fahrenheit, and kelvins The following example provides an illustration.

Public Class Temperature
   Private m_Temp As Decimal

   Public Sub New(temperature As Decimal)
      Me.m_Temp = temperature
   End Sub

   Public ReadOnly Property Celsius() As Decimal
      Get
         Return Me.m_Temp
      End Get   
   End Property

   Public ReadOnly Property Kelvin() As Decimal
      Get
         Return Me.m_Temp + 273.15d   
      End Get
   End Property

   Public ReadOnly Property Fahrenheit() As Decimal
      Get
         Return Math.Round(CDec(Me.m_Temp * 9 / 5 + 32), 2)
      End Get      
   End Property

   Public Overrides Function ToString() As String
      Return Me.ToString("C")
   End Function

   Public Overloads Function ToString(format As String) As String  
      ' Handle null or empty string.
      If Not String.IsNullOrEmpty(format) Then format = format.Trim()
      If String.IsNullOrEmpty(format.Trim()) Then format = "C" 

      ' Convert temperature to Fahrenheit and return string.
      Select Case format.ToUpper()
         Case "F"
            Return Me.Fahrenheit.ToString("N2") & " °F"
         ' Convert temperature to Kelvin and return string.
         Case "K"
            Return Me.Kelvin.ToString("N2") & " K"
         ' Return temperature in Celsius.
         Case "C"
            Return Me.Celsius.ToString("N2") & " °C"
         Case Else
            Throw New FormatException(String.Format("The '{0}' format string is not supported.", format))
      End Select      
   End Function
End Class

Public Module Example
   Public Sub Demo(outputBlock As System.Windows.Controls.TextBlock)
      Dim temp1 As New Temperature(0d)
      outputBlock.Text += temp1.ToString() + vbCrLf
      outputBlock.Text += temp1.ToString("C") + vbCrLf
      outputBlock.Text += temp1.ToString("F") + vbCrLf
      outputBlock.Text += temp1.ToString("K") + vbCrLf
      Dim temp2 As New Temperature(-40d)
      outputBlock.Text += temp2.ToString() + vbCrLf
      outputBlock.Text += temp2.ToString("C") + vbCrLf
      outputBlock.Text += temp2.ToString("F") + vbCrLf
      outputBlock.Text += temp2.ToString("K") + vbCrLf
      Dim temp3 As New Temperature(16d)
      outputBlock.Text += temp3.ToString() + vbCrLf
      outputBlock.Text += temp3.ToString("C") + vbCrLf
      outputBlock.Text += temp3.ToString("F") + vbCrLf
      outputBlock.Text += temp3.ToString("K") + vbCrLf

      outputBlock.Text += String.Format("The temperature is now {0:F}.", temp3) + vbCrLf
   End Sub
End Module
' The example displays the following output:
'       0.00 °C
'       0.00 °C
'       32.00 °F
'       273.15 K
'       -40.00 °C
'       -40.00 °C
'       -40.00 °F
'       233.15 K
'       16.00 °C
'       16.00 °C
'       60.80 °F
'       289.15 K
'       The temperature is now 16.00 °C.
public class Temperature
{
   private decimal m_Temp;

   public Temperature(decimal temperature) {
      this.m_Temp = temperature;
   }

   public decimal Celsius {
      get { return this.m_Temp; }
   }

   public decimal Kelvin {
      get  { return this.m_Temp + 273.15m; }   
   }

   public decimal Fahrenheit {
      get { return Math.Round((decimal)this.m_Temp * 9 / 5 + 32, 2); }
   }

   public override string ToString()
   {
      return this.ToString("C");
   }

   public string ToString(string format)
   {  
      // Handle null or empty string.
      if (! String.IsNullOrEmpty(format))
         format = format.Trim();
      if (String.IsNullOrEmpty(format))
         format = "C"; 

      switch (format.ToUpper())
      {
         // Convert temperature to Fahrenheit and return string.
         case "F":
            return this.Fahrenheit.ToString("N2") + " °F";
         // Convert temperature to Kelvin and return string.
         case "K":
            return this.Kelvin.ToString("N2") + " K";
         // Return temperature in Celsius.
         case "C":
            return this.Celsius.ToString("N2") + " °C";
         default:
            throw new FormatException(String.Format("The '{0}' format string is not supported.", format));
      }      
   }
}

public class Example
{
   public static void Demo(System.Windows.Controls.TextBlock outputBlock)
   {
      Temperature temp1 = new Temperature(0m);
      outputBlock.Text += temp1.ToString() + "\n";
      outputBlock.Text += temp1.ToString("C") + "\n";
      outputBlock.Text += temp1.ToString("F") + "\n";
      outputBlock.Text += temp1.ToString("K") + "\n";

      Temperature temp2 = new Temperature(-40m);
      outputBlock.Text += temp2.ToString() + "\n";
      outputBlock.Text += temp2.ToString("C") + "\n";
      outputBlock.Text += temp2.ToString("F") + "\n";
      outputBlock.Text += temp2.ToString("K") + "\n";

      Temperature temp3 = new Temperature(16m);
      outputBlock.Text += temp3.ToString() + "\n";
      outputBlock.Text += temp3.ToString("C") + "\n";
      outputBlock.Text += temp3.ToString("F") + "\n";
      outputBlock.Text += temp3.ToString("K") + "\n";

      outputBlock.Text += String.Format("The temperature is now {0:F}.\n", temp3);
   }
}
// The example displays the following output:
//       0.00 °C
//       0.00 °C
//       32.00 °F
//       273.15 K
//       -40.00 °C
//       -40.00 °C
//       -40.00 °F
//       233.15 K
//       16.00 °C
//       16.00 °C
//       60.80 °F
//       289.15 K
//       The temperature is now 16.00 °C.

Custom Format Strings

In addition to the standard format strings, the .NET Framework defines custom format specifiers for both numeric values and date and time values. A custom format string consists of one or more custom format specifiers that define the string representation of a value. For example, the custom date and time format string "yyyy/mm/dd hh:mm:ss.ffff t zzz" converts a date to its string representation in the form 2008/11/15 07:45:00.0000 P -08:00 for the en-US culture. Similarly, the custom format string "0000" converts the integer value 12 to "0012".

Many standard format strings are aliases for custom format strings that are defined by properties of the DateTimeFormatInfo or NumberFormatInfo objects. Custom format strings also offer considerable flexibility in providing application-defined formatting for numeric or date and time values.

For a complete list of custom format strings, see Custom Date and Time Format Strings, Custom Numeric Format Strings, and Custom TimeSpan Format Strings.

Format Providers and the IFormatProvider Interface

Although format specifiers let you customize the format of values, producing a meaningful string representation of values often requires additional formatting information. For example, formatting a number as a currency value by using the custom format string "$ #,#.00" requires, at a minimum, that information about the correct currency symbol, group separator, and decimal separator be available to include in the formatted string. In the .NET Framework for Silverlight, this additional formatting information is made available through an IFormatProvider implementation. The following example illustrates how the string representation of an object changes when it is formatted with three different IFormatProvider objects.

Imports System.Globalization

Public Module Example
   Public Sub Demo(outputBlock As System.Windows.Controls.TextBlock)
      Dim value As Decimal = 1603.42d
      outputBlock.Text += value.ToString("C3", New CultureInfo("en-US")) + vbCrLf
      outputBlock.Text += value.ToString("C3", New CultureInfo("fr-FR")) + vbCrLf
      outputBlock.Text += value.ToString("C3", New CultureInfo("de-DE")) + vbCrLf
   End Sub
End Module
' The example displays the following output:
'       $1,603.420
'       1 603,420 €
'       1.603,420 €
using System;
using System.Globalization;

public class Example
{
   public static void Demo(System.Windows.Controls.TextBlock outputBlock)
   {
      decimal value = 1603.42m;
      outputBlock.Text += value.ToString("C3", new CultureInfo("en-US")) + "\n";
      outputBlock.Text += value.ToString("C3", new CultureInfo("fr-FR")) + "\n";
      outputBlock.Text += value.ToString("C3", new CultureInfo("de-DE")) + "\n";
   }
}
// The example displays the following output:
//       $1,603.420
//       1 603,420 €
//       1.603,420 €

The IFormatProvider interface includes a single method, GetFormat, which accepts a Type object that specifies the type of object that provides formatting information. If the method can provide an object of that type, it returns it. Otherwise, it returns a null reference (Nothing in Visual Basic).

IFormatProvider.GetFormat is a callback method. When you call a ToString method overload that includes an IFormatProvider parameter, it calls the GetFormat method. The GetFormat method is responsible for returning an object that provides the necessary formatting information, as specified by its formatType parameter, to the ToString method.

A number of formatting or string conversion methods include a parameter of type IFormatProvider, but in many cases the value of the parameter is ignored when the method is called. The following table lists the formatting methods that use the parameter and the type of the Type object that they pass to the IFormatProvider.GetFormat method.

Method

Type of formatType parameter

ToString of numeric methods

NumberFormatInfo

ToString of date and time methods

DateTimeFormatInfo

Format

ICustomFormatter

AppendFormat

ICustomFormatter

NoteNote:

Although the ToString method of the numeric types and date and time types is overloaded, the remaining overloads call the ToString overload that includes an IFormatProvider parameter. If an explicit IFormatProvider implementation is not provided in the method call, the CultureInfo.CurrentCulture object is passed instead. For example, a call to the default Int32.ToString() method ultimately results in a method call such as the following: Int32.ToString("G", System.Globalization.CultureInfo.CurrentCulture).

The .NET Framework for Silverlight provides three classes that implement IFormatProvider:

You can also implement your own format provider to replace any one of these classes. However, your implementation’s GetFormat method must return an object of the appropriate type if it has to provide formatting information to the ToString method.

The IFormattable Interface

Typically, types that overload the ToString method by using an IFormatProvider implementation and a set of either standard or custom format strings also implement the IFormattable interface. This interface has a single member, IFormattable.ToString, which includes both a format string and a format provider as parameters.

The major advantage of implementing this interface is that its IFormattable.ToString method is called automatically by the Convert.ToString(Object) and Convert.ToString(Object, IFormatProvider) methods. Types that implement the interface should define some behavior for the "G" format specifier, because it is supplied by default if no explicit format string is provided in the method call.

The following example uses the Temperature class to provide an IFormattable implementation.

Imports System.Globalization

Public Class Temperature : Implements IFormattable
   Private m_Temp As Decimal

   Public Sub New(temperature As Decimal)
      Me.m_Temp = temperature
   End Sub

   Public ReadOnly Property Celsius() As Decimal
      Get
         Return Me.m_Temp
      End Get   
   End Property

   Public ReadOnly Property Kelvin() As Decimal
      Get
         Return Me.m_Temp + 273.15d   
      End Get
   End Property

   Public ReadOnly Property Fahrenheit() As Decimal
      Get
         Return Math.Round(CDec(Me.m_Temp * 9 / 5 + 32), 2)
      End Get      
   End Property

   Public Overrides Function ToString() As String
      Return Me.ToString("G", Nothing)
   End Function

   Public Overloads Function ToString(format As String) As String
      Return Me.ToString(format, Nothing)
   End Function

   Public Overloads Function ToString(format As String, provider As IFormatProvider) As String _  
         Implements IFormattable.ToString

      ' Handle null or empty arguments.
      If Not String.IsNullOrEmpty(format) Then format = format.Trim()
      If String.IsNullOrEmpty(format) Then format = "C"
      If provider Is Nothing Then provider = NumberFormatInfo.CurrentInfo

      ' Convert temperature to Fahrenheit and return string.
      Select Case format.ToUpper()
         Case "F"
            Return Me.Fahrenheit.ToString("N2", provider) & " °F"
         ' Convert temperature to Kelvin and return string.
         Case "K"
            Return Me.Kelvin.ToString("N2", provider) & " K"
         ' Return temperature in Celsius.
         Case "C", "G"
            Return Me.Celsius.ToString("N2", provider) & " °C"
         Case Else
            Throw New FormatException(String.Format("The '{0}' format string is not supported.", format))
      End Select      
   End Function
End Class

Public Module Example
   Public Sub Demo(outputBlock As System.Windows.Controls.TextBlock)
      Dim temp1 As New Temperature(22d)
      outputBlock.Text += temp1.ToString("G") + vbCrLf
      outputBlock.Text += Convert.ToString(temp1) + vbCrLf
      outputBlock.Text += Convert.ToString(temp1, Nothing) + vbCrLf
   End Sub
End Module
' The example displays the following output:
'       22.00 °C
'       22.00 °C
'       22.00 °C
using System;
using System.Globalization;

public class Temperature : IFormattable
{
   private decimal m_Temp;

   public Temperature(decimal temperature) {
      this.m_Temp = temperature;
   }

   public decimal Celsius {
      get { return this.m_Temp; }
   }

   public decimal Kelvin {
      get  { return this.m_Temp + 273.15m; }   
   }

   public decimal Fahrenheit {
      get { return Math.Round((decimal)this.m_Temp * 9 / 5 + 32, 2); }
   }

   public override string ToString()
   {
      return this.ToString("G", null);
   }

   public string ToString(string format)
   {
      return this.ToString(format, null);
   }

   public string ToString(string format, IFormatProvider provider)
   {     
      // Handle null or empty string.
      if (! String.IsNullOrEmpty(format))
         format = format.Trim();
      if (String.IsNullOrEmpty(format))
         format = "C"; 
      if (provider == null)
         provider = NumberFormatInfo.CurrentInfo;   

      switch (format.ToUpper())
      {
         // Convert temperature to Fahrenheit and return string.
         case "F":
            return this.Fahrenheit.ToString("N2", provider) + " °F";
         // Convert temperature to Kelvin and return string.
         case "K":
            return this.Kelvin.ToString("N2", provider) + " K";
         // Return temperature in Celsius.
         case "C":
         case "G":
            return this.Celsius.ToString("N2", provider) + " °C";
         default:
            throw new FormatException(String.Format("The '{0}' format string is not supported.", format));
      }      
   }
}

public class Example
{
   public static void Demo(System.Windows.Controls.TextBlock outputBlock)
   {
      Temperature temp1 = new Temperature(22m);
      outputBlock.Text += temp1.ToString("G") + "\n";
      outputBlock.Text += Convert.ToString(temp1) + "\n";
      outputBlock.Text += Convert.ToString(temp1, null) + "\n";
   }
}
// The example displays the following output:
//       22.00 °C
//       22.00 °C
//       22.00 °C

Composite Formatting

Some of the overloads of methods such as String.Format and StringBuilder.AppendFormat support composite formatting. A composite format string is a kind of template that is used to return a single string that incorporates the string representation of zero, one, or more objects. Each object is represented in the composite format string by an indexed format item. The index of the format item corresponds to the position of the object that it represents in the parameter list. Indexes are zero-based. For example, in the following method call the first format item, {0:D}, is replaced by the string representation of thatDate; the second format item, {1}, is replaced by the string representation of inventory1; and the third format item, {2:C2}, is replaced by the string representation of inventory1.Value.

result = String.Format("On {0:D}, the value of {1} was {2:C2}.", thatDate, inventory1, inventory1.Value);

For more information about composite formatting, see Composite Formatting.

Custom Formatting with ICustomFormatter

Some composite formatting methods also include a format provider parameter that supports custom formatting. When the formatting method is called, it passes the format provider’s GetFormat method a Type object that represents an ICustomFormatter interface. The GetFormat method is then responsible for returning the ICustomFormatter implementation that provides custom formatting.

The ICustomFormatter interface has a single method, Format, that is passed a format string, an object that represents the argument to format, and an IFormatProvider implementation. Format returns a custom formatted string. The method is called once for each format item in the composite format string. Typically, the class that implements ICustomFormatter also implements IFormatProvider.

The following example provides an ICustomFormatter implementation that displays integer values as a sequence of two-digit hexadecimal values followed by a space.

Public Class ByteByByteFormatter : Implements IFormatProvider, ICustomFormatter
   Public Function GetFormat(formatType As Type) As Object _
                   Implements IFormatProvider.GetFormat
      If formatType Is GetType(ICustomFormatter) Then
         Return Me
      Else
         Return Nothing
      End If
   End Function

   Public Function Format(fmt As String, arg As Object, _
                          formatProvider As IFormatProvider) As String _
                          Implements ICustomFormatter.Format
      ' Handle only hexadecimal format string.
      If Not fmt.StartsWith("X") Then Return Nothing

      ' Handle only integral types.
      If Not typeof arg Is Byte AndAlso _
         Not typeof arg Is Int16 AndAlso _
         Not typeof arg Is Int32 AndAlso _
         Not typeof arg Is Int64 AndAlso _
         Not typeof arg Is SByte AndAlso _
         Not typeof arg Is UInt16 AndAlso _
         Not typeof arg Is UInt32 AndAlso _
         Not typeof arg Is UInt64 Then _
            Return Nothing

      Dim bytes() As Byte = BitConverter.GetBytes(arg)
      Dim output As String = Nothing

      For ctr As Integer = bytes.Length - 1 To 0 Step -1
         output += String.Format("{0:X2} ", bytes(ctr))   
      Next

      Return output.Trim()
   End Function
End Class
public class ByteByByteFormatter : IFormatProvider, ICustomFormatter
{
   public object GetFormat(Type formatType)
   { 
      if (formatType == typeof(ICustomFormatter))
         return this;
      else
         return null;
   }

   public string Format(string format, object arg, 
                          IFormatProvider formatProvider)
   {   
      // Handle only hexadecimal format string.
      if (! format.StartsWith("X")) return null;

      byte[] bytes;
      string output = null;

      // Handle only integral types.
      if (arg is Byte) 
         bytes = BitConverter.GetBytes((Byte) arg);
      else if (arg is Int16)
         bytes = BitConverter.GetBytes((Int16) arg);
      else if (arg is Int32)
         bytes = BitConverter.GetBytes((Int32) arg);
      else if (arg is Int64)   
         bytes = BitConverter.GetBytes((Int64) arg);
      else if (arg is SByte)
         bytes = BitConverter.GetBytes((SByte) arg);
      else if (arg is UInt16)
         bytes = BitConverter.GetBytes((UInt16) arg);
      else if (arg is UInt32)
         bytes = BitConverter.GetBytes((UInt32) arg);
      else if (arg is UInt64)
         bytes = BitConverter.GetBytes((UInt64) arg);
      else
         return null;

      for (int ctr = bytes.Length - 1; ctr >= 0; ctr--)
         output += String.Format("{0:X2} ", bytes[ctr]);   

      return output.Trim();
   }
}

The following example illustrates the use of the ICustomFormatter object to format integer values. Note that the Format method is called more than once in the second String.Format(IFormatProvider, String, array<Object[]) method call.

Public Module Example
   Public Sub Demo(outputBlock As System.Windows.Controls.TextBlock)
      Dim value As Long = 3210662321 
      Dim value1 As Byte = 214
      Dim value2 As Byte = 19

      outputBlock.Text += (String.Format(New ByteByByteFormatter(), "{0:X}", value)) + vbCrLf
      outputBlock.Text += (String.Format(New ByteByByteFormatter(), "{0:X} And {1:X} = {2:X} ({2:000})", _
                                      value1, value2, value1 And value2)) + vbCrLf                                
   End Sub
End Module
' The example displays the following output:
'       00 00 00 00 BF 5E D1 B1
'       00 D6 And 00 13 = 00 12 (018)
public class Example
{
   public static void Demo(System.Windows.Controls.TextBlock outputBlock)
   {
      long value = 3210662321; 
      byte value1 = 214;
      byte value2 = 19;

      outputBlock.Text += String.Format(new ByteByByteFormatter(), "{0:X}\n", value);
      outputBlock.Text += String.Format(new ByteByByteFormatter(), "{0:X} And {1:X} = {2:X} ({2:000})\n", 
                                      value1, value2, value1 & value2);                                
   }
}
// The example displays the following output:
//       00 00 00 00 BF 5E D1 B1
//       00 D6 And 00 13 = 00 00 00 12 (018)