@@ -64,18 +64,7 @@ protected override void OnParametersSet()
6464
6565 if ( ! string . IsNullOrEmpty ( Format ) )
6666 {
67- // TODO: Consider using reflection to avoid having to box every value just to call IFormattable.ToString
68- // For example, define a method "string Type<U>(Func<TGridItem, U> property) where U: IFormattable", and
69- // then construct the closed type here with U=TProp when we know TProp implements IFormattable
70-
71- // If the type is nullable, we're interested in formatting the underlying type
72- var nullableUnderlyingTypeOrNull = Nullable . GetUnderlyingType ( typeof ( TProp ) ) ;
73- if ( ! typeof ( IFormattable ) . IsAssignableFrom ( nullableUnderlyingTypeOrNull ?? typeof ( TProp ) ) )
74- {
75- throw new InvalidOperationException ( $ "A '{ nameof ( Format ) } ' parameter was supplied, but the type '{ typeof ( TProp ) } ' does not implement '{ typeof ( IFormattable ) } '.") ;
76- }
77-
78- _cellTextFunc = item => ( ( IFormattable ? ) compiledPropertyExpression ! ( item ) ) ? . ToString ( Format , null ) ;
67+ _cellTextFunc = CreateFormatter ( compiledPropertyExpression , Format ) ;
7968 }
8069 else
8170 {
@@ -87,10 +76,8 @@ protected override void OnParametersSet()
8776 {
8877 return ( value as Enum ) ? . GetDisplayName ( ) ;
8978 }
90- else
91- {
92- return value ? . ToString ( ) ;
93- }
79+
80+ return value ? . ToString ( ) ;
9481 } ;
9582 }
9683 if ( Sortable . HasValue )
@@ -118,6 +105,57 @@ protected override void OnParametersSet()
118105 }
119106 }
120107
108+ private static Func < TGridItem , string ? > CreateFormatter ( Func < TGridItem , TProp > getter , string format )
109+ {
110+ var closedType = typeof ( PropertyColumn < , > ) . MakeGenericType ( typeof ( TGridItem ) , typeof ( TProp ) ) ;
111+
112+ //Nullable struct
113+ if ( Nullable . GetUnderlyingType ( typeof ( TProp ) ) is Type underlying &&
114+ typeof ( IFormattable ) . IsAssignableFrom ( underlying ) )
115+ {
116+ var method = closedType
117+ . GetMethod ( nameof ( CreateNullableValueTypeFormatter ) , BindingFlags . NonPublic | BindingFlags . Static ) !
118+ . MakeGenericMethod ( underlying ) ;
119+ return ( Func < TGridItem , string ? > ) method . Invoke ( null , [ getter , format ] ) ! ;
120+ }
121+
122+
123+ if ( typeof ( IFormattable ) . IsAssignableFrom ( typeof ( TProp ) ) )
124+ {
125+ //Struct
126+ if ( typeof ( TProp ) . IsValueType )
127+ {
128+ var method = closedType
129+ . GetMethod ( nameof ( CreateValueTypeFormatter ) , BindingFlags . NonPublic | BindingFlags . Static ) !
130+ . MakeGenericMethod ( typeof ( TProp ) ) ;
131+ return ( Func < TGridItem , string ? > ) method . Invoke ( null , [ getter , format ] ) ! ;
132+ }
133+
134+ //Double cast required because CreateReferenceTypeFormatter required the TProp to be a reference type which implements IFormattable.
135+ return CreateReferenceTypeFormatter ( ( Func < TGridItem , IFormattable ? > ) ( object ) getter , format ) ;
136+ }
137+
138+ throw new InvalidOperationException ( $ "A '{ nameof ( Format ) } ' parameter was supplied, but the type '{ typeof ( TProp ) } ' does not implement '{ typeof ( IFormattable ) } '.") ;
139+ }
140+
141+ private static Func < TGridItem , string ? > CreateReferenceTypeFormatter < T > ( Func < TGridItem , T ? > getter , string format )
142+ where T : class , IFormattable
143+ {
144+ return item => getter ( item ) ? . ToString ( format , null ) ;
145+ }
146+
147+ private static Func < TGridItem , string ? > CreateValueTypeFormatter < T > ( Func < TGridItem , T > getter , string format )
148+ where T : struct , IFormattable
149+ {
150+ return item => getter ( item ) . ToString ( format , null ) ;
151+ }
152+
153+ private static Func < TGridItem , string ? > CreateNullableValueTypeFormatter < T > ( Func < TGridItem , T ? > getter , string format )
154+ where T : struct , IFormattable
155+ {
156+ return item => getter ( item ) ? . ToString ( format , null ) ;
157+ }
158+
121159 /// <inheritdoc />
122160 protected internal override void CellContent ( RenderTreeBuilder builder , TGridItem item )
123161 => builder . AddContent ( 0 , _cellTextFunc ? . Invoke ( item ) ) ;
0 commit comments