Reputation: 100
I have a custom editable ConfigurationElement and generally I'm using it as a settings object for some of my classes. (by passing it through the constructor)
When I have an object from an external dll with its own settings properties (so I can't change it to directly reading from my config element) I'm using this extension to copy the config properties values to the object properties:
/// <summary>
/// Set the object properties from a configuration element including the unrecognized attributes.
/// </summary>
/// <typeparam name="T">The object type</typeparam>
/// <param name="obj">The object to set</param>
/// <param name="configElement">The configuration element to take the properties from</param>
/// <returns></returns>
public static T SetProperties<T>(this T obj, BaseConfigurationElement configElement) => obj.SetProperties(configElement.GetProperties(true));
public static T SetProperties<T>(this T obj, object properties) => SetProperties(obj, properties?.GetType().GetProperties().ToDictionary(p => p.Name, p => p.GetValue(properties)));
public static T SetProperties<T>(this T obj, Dictionary<string, string> properties) => SetProperties(obj, properties.ToDictionary(i => i.Key, i => i.Value as object));
public static T SetProperties<T>(this T obj, Dictionary<string, object> properties)
{
if (obj != null && properties != null)
foreach (PropertyInfo pi in obj.GetType().GetProperties())
if (properties.Keys.Contains(pi.Name) && pi.CanWrite)
try // Convert value to property type.
{
object valueToSet = properties[pi.Name];
if (pi.PropertyType.IsEnum)
pi.SetValue(obj, Enum.Parse(pi.PropertyType, valueToSet.ToString()));
else pi.SetValue(obj, Convert.ChangeType(valueToSet, pi.PropertyType), null);
}
catch (Exception ex) { Logging.WriteError($"Can't convert from type [{GetTypeName(properties[pi.Name])}] to type [{pi.PropertyType.Name}] for property [{pi.Name}] of object type [{GetTypeName(obj)}]: {ex.Message}"); }
return obj;
}
The point is that I want to make it possible to change the configuration on real time but I have no event on the ConfigurationElement that will be arise when a value was changed so I can re-copy the changed properties.
Is there a way to create an event for that on my custom ConfigurationElement?
P.s. I don't want to use the INotifyPropertyChanged interface because it would be very cumbersome to add a call in each property. I'm asking cuz the ConfigurationElement has its indexer so maybe there is a way that I don't know on this base class.
Upvotes: 0
Views: 146
Reputation: 100
I have found that the SetPropertyValue protected method was not marked as virtual, if it was virtual it could been easier.
My solution is to hide the indexers using the "new" keyword, it's not like overriding but it's ok since the indexers are not public so when inheriting the custom class you usually will not cast your self to the base type.
That should work:
public class PropertyChangedEventArgs : EventArgs
{
public ConfigurationProperty Property { get; }
public object Value { get; }
public PropertyChangedEventArgs(ConfigurationProperty property, object value) { Property = property; Value = value; }
}
public abstract class BaseConfigurationElement : ConfigurationElement
{
protected new Object this[ConfigurationProperty prop]
{
get => base[prop];
set
{
base[prop] = value;
OnPropertyChanged(new PropertyChangedEventArgs(prop, value)); // Must be arise only after the base indexer was set, because an exception may will be thrown from the indexer.
}
}
protected new Object this[String propertyName]
{
get => base[propertyName];
set
{
base[propertyName] = value;
OnPropertyChanged(new PropertyChangedEventArgs(Properties[propertyName], value));
}
}
public event EventHandler<PropertyChangedEventArgs> PropertyChanged;
protected virtual void OnPropertyChanged(PropertyChangedEventArgs e) => PropertyChanged?.Invoke(this, e);
}
Upvotes: 0