identify what property changed, and edit all selected Items - c#

I recently found how to add an event when changing a property of an Item in my ListView :
foreach (Profil item in gp.ListProfils)
{
if (item is INotifyPropertyChanged)
{
INotifyPropertyChanged observable = (INotifyPropertyChanged)item;
observable.PropertyChanged +=
new PropertyChangedEventHandler(ItemPropertyChanged);
}
}
Now, I would like to apply the modify to all selected items.
I found how to identify the name of property changed :
private void ItemPropertyChanged(object sender, PropertyChangedEventArgs e)
{
if(sender is Profil)
{
string myPropertyName = e.PropertyName;
Profil editedProfile = (Profil)sender;
foreach(Profil prf in this.SelectedProfiles)
{
//prf.NAME_PROPERTY = editedProfile.NAME_PROPERTY
if (!this.ListProfilToEdit.Contains(editedProfile))
{
this.ListProfilToEdit.Add(editedProfile);
}
}
}
}
But how can I replace the line in comment.
Concretely if I edited the field "Width", I want to change the width for all selected elements.
Is there a way to do it, without creating a separated function EditProperty(string nameProperty)?

Here you need to be able to read a property dynamically from an object and then write it dynamically on another object.
You can use Expression Trees to generate Getters & Setters dynamically and them put in the cache.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
namespace CoreAppMain
{
public class TypeAccessors<T> where T : class
{
//Getters
public static Dictionary<string, Func<T, object>> Getters = GetTypeGetters();
private static Dictionary<string, Func<T, object>> GetTypeGetters()
{
var _getters = new Dictionary<string, Func<T, object>>();
var props = typeof(T).GetProperties();
foreach (var p in props)
{
var entityParam = Expression.Parameter(typeof(T), "e");
Expression columnExpr = Expression.Property(entityParam, p);
Expression conversion = Expression.Convert(columnExpr, typeof(object));
var fct = Expression.Lambda<Func<T, object>>(conversion, entityParam).Compile();
_getters.Add(p.Name, fct);
}
return _getters;
}
//setters
public static Dictionary<string, Action<T, object>> Setters = GetTypeSetters();
public static Dictionary<string, Action<T, object>> GetTypeSetters()
{
var setters = new Dictionary<string, Action<T, object>>();
const BindingFlags flags = BindingFlags.Public | BindingFlags.Instance;
var propsToSet = typeof(T).GetProperties(flags)
.Where(x => x.CanWrite)
.ToList();
foreach (var field in propsToSet)
{
var propertyInfo = typeof(T).GetProperty(field.Name);
setters.Add(field.Name, GetPropertySetter<T>(propertyInfo));
}
return setters;
}
public static Action<TModel, object> GetPropertySetter<TModel>(PropertyInfo propertyInfo)
{
// Note that we are testing whether this is a value type
bool isValueType = propertyInfo.DeclaringType.IsValueType;
var method = propertyInfo.GetSetMethod(true);
var obj = Expression.Parameter(typeof(object), "o");
var value = Expression.Parameter(typeof(object));
// Note that we are using Expression.Unbox for value types
// and Expression.Convert for reference types
Expression<Action<TModel, object>> expr =
Expression.Lambda<Action<TModel, object>>(
Expression.Call(
isValueType ?
Expression.Unbox(obj, method.DeclaringType) :
Expression.Convert(obj, method.DeclaringType),
method,
Expression.Convert(value, method.GetParameters()[0].ParameterType)),
obj, value);
Action<TModel, object> action = expr.Compile();
return action;
}
}
}
Usage Example:
var cust1 = new Customer()
{
FirstName = "TOTOT",
LastName = "TITI"
};
var cust2 = new Customer()
{
FirstName = "XXXXXX",
LastName = "YYYYYY"
};
var value = TypeAccessors<Customer>.Getters[nameof(Customer.FirstName)](cust2);
TypeAccessors<Customer>.Setters[nameof(Customer.FirstName)](cust1, value);
Console.WriteLine(cust1.FirstName);
Console.ReadKey();

