I'm trying to figure out how IFormatProvider and ICustomFormatter work after following Format TimeSpan in DataGridView column on how to customize a TimeSpan in a DataGridView. I've created a completely custom formatter that always returns "foo" regardless of what it is formatting.
I'm using it on Int but I assume it should work on all types as it doesn't check the value being passed, it just returns "foo"
.
class MyFormatter : IFormatProvider, ICustomFormatter
{
public object GetFormat(Type formatType)
{
Console.WriteLine("GetFormat");
return this;
}
public string Format(string format, object arg, IFormatProvider formatProvider)
{
Console.WriteLine("Format");
return "foo";
}
}
And I'm passing it to int.ToString()
:
int number = 10;
Console.WriteLine(number.ToString(new MyFormatter()));
What I'm getting is:
GetFormat 10
While what I was hoping to get is:
GetFormat Format foo
Edit: I found How to create and use a custom IFormatProvider for DateTime? and the answers there say that DateTime.ToString()
will not accept anything but DateTimeFormatInfo
or CultureInfo
and an object will be rejected if it's not of these types even if it implements ICustomFormatter
- https://stackoverflow.com/a/2382481/492336.
So my question is then does that hold in all cases of the ToString()
methods? Does it hold also for DataGridView, and in which cases can I pass a truly custom formatter?
When you call ToString on integer and provide IFormatProvider, it will try to extract NumberFormatInfo from it, roughly in the following way:
public static NumberFormatInfo GetInstance(IFormatProvider formatProvider)
{
CultureInfo cultureInfo = formatProvider as CultureInfo;
if (cultureInfo != null && !cultureInfo.m_isInherited)
return cultureInfo.numInfo ?? cultureInfo.NumberFormat;
NumberFormatInfo numberFormatInfo = formatProvider as NumberFormatInfo;
if (numberFormatInfo != null)
return numberFormatInfo;
if (formatProvider != null)
{
NumberFormatInfo format = formatProvider.GetFormat(typeof (NumberFormatInfo)) as NumberFormatInfo;
if (format != null)
return format;
}
return NumberFormatInfo.CurrentInfo;
}
So you see if all else fails, it calls GetFormat
with type equal to NumberFormatInfo
, and expects NumberFormatInfo
back. You don't return it from GetFormat
, so it uses default formatter instead (current cutlure). So valid way to use it in this case would be something like:
class MyFormatter : IFormatProvider, ICustomFormatter
{
public object GetFormat(Type formatType)
{
if (formatType == typeof(NumberFormatInfo)) {
return new NumberFormatInfo()
{
// something here
};
}
Console.WriteLine("GetFormat");
return this;
}
public string Format(string format, object arg, IFormatProvider formatProvider)
{
Console.WriteLine("Format");
return "foo";
}
}
But this way I doubt you can return arbitrary values like "foo" for any number.