So how do you custom format the strings in .Net? You got it - you use IFormatProvider. IFormatProvider is an interface that provides you an option to format the value as per your requirements. The sole member contained in this interface is GetFormat(). To provide the custom formatting, you need to make a class that implements IFormatProvider. Apart from this, your class must also implement ICustomFormatter. This is the interface that actually handles the custom logic of formatting the supplied value. ICustomFormatter contains a single method named Format() which accepts the format in which value should be formatted, the value that is to be formatted and an instance of IFormatProvider(i.e your custom class that implements IFormatProvider).
Lets get down to the code and see what it need to do a custom formatting:-
Our requirement: We have a string that contains the phone number. The phone number is a standard 10 digit number. However, it may or may not have a country code attached. If the country code is present, the format will be '+' followed by country code, a space and then the 10 digit phone number. So, example of valid values are:
Phone number without country code: 1234567890
Phone number with country code: +91 1234567890
Lets look at the implementation of our class that handles the formatting:
As you can see, we have a class defined, that implements IFormatProvider and ICustomFormatter. I have commented code at each step so that it is easier to understand whats going on.public class PhoneFormatter : IFormatProvider, ICustomFormatter
{
#region IFormatProvider Members
public object GetFormat(Type formatType)
{
// Check if the class implements ICustomFormatter
if (formatType == typeof(ICustomFormatter))
{
return this;
}
else
{
return null;
}
}
#endregion
#region ICustomFormatter Members
public string Format(string format, object arg, IFormatProvider formatProvider)
{
// if the passed in argument is null, return empty string
if (arg == null)
{
return string.Empty;
}
// Get the value of argument in string
string phoneNumber = arg.ToString();
// Check if phone number has country code
if (phoneNumber.StartsWith("+") && phoneNumber.IndexOf(' ') > 1)
{
// If it contains country code, separate it from phone number
string countryCode = phoneNumber.Substring(0, phoneNumber.IndexOf(' ') + 1);
phoneNumber = phoneNumber.Remove(0, countryCode.Length);
// Get the formatted value of phone number and prefix it with the country code
phoneNumber = string.Format("{0}{1}", countryCode, this.GetFormattedPhoneNumber(phoneNumber, format));
}
// Check if the phone number is a valid 10 digit number
if (phoneNumber.Length == 10)
{
// Get the formatted value of phone number
phoneNumber = this.GetFormattedPhoneNumber(phoneNumber, format);
}
return phoneNumber;
}
#endregion
#region Helper method
private string GetFormattedPhoneNumber(string phoneNumber, string format)
{
long number = 0;
//Check if the phone number is a valid numeric value
if (long.TryParse(phoneNumber, out number))
{
// If phone number is numeric, format it as per the passed in value
phoneNumber = number.ToString(format);
}
return phoneNumber;
}
#endregion
}
Now, lets have a look at its usage:
string phoneNumber = "+91 1234567890";
Console.WriteLine(string.Format(new PhoneFormatter(), "{0:(###) ###-####}", phoneNumber));
This outputs: +91 (123) 456-7890
Similarly, following code produces (123) 456-7890
string phoneNumber = "1234567890";
Console.WriteLine(string.Format(new PhoneFormatter(), "{0:(###) ###-####}", phoneNumber));
Another interesting implementation could be in case of displaying Bank account numbers where only last 4 or 5 digits are displayed and rest are shown as '*' (e.g. ****-****-1234). Its not that this can't be achieved by string operations like substring, but its just that this approach provides you a more structured, managable and reusable way of formatting values.
I hope that this article was easy to follow and enjoyable to you as much as it was to me writing it.