Is there a way to do it, without creating a separated function EditProperty(string nameProperty)?
Creating a separate method or not is a matter of good design and clean code, it does not affect the core of the question in any way. You can always create a separate method or write the code in your existing method.
But how can I replace the line in comment.
It is not exactly clear what you are doing, so I am in doubt whether what you are asking could be solved in different way, but as it is, you are asking to read and write properties of instances by only knowing their names. You will need to use Reflection for this.
For performing late binding, accessing methods on types created at run time. See the topic Dynamically Loading and Using Types.
Assuming your properties are public, this should work:
var propertyInfo = typeof(Profil).GetProperty(myPropertyName);
var value = propertyInfo.GetValue(editedProfile, null);
foreach(Profil prf in this.SelectedProfiles)
{
propertyInfo.SetValue(prf, value);
// ...other code.
}
That said, be careful using reflection, for further reading:
Reflection: Is using reflection still “bad” or “slow”?
If reflection is inefficient, when is it most appropriate?

Related

C# FieldInfo reflection alternatives

I am currently using FieldInfo.GetValue and FieldInfo.SetValue quite a lot in my programm, which is significantly slowing up my programm.
For PropertyInfo I use the GetValueGetter and GetValueSetter methods so I only use reflection once for a given type. For the FieldInfo, the methods don't exist.
What is the suggested approach for FieldInfo?
EDIT:
I followed this useful link from CodeCaster's reply. This is a great search direction.
Now the "only" point that I don't get in this answer is how I can cache the getters / setters and re-use them in a generic way, using only the field's name - which is basically what the SetValue is doing
// generate the cache
Dictionary<string, object> setters = new Dictionary<string, object>();
Type t = this.GetType();
foreach (FieldInfo fld in t.GetFields()) {
MethodInfo method = t.GetMethod("CreateSetter");
MethodInfo genericMethod = method.MakeGenericMethod( new Type[] {this.GetType(), fld.FieldType});
setters.Add(fld.Name, genericMethod.Invoke(null, new[] {fld}));
}
// now how would I use these setters?
setters[fld.Name].Invoke(this, new object[] {newValue}); // => doesn't work without cast....
You could use Expressions to generate faster field accessors. If you want them to work on Object rather than the field's concrete type, you'd have to add casts (Convert) in the expression.
using System.Linq.Expressions;
class FieldAccessor
{
private static readonly ParameterExpression fieldParameter = Expression.Parameter(typeof(object));
private static readonly ParameterExpression ownerParameter = Expression.Parameter(typeof(object));
public FieldAccessor(Type type, string fieldName)
{
var field = type.GetField(fieldName,
BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
if (field == null) throw new ArgumentException();
var fieldExpression = Expression.Field(
Expression.Convert(ownerParameter, type), field);
Get = Expression.Lambda<Func<object, object>>(
Expression.Convert(fieldExpression, typeof(object)),
ownerParameter).Compile();
Set = Expression.Lambda<Action<object, object>>(
Expression.Assign(fieldExpression,
Expression.Convert(fieldParameter, field.FieldType)),
ownerParameter, fieldParameter).Compile();
}
public Func<object, object> Get { get; }
public Action<object, object> Set { get; }
}
Usage:
class M
{
private string s;
}
var accessors = new Dictionary<string, FieldAccessor>();
// expensive - you should do this only once
accessors.Add("s", new FieldAccessor(typeof(M), "s"));
var m = new M();
accessors["s"].Set(m, "Foo");
var value = accessors["s"].Get(m);

How to alter this class object property text search so class does not need to be hardcoded

The following code is used to search a class object's properties for a text match.
I call it like so:
ClassPropertyTextSearchOrig<UserViewModel>.FullTextSearchInit();
if (FullTextSearch<UserViewModel>.Match((UserViewModel)item, searchValue))
{
matchedItems.Add(item);
}
Class Property Search:
public static class ClassPropTextSearch<T>
{
private static List<Func<T, string>> _properties;
public static void FullTextSearchInit()
{
_properties = GetPropertyFunctions().ToList();
}
public static IEnumerable<Func<T, string>> GetPropertyFunctions()
{
var stringProperties = GetStringPropertyFunctions();
return stringProperties;
}
public static IEnumerable<Func<T, string>> GetStringPropertyFunctions()
{
var propertyInfos = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.GetProperty | BindingFlags.SetProperty)
.Where(p => p.PropertyType == typeof(string)).ToList();
var properties = propertyInfos.Select(GetStringPropertyFunc);
return properties;
}
public static Func<T, string> GetStringPropertyFunc(PropertyInfo propInfo)
{
ParameterExpression x = System.Linq.Expressions.Expression.Parameter(typeof(T), "x");
Expression<Func<T, string>> expression = System.Linq.Expressions.Expression.Lambda<Func<T, string>>(System.Linq.Expressions.Expression.Property(x, propInfo), x);
Func<T, string> propertyAccessor = expression.Compile();
return propertyAccessor;
}
public static bool Match(T item, string searchTerm)
{
bool match = _properties.Select(prop => prop(item)).Any(value => value != null && value.ToLower().Contains(searchTerm.ToLower()));
return match;
}
}
What I'd like to do is make it more dynamic, so that I can just pass the object's Type in and not hard-code the Object T.
Getting rid of the T and passing the Type in, is fine. But if I'm doing it like this, can someone help me with creating an efficient process. This may have tens of thousands of objects to iterate through. I'm a little lost as of how to start. Can I still save time by initializing some of it?
[EDIT]
This piece of code show how I get a list of property names that are bound to columns in a DataGrid. This is done each time there is a search as column order may change.
string binding_path = "";
var columnBoundProperties = new List<KeyValuePair<int, string>>();
//Gets list of column bound properties and their display index
foreach (var col in datagrid.Columns.Where(c => c.Visibility == System.Windows.Visibility.Visible))
{
var binding = (col as DataGridBoundColumn).Binding as Binding;
binding_path = binding.Path.Path;
columnBoundProperties.Add(new KeyValuePair<int, string>(col.DisplayIndex, binding.Path.Path));
}
ClassPropTextSearch.Init(datagrid.Items[0].GetType(), columnBoundProperties)
var itemsSource = datagrid.Items as IEnumerable;
foreach (var item in itemsSource)
{
int column_index_match = ClassPropTextSearch.FirstPropMatch(item, searchValue);
if (column_index_match != null)
{
//Do something
break;
}
//else continue searching items
}
As far as the object search goes I would still like to keep initialization side of things, so here is the mockup of that
public static class ClassPropTextSearch
{
private static Type _itemType;
private static List<KeyValuePair<int, PropertyInfo>> _stringProperties = new List<KeyValuePair<int, PropertyInfo>>();
public static void init(Type itemType, List<KeyValuePair<int, string>> binding_properties)
{
_itemType = itemType;
foreach (var prop in binding_properties)
{
PropertyInfo propertyInfo = _itemType.GetProperty(prop.Value);
if (propertyInfo != null)
{
if (propertyInfo.PropertyType == typeof(string))
{
_stringProperties.Add(new KeyValuePair<int, PropertyInfo>(prop.Key, propertyInfo));
}
}
}
}
public static bool Match(object item, string searchTerm)
{
return PropertiesMatch(item, searchTerm).Any();
}
public static string FirstPropMatch(object item, string searchTerm)
{
//return int index of first property match
}
private static IEnumerable<PropertyInfo> PropertiesMatch(object item, string searchTerm)
{
//return list of matches
}
}
Try the following version, major changes:
No need to pass in generic type explicitly
Store the list of string properties of the target type
EDIT: Support to find the first matching property's name
public static class ClassPropTextSearch
{
private static Dictionary<Type, List<PropertyInfo>> _stringProperties =
new Dictionary<Type, List<PropertyInfo>>();
public static bool Match(object item, string searchTerm)
{
return PropertiesMatch(item, searchTerm).Any();
}
public static string FirstPropMatch(object item, string searchTerm)
{
var prop = PropertiesMatch(item, searchTerm).FirstOrDefault();
return prop != null ? prop.Name : string.Empty;
}
private static IEnumerable<PropertyInfo> PropertiesMatch(object item, string searchTerm)
{
// null checking skipped...
if (!_stringProperties.ContainsKey(item.GetType()))
{
// Retrieve and store the list of string properties of the input's type
var stringProperties = item.GetType()
.GetProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.GetProperty | BindingFlags.SetProperty)
.Where(p => p.PropertyType == typeof(string))
.ToList();
_stringProperties.Add(item.GetType(), stringProperties);
}
return _stringProperties[item.GetType()]
.Where(prop => prop.GetValue(item, null) != null &&
((string)prop.GetValue(item, null)).ToLower().Contains(searchTerm.ToLower()));
}
}
Usage is now simplified to:
if (ClassPropTextSearch.Match(item, searchValue))
{
matchedItems.Add(item);
}
Do it all transparently.
Don't pass in the Type - instead, just pass in the object you want to investigate. You can get the Type by calling GetType().
Then, in your helper class (the one that does the searching) have a singleton Dictionary (or ConcurrentDictionary) which will key off the Type of the class to a class you create. Your class will look something like this (and will be immutable):
class StringProps
{
PropertyInfo[] m_infos;
}
So now you have a list of StringPropertyInfo[] you create the same way you do in your code. (If the StringProps is missing in your Dictionary, you simply create it and add it). That way you have cached versions of all your properties and you can just use them to grab the relevant text strings off your object..
A few notes:
This simplistic approach is cool if you have a finite set of types you interrogate in this fashion. If your app generates types dynamically, this will be an ever-growing memory hog (though, to be fair, since you cannot unload dynamically created types, it almost doesn't matter).
If performance is still an issue after you do this, you may need to resort to emitting code that accesses the property and not via reflection. This can be done by utilizing System.Linq.Expressions and specifically System.Linq.Expressions.LambdaExpression which will allow you to create a delegate that takes an object and casts it to the correct type, calling the correct property (thus not going thru reflection).

Convert anonymous object to expression delegate

I have an third party library that has the following API:
Update<TReport>(object updateOnly, Expression<Func<TReport,bool>> where)
What I want to do is call this method but using anonymous objects such as :
Update(new {Name = "test"}, new {Id = id})
Is it possible to take the second anonymous object and convert it to something like :
x => x.Id == id.
So what I want is to convert the new {Id = id} to a function that takes TReport and returns bool?
Even if I agree with Daniel A. White on the fact that it's complicating things, I tried a bit.
But it's not safe, cause you're losing strong typing. (You can put whatever you want in an anonymous object : it's not linked to the object's "real" properties... So no refactoring, no check...)
It's not really tested, so not sure if that's what you want. You can have (if it works) different objects in the "predicate object" :
new {Name="test"}, new{Id=1, Name="test2"})
So, you could have something like that :
public static class MyHelpers
{
public static Expression<Func<TReport, bool>> CreatePredicate<TReport>(this object predicateObject)
{
var parameterExpression = Expression.Parameter(typeof(TReport), "item");
Expression memberExpression = parameterExpression;
var objectDictionary = MakeDictionary(predicateObject);
foreach (var entry in objectDictionary.Where(entry => typeof(TReport).GetProperty(entry.Key) == null))
{
throw new ArgumentException(string.Format("Type {0} has no property {1}", typeof(TReport).Name, entry.Key));
}
var equalityExpressions = GetBinaryExpressions(objectDictionary, memberExpression).ToList();
var body = equalityExpressions.First();
body = equalityExpressions.Skip(1).Aggregate(body, Expression.And);
return Expression.Lambda<Func<TReport, bool>>(body, new[] { parameterExpression });
}
private static IDictionary<string, object> MakeDictionary(object withProperties)
{
var properties = TypeDescriptor.GetProperties(withProperties);
return properties.Cast<PropertyDescriptor>().ToDictionary(property => property.Name, property => property.GetValue(withProperties));
}
private static IEnumerable<BinaryExpression> GetBinaryExpressions(IDictionary<string, object> dic, Expression expression)
{
return dic.Select(m => Expression.Equal(Expression.Property(expression, m.Key), Expression.Constant(m.Value)));
}
}
usage, for example
public void Update<TReport>(object updateOnly, object predicateObject) {
var predicate = predicateObject.CreatePredicate<TReport>();
yourGenericApi.Update(updateOnly, predicate);
}
EDIT :
As you're losing strong typing security, you should add something like
foreach (var entry in objectDictionary.Where(entry => typeof(TReport).GetProperty(entry.Key) == null))
{
throw new ArgumentException(string.Format("Type {0} has no property {1}", typeof(TReport).Name, entry.Key));
}
after
var objectDictionary = MakeDictionary(predicateObject);
If you have a specific value that you want the function to return, I think you can simply do this:
bool desiredResult = true;
Update(new { Name = "test" }, x => desiredResult);

