I have a LookUpEdit control and I need set property value to NullText with reflection, but I'm getting the TargetException:
private static void SetObjectProperty(string propiedad, string valor, object obj)
{
if (obj.GetType() == typeof(LookUpEdit))
{
string[] vv = propiedad.Split('.');
string prop = vv[0];
string propType = vv[1];
var p = obj.GetType().GetProperty(prop, BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly);
PropertyInfo propertyInfo = p.PropertyType.GetProperty(propType);
if (propertyInfo != null)
{
propertyInfo.SetValue(obj, valor, null);
}
}
}
I only get the exception with LookUpEdit control.
"propiedad" is a string contains "Properties.NullText" so this is why I'm doing a split
You should apply operations with nested properties to corresponding nested objects:
static void SetObjectProperty(object obj, string propertyPath, object value) {
if(obj != null && obj.GetType() == typeof(LookUpEdit)) {
string[] parts = propertyPath.Split('.');
var rootInfo = typeof(LookUpEdit).GetProperty(parts[0],
BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly);
object root = rootInfo.GetValue(obj); // obtaining a root
var nestedInfo = rootInfo.PropertyType.GetProperty(parts[1]);
if(nestedInfo != null)
nestedInfo.SetValue(root, value, null); // using root object
}
}
PS. Why you're using this ugly way of modifying object properties?
Related
Is it possible to discover all dynamic resources within a datatemplate - either within the datatemplate itself, or after it was applied to some ContentPresenter?
My idea was to make some sort of property editor to edit the appearance of wpf objects (maybe dynamically created using XamlReader) and show - for a certain object, only the resource entries used inside the corresponding DataTemplate.
Please look at Snoop utility. You can look at the source code and see how you can view the style/template of any object and change its appearance.
Up to now, I have not found a "clean" way to get the dynamic resource using framework methods.
However, one possibility is to search the DataTemplate using reflection. The dynamic resources are stored inside structs of type System.Windows.ChildValueLookup where the LookupType is "Resource". I have written a helper class to enumerate the resources:
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Windows;
namespace DashboardTest
{
public struct ResourceInfo
{
public ResourceInfo(string resourceKey, DependencyProperty property)
: this()
{
ResourceKey = resourceKey;
Property = property;
}
public string ResourceKey { get; private set; }
public DependencyProperty Property { get; set; }
}
public static class DataTemplateHelper
{
private static readonly Type ChildValueLookupType = GetType("System.Windows.ChildValueLookup");
private static readonly FieldInfo LookupTypeField = ChildValueLookupType.GetField("LookupType", BindingFlags.Instance | BindingFlags.NonPublic);
private static readonly FieldInfo ValueField = ChildValueLookupType.GetField("Value", BindingFlags.Instance | BindingFlags.NonPublic);
private static readonly FieldInfo PropertyField = ChildValueLookupType.GetField("Property", BindingFlags.Instance | BindingFlags.NonPublic);
private static readonly object LookupTypeResource = Enum.Parse(LookupTypeField.FieldType, "Resource");
public static List<ResourceInfo> FindDynamicResources(DataTemplate template)
{
var recordField = typeof(DataTemplate).GetField("ChildRecordFromChildIndex", BindingFlags.Instance | BindingFlags.NonPublic);
Debug.Assert(recordField != null);
var values = EnumerateObjects(recordField.GetValue(template)).Where(IsDynamicResource).Select(ToResourceInfo).ToList();
return values;
}
private static ResourceInfo ToResourceInfo(object lookup)
{
return new ResourceInfo((string)ValueField.GetValue(lookup), (DependencyProperty)PropertyField.GetValue(lookup));
}
private static Type GetType(string typeName)
{
foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())
{
Type type = assembly.GetType(typeName);
if (type != null)
return type;
}
return null;
}
private static bool IsDynamicResource(object obj)
{
if (obj == null || obj.GetType() != ChildValueLookupType) return false;
return LookupTypeResource.Equals(LookupTypeField.GetValue(obj));
}
private static IEnumerable<object> EnumerateObjects(object obj)
{
var visited = new HashSet<object>();
EnumerateObjectsInternal(obj, visited, 20);
return visited;
}
private static void EnumerateObjectsInternal(object obj, HashSet<object> visited, int maxDepth)
{
if (obj == null || maxDepth <= 0) return;
var type = obj.GetType();
if (obj is Type || obj is string || obj is DateTime || type.IsPrimitive
|| type.IsEnum || visited.Add(obj) == false) return;
var array = obj as Array;
if (array != null)
{
foreach (var item in array)
{
EnumerateObjectsInternal(item, visited, maxDepth - 1);
}
}
else
{
foreach (var field in GetAllFields(type))
{
var fieldValue = field.GetValue(obj);
EnumerateObjectsInternal(fieldValue, visited, maxDepth - 1);
}
}
}
private static IEnumerable<FieldInfo> GetAllFields(Type type)
{
const BindingFlags bindingFlags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly;
if (type == typeof(object) || type.BaseType == typeof(object))
{
return type.GetFields(bindingFlags);
}
else
{
var fieldInfoList = new List<FieldInfo>();
var currentType = type;
while (currentType != typeof(object))
{
fieldInfoList.AddRange(currentType.GetFields(bindingFlags));
currentType = currentType.BaseType;
}
return fieldInfoList;
}
}
}
}
I am using reflection to assign properties for controls as the control properties are stored in the database.And I am facing issues with following situation.
I have raddateinput control and it is having a property DateInput.DataFormat and when I tried get the propertyinfo for the using following code it returns as null.
ctrl.GetType().GetProperty(propertyName.Split('.').FirstOrDefault(), BindingFlags.Public | BindingFlags.Instance)
.GetType().GetProperty(propertyName.Split('.').LastOrDefault())
ctrl is a Control. propertyName is DateInput.DataFormat
I achieved the solution with the following code.
Private void SetProperty(Object ctrl, string propertyName, string value)
{
string name = propertyName.Split('.').First();
PropertyInfo property = ctrl.GetType().GetProperty(name, BindingFlags.Public | BindingFlags.Instance);
if (name != propertyName)
{
ctrl = property.GetValue(ctrl, null);
SetProperty(ctrl, propertyName.Replace(string.Concat(name, "."), string.Empty), value);
return;
}
TypeConverter converter = TypeDescriptor.GetConverter(property.PropertyType);
if (converter != null && converter.CanConvertFrom(typeof(String)))
property.SetValue(ctrl, converter.ConvertFrom(value), null);
}
i have used the following code to change the current value for the current field value as
FieldInfo connectionStringField = GetType().BaseType.GetField("_sqlConnectionString", BindingFlags.Instance | BindingFlags.NonPublic);
connectionStringField.SetValue(this, connectionString);
but my query is to get current value of connectionstringfied...
i tried the below code as
getvalue(obj ss);
waiting for your valuable esponses
it throws me null values
If connectionStringField has found the field (i.e. it is in the base type and is called "_sqlConnectionString", then it should just be:
string connectionString = (string)connectionStringField.GetValue(this);
?
However, using reflection to talk to non-public fields is... unusual.
public static string GetPropertyValue<T>(this T obj, string parameterName)
{
PropertyInfo[] property = null;
Type typ = obj.GetType();
if (listPropertyInfo.ContainsKey(typ.Name))
{
property = listPropertyInfo[typ.Name];
}
else
{
property = typ.GetProperties();
listPropertyInfo.TryAdd(typ.Name, property);
}
return property.First(p => p.Name == parameterName).GetValue(obj, null).ToString();
}
listPropertyInfo is a cache to avoid reflection performance issue
public static void SetPropertyValue<T>(this T obj, string parameterName, object value)
{
PropertyInfo[] property = null;
Type typ = obj.GetType();
if (listPropertyInfo.ContainsKey(typ.Name))
{
property = listPropertyInfo[typ.Name];
}
else
{
property = typ.GetProperties();
listPropertyInfo.TryAdd(typ.Name, property);
}
if (value == DBNull.Value)
{
value = null;
}
property.First(p => p.Name == parameterName).SetValue(obj,value, null);
}
I used the same trick for setters
I am trying to set a value to a Nested Property of Class dynamically using reflection. Could anyone help me to do this.
I am having a class Region like below.
public class Region
{
public int id;
public string name;
public Country CountryInfo;
}
public class Country
{
public int id;
public string name;
}
I have a Oracle Data reader to provide the Values from the Ref cursor.
which will give me as
Id,name,Country_id,Country_name
I could able to assign the values to the Region.Id, Region.Name by below.
FieldName="id"
prop = objItem.GetType().GetProperty(FieldName, BindingFlags.Public | BindingFlags.Instance);
prop.SetValue(objItem, Utility.ToLong(reader_new[ResultName]), null);
And for the Nested Property I could able to do the assign values to the as below by creating a Instance by reading the Fieldname.
FieldName="CountryInfo.id"
if (FieldName.Contains('.'))
{
Object NestedObject = objItem.GetType().GetProperty(Utility.Trim(FieldName.Split('.')[0]), BindingFlags.Public | BindingFlags.Instance);
//getting the Type of NestedObject
Type NestedObjectType = NestedObject.GetType();
//Creating Instance
Object Nested = Activator.CreateInstance(typeNew);
//Getting the nested Property
PropertyInfo nestedpropinfo = objItem.GetType().GetProperty(Utility.Trim(FieldName.Split('.')[0]), BindingFlags.Public | BindingFlags.Instance);
PropertyInfo[] nestedpropertyInfoArray = nestedpropinfo.PropertyType.GetProperties();
prop = nestedpropertyInfoArray.Where(p => p.Name == Utility.Trim(FieldName.Split('.')[1])).SingleOrDefault();
prop.SetValue(Nested, Utility.ToLong(reader_new[ResultName]), null);
Nestedprop = objItem.GetType().GetProperty(Utility.Trim(FieldName.Split('.')[0]), BindingFlags.Public | BindingFlags.Instance);
Nestedprop.SetValue(objItem, Nested, null);
}
The above assign values to Country.Id.
But Since I am creating instance each and every time I could not able to get the previous Country.Id value if I go for the Next Country.Name.
Could anybody tell could to assign values to the objItem(that is Region).Country.Id and objItem.Country.Name. Which means how to assign values to the Nested Properties instead of creating instance and assigning everytime.
Thanks in advance.!
You should be calling PropertyInfo.GetValue using the Country property to get the country, then PropertyInfo.SetValue using the Id property to set the ID on the country.
So something like this:
public void SetProperty(string compoundProperty, object target, object value)
{
string[] bits = compoundProperty.Split('.');
for (int i = 0; i < bits.Length - 1; i++)
{
PropertyInfo propertyToGet = target.GetType().GetProperty(bits[i]);
target = propertyToGet.GetValue(target, null);
}
PropertyInfo propertyToSet = target.GetType().GetProperty(bits.Last());
propertyToSet.SetValue(target, value, null);
}
Get Nest properties e.g., Developer.Project.Name
private static System.Reflection.PropertyInfo GetProperty(object t, string PropertName)
{
if (t.GetType().GetProperties().Count(p => p.Name == PropertName.Split('.')[0]) == 0)
throw new ArgumentNullException(string.Format("Property {0}, is not exists in object {1}", PropertName, t.ToString()));
if (PropertName.Split('.').Length == 1)
return t.GetType().GetProperty(PropertName);
else
return GetProperty(t.GetType().GetProperty(PropertName.Split('.')[0]).GetValue(t, null), PropertName.Split('.')[1]);
}
Expanding on Jon Skeets answer, if the value you're getting is null and you want to create an object for that type:
public void SetProperty(string compoundProperty, object target, object value)
{
var bits = compoundProperty.Split('.');
for (var i = 0; i < bits.Length - 1; i++) {
var propertyToGet = target.GetType().GetProperty(bits[i]);
var propertyValue = propertyToGet.GetValue(target, null);
if (propertyValue == null) {
propertyValue = Activator.CreateInstance(propertyToGet.PropertyType);
propertyToGet.SetValue(target, propertyValue);
}
target = propertyToGet.GetValue(target, null);
}
var propertyToSet = target.GetType().GetProperty(bits.Last());
propertyToSet.SetValue(target, value, null);
}
Expanding on Luke Garrigan Answer:
If you want to have it working also with non lists too:
public void SetProperty(string compoundProperty, object target, object value)
{
var bits = compoundProperty.Split('.');
if(bits == null) bits = new string[1]{compoundProperty};
for (var i = 0; i < bits.Length - 1; i++) {
var propertyToGet = target.GetType().GetProperty(bits[i]);
var propertyValue = propertyToGet.GetValue(target, null);
if (propertyValue == null) {
propertyValue = Activator.CreateInstance(propertyToGet.PropertyType);
propertyToGet.SetValue(target, propertyValue);
}
target = propertyToGet.GetValue(target, null);
}
var propertyToSet = target.GetType().GetProperty(bits.Last());
propertyToSet.SetValue(target, value, null);
}
I have this method and want to get all properties from the FieldInfos? How to get it?
private static void FindFields(ICollection<FieldInfo> fields, Type t)
{
var flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance | BindingFlags.DeclaredOnly;
foreach (var field in t.GetFields(flags))
{
fields.Add(field);
}
var baseType = t.BaseType;
if (baseType != null)
{
FindFields(fields, baseType);
}
}
var fields = new Collection<FieldInfo>();
FindFields(fields, this.GetType());
Thanks.
Best regards.
To get the value of a field for a specific object use GetValue and pass the object for which you want to get the value.
var fields = new Collection<FieldInfo>();
FindFields(fields, this.GetType());
foreach (var field in fields)
{
Console.WriteLine( "{0} = {1}", field.Name , field.GetValue(this));
}