That leads to the fact that I dont know the type of the objects my grid is bound to at compile time, becuase the type is dynamically generated at runtime. I have the scenario that the user clicks to "Edit current entry", then a new dialog opens showing the details for the item selected within the grid. What I needed was a method that gives me the Value of the primary key field of this object.
To avoid writing reflection code every time, I intruduced two extender called GetPropertyValue and SetPropertyValue.
First of all I need to be able to get a PropertyInfo object from a given Property path. I wanted to support multi level pathes (such Person.Address.ZipCode). Indexed properties are not supported.
The code to retrieve the PropertyInfo is recursive. As long as the path contains a "." I get the value of the head property using GetPropertyValue extender and then get the next property for the rest paht using a recursive call to GetProperty:
public static PropertyInfo GetProperty(this object target, string path)
{
if ((target == null) || string.IsNullOrEmpty(path))
{
return null;
}
String CurrentPath = String.Empty;
if (path.Contains('.'))
{
String[] pathParts = path.Split(new char[] { '.' });
String restPath = path.StripLeft(pathParts[0].Length + 1);
CurrentPath = pathParts[0];
return target.GetPropertyValue<Object>(CurrentPath).GetProperty(restPath);
}
PropertyInfo property = target.GetType().GetProperty(path, BindingFlags.IgnoreCase | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
return property;
}
public static T GetPropertyValue<T>(this object target, string path)
{
PropertyInfo property = target.GetProperty(path);
if (property == null)
{
return default(T);
}
try
{
return property.GetValue(target, null).To<T>(default(T));
}
catch
{
return default(T);
}
}
Using
if (path.Contains('.'))
I check wether I need to make a recursive call. If so, the path is split up by the '.'. I then take the head of the path and the rest path. Then I do the recursive call:return target.GetPropertyValue<Object>(CurrentPath).GetProperty(restPath);
If there is no dot in the path left I get the PropertyInfo using GetType().GetPropert(path). It was important to me to get the Property using a case invariant path, so 'Name' and 'NAME' should both retrieve the 'Name' property, so I had to provide the correct binding flags.
The GetPropertyValue extender then just delivers the value of the property via the normal reflection api. To get the correct type I use the To
public static T To<T>(this object Value, T DefaultValue)
{
try
{
// Check if target type is a nullable
Type TypeOfT = typeof(T);
if (TypeOfT.IsGenericType && TypeOfT.GetGenericTypeDefinition() == typeof(Nullable<>))
{
// Check if value is not a nullable type
Type TypeOfValue = Value.GetType();
if (!TypeOfValue.IsGenericType || !(TypeOfValue.GetGenericTypeDefinition() == typeof(Nullable<>)))
{
// Target type is a nullable, but value type is not => cast the value to the nullables target type first
Type TypeOfNullable = TypeOfT.GetGenericArguments()[0];
Object ValueInTargetType = Convert.ChangeType(Value, TypeOfNullable, LanguageContext.DefaultInstance.Culture);
// Return converted value as Nullable<TypeOfValue>
return (T)ValueInTargetType;
}
}
// Either both types are nullable or neither types are nullable, so just convert them
return (T)Convert.ChangeType(Value, typeof(T), LanguageContext.DefaultInstance.Culture);
}
catch (Exception ex)
{
return DefaultValue;
}
}
This code can handle the conversion of nullable types to its underlying type and vice versa as well.
Having this in place, SetPropertyValue is easy:
public static void SetPropertyValue<T>(this object target, string path, T value)
{
PropertyInfo property = target.GetProperty(path);
if (property == null)
{
return;
}
try
{
property.SetValue(target, value, null);
}
catch
{
return;
}
}
Actually I am thinking about a small fluent reflection API that hides you from all the GetType() kind of stuff. I will blog about this another time.
Have fun!.