Listing object properties like the Visual Studio Immediate window

I store a few classes in session. I want to be able to see the values of my class properties in trace viewer. By default I only the Type name MyNamespace.MyClass. I was wondering if I overwrite the .ToString() method and use reflection to loop over all the properties and construct a string like that ... it would do the trick but just wanted to see if there is anything already out there (specially since Immediate Window has this capability) which does the same ... i.e. list the class property values in trace instead of just the Name of the class.
You can try something like that:
static void Dump(object o, TextWriter output)
{
if (o == null)
{
output.WriteLine("null");
return;
}
var properties =
from prop in o.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public)
where prop.CanRead
&& !prop.GetIndexParameters().Any() // exclude indexed properties to keep things simple
select new
{
prop.Name,
Value = prop.GetValue(o, null)
};
output.WriteLine(o.ToString());
foreach (var prop in properties)
{
output.WriteLine(
"\t{0}: {1}",
prop.Name,
(prop.Value ?? "null").ToString());
}
}
Of course, it's not very efficient because of the reflection... A better solution would be to dynamically generate and cache a dumper method for each specific type.
EDIT: here's an improved solution, that uses Linq expressions to generate a specialized dumper method for each type. Slightly more complex ;)
static class Dumper
{
private readonly static Dictionary<Type, Action<object, TextWriter>> _dumpActions
= new Dictionary<Type, Action<object, TextWriter>>();
private static Action<object, TextWriter> CreateDumper(Type type)
{
MethodInfo writeLine1Obj = typeof(TextWriter).GetMethod("WriteLine", new[] { typeof(object) });
MethodInfo writeLine1String2Obj = typeof(TextWriter).GetMethod("WriteLine", new[] { typeof(string), typeof(object), typeof(object) });
ParameterExpression objParam = Expression.Parameter(typeof(object), "o");
ParameterExpression outputParam = Expression.Parameter(typeof(TextWriter), "output");
ParameterExpression objVariable = Expression.Variable(type, "o2");
LabelTarget returnTarget = Expression.Label();
List<Expression> bodyExpressions = new List<Expression>();
bodyExpressions.Add(
// o2 = (<type>)o
Expression.Assign(objVariable, Expression.Convert(objParam, type)));
bodyExpressions.Add(
// output.WriteLine(o)
Expression.Call(outputParam, writeLine1Obj, objParam));
var properties =
from prop in type.GetProperties(BindingFlags.Instance | BindingFlags.Public)
where prop.CanRead
&& !prop.GetIndexParameters().Any() // exclude indexed properties to keep things simple
select prop;
foreach (var prop in properties)
{
bool isNullable =
!prop.PropertyType.IsValueType ||
prop.PropertyType.IsGenericType && prop.PropertyType.GetGenericTypeDefinition() == typeof(Nullable<>);
// (object)o2.<property> (cast to object to be passed to WriteLine)
Expression propValue =
Expression.Convert(
Expression.Property(objVariable, prop),
typeof(object));
if (isNullable)
{
// (<propertyValue> ?? "null")
propValue =
Expression.Coalesce(
propValue,
Expression.Constant("null", typeof(object)));
}
bodyExpressions.Add(
// output.WriteLine("\t{0}: {1}", "<propertyName>", <propertyValue>)
Expression.Call(
outputParam,
writeLine1String2Obj,
Expression.Constant("\t{0}: {1}", typeof(string)),
Expression.Constant(prop.Name, typeof(string)),
propValue));
}
bodyExpressions.Add(Expression.Label(returnTarget));
Expression<Action<object, TextWriter>> dumperExpr =
Expression.Lambda<Action<object, TextWriter>>(
Expression.Block(new[] { objVariable }, bodyExpressions),
objParam,
outputParam);
return dumperExpr.Compile();
}
public static void Dump(object o, TextWriter output)
{
if (o == null)
{
output.WriteLine("null");
}
Type type = o.GetType();
Action<object, TextWriter> dumpAction;
if (!_dumpActions.TryGetValue(type, out dumpAction))
{
dumpAction = CreateDumper(type);
_dumpActions[type] = dumpAction;
}
dumpAction(o, output);
}
}
Usage:
Dumper.Dump(myObject, Console.Out);
This is the code I use. I find it incredibly useful and is almost instant. The code is using the Newtonsoft JSON converter.
[System.Obsolete("ObjectDump should not be included in production code.")]
public static void Dump(this object value)
{
try
{
System.Diagnostics.Trace.WriteLine(JsonConvert.SerializeObject(value, Formatting.Indented));
}
catch (Exception exception)
{
System.Diagnostics.Trace.WriteLine("Object could not be formatted. Does it include any interfaces? Exception Message: " + exception.Message);
}
}
Add this to a common library, reference it and add it to the using clause. Can be used in the immediate window by typing YourObject.Dump() (the exact same as you'd do in linqpad).
Classes including interfaces have to be handled differently as this is simply a JSON converter. A workaround for classes that include interface implementations that I use is to remove the default empty constructor and implement a constructor using the specific instances of the interfaces.
I find JSON a very easy format to read and consider this small method invaluable for debugging.

How can I get the name of a C# static class property using reflection?

I want to make a C# Dictionary in which the key is the string name of a static property in a class and the value is the value of the property. Given a static property in the class called MyResources.TOKEN_ONE, how can I get the at the name of the property rather than its value? I only care about the end part of the property name (e.g. "TOKEN_ONE").
I've edited this to mention that I don't want to map all property values in the Dictionary, just a small subset of everything that's in the class. So assume that I want to get the name for a single property. Given MyResources.TOKEN_ONE, I want to get back "MyResources.TOKEN_ONE" or just "TOKEN_ONE".
Here's some sample code that shows what I'm trying to do. I need the property name because I'm trying to generate a javascript variable in which I map the property name to the variable name and the property value to the variable value. For example, I want the C# Dictionary to generate lines like the one below:
var TOKEN_ONE = "One";
using System;
using System.Collections.Generic;
namespace MyConsoleApp
{
class Program
{
static void Main(string[] args)
{
Dictionary<String, String> kvp = new Dictionary<String, String>();
// How can I use the name of static property in a class as the key for the dictionary?
// For example, I'd like to do something like the following where 'PropertyNameFromReflection'
// is a mechanism that would return "MyResources.TOKEN_ONE"
kvp.Add(MyResources.TOKEN_ONE.PropertyNameFromReflection, MyResources.TOKEN_ONE);
kvp.Add(MyResources.TOKEN_TWO.PropertyNameFromReflection, MyResources.TOKEN_TWO);
Console.ReadLine();
}
}
public static class MyResources
{
public static string TOKEN_ONE
{
get { return "One"; }
}
public static string TOKEN_TWO
{
get { return "Two"; }
}
}
}
If all you want is just to be able to refer to a single, specific property in one place in the code without having to refer to it by a literal string, then you can use an expression tree. For example, the following code declares a method that turns such an expression tree into a PropertyInfo object:
public static PropertyInfo GetProperty(Expression<Func<string>> expr)
{
var member = expr.Body as MemberExpression;
if (member == null)
throw new InvalidOperationException("Expression is not a member access expression.");
var property = member.Member as PropertyInfo;
if (property == null)
throw new InvalidOperationException("Member in expression is not a property.");
return property;
}
Now you can do something like this:
public void AddJavaScriptToken(Expression<Func<string>> propertyExpression)
{
var p = GetProperty(propertyExpression);
_javaScriptTokens.Add(p.Name, (string) p.GetValue(null, null));
}
public void RegisterJavaScriptTokens()
{
AddJavaScriptToken(() => Tokens.TOKEN_ONE);
AddJavaScriptToken(() => Tokens.TOKEN_TWO);
}
Here is a function that will get you the names of all static properties in a given type.
public static IEnumerable<string> GetStaticPropertyNames(Type t) {
foreach ( var prop in t.GetProperties(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic) ) {
yield return prop.Name;
}
}
If you want to build up the map of all property names to their values you can do the following
public static Dictionary<string,object> GetStaticPropertyBag(Type t) {
var flags = BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
var map = new Dictionary<string,object>();
foreach ( var prop in t.GetProperties(flags) ) {
map[prop.Name] = prop.GetValue(null,null);
}
return map;
}
Now you can call it with the following
var bag = GetStaticPropertyBag(typeof(MyResources));
You can accomplish this with reflection. The easiest way to get the property names is to loop over all of them.
foreach(var propInfo in this.GetType().GetProperties()) {
var name = propInfo.Name;
var value = propInfo.GetValue(this, null);
}
See GetProperties() and GetValue() for more specifics.
Well, I'm reluctantly answering my own question because I don't think it's possible to do this for a single static property. Ultimately, I ended up hard-coding the key in the Dictionary using a cut-and-paste of the property name. Here's what I ended up with:
public void RegisterJavaScriptTokens()
{
AddJavaScriptToken(Token.FOOBAR_TITLE, "FOOBAR_TITLE");
}
And here's the rest of the code:
protected Dictionary<String, String> _javaScriptTokens = new Dictionary<String, String>();
public void AddJavaScriptToken(string tokenValue, string propertyName)
{
_javaScriptTokens.Add(propertyName, tokenValue);
}
protected override void OnPreRender(EventArgs e)
{
if (_javaScriptTokens.Count > 0)
{
StringBuilder sb = new StringBuilder();
foreach (KeyValuePair<String, String> kvp in _javaScriptTokens)
{
sb.AppendLine(String.Format("var TOKEN_{0} = unescape('{1}');", kvp.Key, PUtilities.Escape(kvp.Value)));
}
ClientScript.RegisterStartupScript(this.GetType(), "PAGE_TOKENS", sb.ToString(), true);
}
base.OnPreRender(e);
}
I hate having to use cut-and-paste hard-coding to keep the property name and the key in sync...Oh well...
Other answers have already explained how you can get a list of the static properties via Reflection. You said you don’t want all of them, only a subset of them. It seems, therefore, that you need a way to distinguish the properties you want from the ones you don’t want. One way to do this is using custom attributes.
Declare a custom attribute class:
[AttributeUsage(AttributeTargets.Property)]
public class WantThisAttribute : Attribute { }
Add this custom attribute to the properties you want:
public static class MyResources
{
[WantThis]
public static string TOKEN_ONE { get { return "One"; } }
[WantThis]
public static string TOKEN_TWO { get { return "Two"; } }
public static string DontWantThis { get { return "Nope"; } }
}
Iterate over the properties to find the ones you want:
public static Dictionary<string, object> GetStaticPropertyBag(Type t)
{
var flags = BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic;
var map = new Dictionary<string, object>();
foreach (var prop in t.GetProperties(flags))
if (prop.IsDefined(typeof(WantThisAttribute), true))
map[prop.Name] = prop.GetValue(null,null);
return map;
}

Categories

Resources