I have a PropertyGrid that I used to display the properties in a helper class. I assign the helper class to the PropertyGrid like this:
myPropertyGrid.SelectedObject = mySettingsHelper;
In the helper class I assign the ReadOnlyAttribute at design time like this:
[DisplayName("DisplayExA"),
Description("DescriptionExA"),
ReadOnlyAttribute(true)]
public string PropertyA { get; set; }
[DisplayName("DisplayExB"),
Description("DescriptionExB"),
ReadOnlyAttribute(false)]
public string PropertyB { get; set; }
[DisplayName("DisplayExC"),
Description("DescriptionExC"),
ReadOnlyAttribute(true)]
public string PropertyC { get; set; }
But now I need to be able to change this attribute on individual properties dynamically during runtime. Based on certain criteria some of these properties may need to be read-only or not read-only. How would I make the change dynamically at runtime?
EDIT:
I tried the following code but this sets the ReadOnly attribute for every instance of the object! I want to do it per object. Sometimes one object might have PropertyA read-only while a second object has PropertyA to not be read-only.
public static class PropertyReadOnlyHelper
{
public static void SetReadOnly(object container, string name, bool value)
{
try
{
PropertyDescriptor descriptor = TypeDescriptor.GetProperties(container.GetType())[name];
ReadOnlyAttribute attribute = (ReadOnlyAttribute)descriptor.Attributes[typeof(ReadOnlyAttribute)];
FieldInfo fieldToChange = attribute.GetType().GetField("isReadOnly",
System.Reflection.BindingFlags.NonPublic |
System.Reflection.BindingFlags.Instance);
fieldToChange.SetValue(attribute, value);
}
catch { }
}
}
I was able to do exactly what I need (object level assignment of the read-only attribute) using the library from this CodeProject article. What is nice is that it enables me to still use the .NET PropertyGrid and just use the custom attributes to handle the dynamic settings.
Use reflection to get the instance reference of the ReadOnlyAttribute class, then toggle the IsReadOnly property on that instance. Finally, re-select the item in the PropertyGrid if needed by settings its SelectedObjects to null and then resetting it. You might be able to do this using the PropertyGrid RefreshTabs method too, I'm not sure.
EDIT:
Unfortunately the IsReadOnly property itself is read only... in this case we'd have to use reflection to change the value of the backing field for the IsReadOnly property.
Add Readonly
TextBoxID.Attributes.Add("readonly","true");
Remove readonly
TextBoxID.Attributes.Remove("readonly");
Dynamically setting browsable or readonly attribute of a property in a PropertyGrid is often needed together and also they are similiar jobs
After a few touches, the great answer of Reza Aghaei about "Hide some properties in PropertyGrid at run-time" is also applicable for manipulating the readonly attribute.
public class CustomObjectWrapper : CustomTypeDescriptor
{
public object WrappedObject { get; private set; }
public List<string> BrowsableProperties { get; private set; }
public List<string> ReadonlyProperties { get; private set; }
public CustomObjectWrapper(object o)
: base(TypeDescriptor.GetProvider(o).GetTypeDescriptor(o))
{
WrappedObject = o;
BrowsableProperties = new List<string>() { "Text", "BackColor" };
ReadonlyProperties = new List<string>() { "Font" };
}
public override PropertyDescriptorCollection GetProperties()
{
return this.GetProperties(new Attribute[] { });
}
public override PropertyDescriptorCollection GetProperties(Attribute[] attributes)
{
List<PropertyDescriptor> result = new List<PropertyDescriptor>();
IEnumerable<PropertyDescriptor> properties = base.GetProperties(attributes).Cast<PropertyDescriptor>()
.Where(p => BrowsableProperties.Contains(p.Name));//unbrowsable filtering
foreach (var p in properties)
{
PropertyDescriptor resultPropertyDescriptor = null;
//handle being readonly
if (ReadonlyProperties.Contains(p.Name))
{
List<Attribute> atts = p.Attributes.Cast<Attribute>().ToList();
atts.RemoveAll(a => a.GetType().Equals(typeof(ReadOnlyAttribute)));//remove any readonly attribute
atts.Add(new ReadOnlyAttribute(true));//add "readonly=true" attribute
resultPropertyDescriptor = TypeDescriptor.CreateProperty(WrappedObject.GetType(), p, atts.ToArray());
}
else
{
resultPropertyDescriptor = TypeDescriptor.CreateProperty(WrappedObject.GetType(), p, p.Attributes.Cast<Attribute>().ToArray());
}
if (resultPropertyDescriptor != null)
result.Add(resultPropertyDescriptor);
}
return new PropertyDescriptorCollection(result.ToArray());
}
}
and the usage:
propertyGrid1.SelectedObject = new CustomObjectWrapper(myobject);
Please try the code below.
[CategoryAttribute("2. LINE"), DisplayNameAttribute("Spline Line Tension"),
DescriptionAttribute("Chart's Spline Line Tension "), ReadOnlyAttribute(false)]
public float _PG_SplineTension
{
get
{
bool lbReadyOnly = true;
SetPropertyReadOnly("_PG_SplineTension", lbReadyOnly);
return this.cfSplineTension;
}
set { this.cfSplineTension = value; }
}
private void SetPropertyReadOnly(string lsProperty, bool lbIsReadOnly)
{
PropertyDescriptor descriptor = TypeDescriptor.GetProperties(this.GetType())[lsProperty];
ReadOnlyAttribute attribute = (ReadOnlyAttribute)
descriptor.Attributes[typeof(ReadOnlyAttribute)];
FieldInfo fieldToChange = attribute.GetType().GetField("isReadOnly",
System.Reflection.BindingFlags.NonPublic |
System.Reflection.BindingFlags.Instance);
fieldToChange.SetValue(attribute, lbIsReadOnly);
}
Thanks a lot. Based on the answers I came with the following code that works just fine:
private void SetReadonly ( object o, bool value )
{
foreach ( PropertyInfo property in o.GetType().GetProperties() )
if ( property.GetCustomAttribute<ReadOnlyAttribute>() != null )
{
Attribute readOnly = TypeDescriptor.GetProperties( o.GetType() )[property.Name].Attributes[typeof( ReadOnlyAttribute )];
readOnly.GetType().GetField( nameof( ReadOnlyAttribute.IsReadOnly ), BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.IgnoreCase ).SetValue( readOnly, value );
}
}
Check this page out:
https://www.codeproject.com/Articles/152945/Enabling-disabling-properties-at-runtime-in-the-Pr
Quoted from above post:
it is important to statically define the ReadOnly attribute of every property of the class to whatever value you want. If not, changing the attribute at runtime that way will wrongly modify the attributes of every property of the class.
Use reflection to modify the "ReadOnly" attribute of the target property during runtime to achieve your goal. The problem that setting one property applies to all is because you need to set all properties in the same PropertyGrid object with ReadOnly attribute explicitly to avoid that problem.
[RefreshProperties(System.ComponentModel.RefreshProperties.All)]
[ReadOnly(false)]
public string Country
{
get { return mCountry; }
set
{
mCountry = value;
PropertyDescriptor descriptor = TypeDescriptor.GetProperties(this.GetType())["State"];
ReadOnlyAttribute attribute = (ReadOnlyAttribute)
descriptor.Attributes[typeof(ReadOnlyAttribute)];
FieldInfo fieldToChange = attribute.GetType().GetField("isReadOnly",
System.Reflection.BindingFlags.NonPublic |
System.Reflection.BindingFlags.Instance);
fieldToChange.SetValue(attribute, mCountry != "U.S.");
}
}
[ReadOnly(true)]
public string State
{
get { return mState; }
set { mState = value; }
}
Related
I have a property grid that i wanna change readonly attribute of some of its items at run-time.
Changing readonly for simple items is easy and i have no problem there, my problem is i cannot change readonly for hierarchical items like latitude and longitude in picture below. I even tried to make the whole "Position" category readonly but it didn't seem to help.
Side question: Is there any way to put some items in a group (like this position item) without wrapping them in a class?
Yes! This could be helpful!
public class Member
{
string name;
bool isMarried;
string spouseName;
public string Name
{
get { return name; }
set { name = value; }
}
[System.ComponentModel.RefreshProperties(RefreshProperties.All)]
public bool IsMarried
{
get { return isMarried; }
set
{
isMarried = value;
bool newValue = !value;
PropertyDescriptor descriptor = TypeDescriptor.GetProperties(this.GetType())["SpouseName"];
ReadOnlyAttribute attrib = (ReadOnlyAttribute)descriptor.Attributes[typeof(ReadOnlyAttribute)];
FieldInfo isReadOnly = attrib.GetType().GetField("isReadOnly", BindingFlags.NonPublic | BindingFlags.Instance);
isReadOnly.SetValue(attrib, newValue);
}
}
[ReadOnly(true)]
public string SpouseName
{
get { return spouseName; }
set
{
spouseName = value;
}
}
}
The problem is simple(and I hope that this have a simple solution!): I want to hide ( Browsable(false) ) the property "Element" (in my PropertyGrid object) when it's zero.
public class Question
{
...
public int Element
{
get; set;
}
}
The easiest way to hide a property in PropertGrid and in a Custom Control for me is this:
public class Question
{
...
[Browsable(false)]
public int Element
{
get; set;
}
}
To do it dynamically you can use this code, where Question is your class and your property is Element, so you can show or hide it without remove element from collection:
PropertyDescriptorCollection propCollection = TypeDescriptor.GetProperties(Question.GetType());
PropertyDescriptor descriptor = propCollection["Element"];
BrowsableAttribute attrib = (BrowsableAttribute)descriptor.Attributes[typeof(BrowsableAttribute)];
FieldInfo isBrow = attrib.GetType().GetField("browsable", BindingFlags.NonPublic | BindingFlags.Instance);
//Condition to Show or Hide set here:
isBrow.SetValue(attrib, true);
propertyGrid1.Refresh(); //Remember to refresh PropertyGrid to reflect your changes
So to refine the answer:
public class Question
{
...
private int element;
[Browsable(false)]
public int Element
{
get { return element; }
set {
element = value;
PropertyDescriptorCollection propCollection = TypeDescriptor.GetProperties(Question.GetType());
PropertyDescriptor descriptor = propCollection["Element"];
BrowsableAttribute attrib = (BrowsableAttribute)descriptor.Attributes[typeof(BrowsableAttribute)];
FieldInfo isBrow = attrib.GetType().GetField("browsable", BindingFlags.NonPublic | BindingFlags.Instance);
if(element==0)
{
isBrow.SetValue(attrib, false);
}
else
{
isBrow.SetValue(attrib, true);
}
}
}
}
What you could do is reuse the DynamicTypeDescriptor class described in my answer to this question here on SO: PropertyGrid Browsable not found for entity framework created property, how to find it?
like this for example:
public Form1()
{
InitializeComponent();
DynamicTypeDescriptor dt = new DynamicTypeDescriptor(typeof(Question));
Question q = new Question(); // initialize question the way you want
if (q.Element == 0)
{
dt.RemoveProperty("Element");
}
propertyGrid1.SelectedObject = dt.FromComponent(q);
}
Try BrowsableAttributes/BrowsableProperties and HiddenAttributes/HiddenProperties:
More info here
When I wanted solve this problem many years ago, as I remember, attribute [Browsable] not works. I see that it works now great, but I also I made solution by way of creating proxy objects.
There is code:
https://github.com/NightmareZ/PropertyProxy
You can highlight required properties with attribute and then create proxy object which will forward only highlighted properties to PropertyGrid control.
public class Question
{
...
[PropertyProxy]
public int Element
{
get; set;
}
}
...
var factory = new PropertyProxyFactory();
var question = new Question();
var proxy = factory.CreateProxy(question);
propertyGrid.SelectedObject = proxy;
I have the following code:
[Serializable]
public class CustomClass
{
public CustomClass()
{
this.Init();
}
public void Init()
{
foreach (PropertyInfo p in this.GetType().GetProperties())
{
DescriptionAttribute da = null;
DefaultValueAttribute dv = null;
foreach (Attribute attr in p.GetCustomAttributes(true))
{
if (attr is DescriptionAttribute)
{
da = (DescriptionAttribute) attr;
}
if (attr is DefaultValueAttribute)
{
dv = (DefaultValueAttribute) attr;
}
}
UInt32 value = 0;
if (da != null && !String.IsNullOrEmpty(da.Description))
{
value = Factory.Instance.SelectByCode(da.Description, 3);
}
if (dv != null && value == 0)
{
value = (UInt32) dv.Value;
}
p.SetValue(this, value, null);
}
}
private UInt32 name;
[Description("name")]
[DefaultValue(41)]
public UInt32 Name
{
get { return this.name; }
set { this.name = value; }
}
(30 more properties)
}
Now the weird thing is: when I try to serialize this class I will get an empty node CustomClass!
<CustomClass />
And when I remove Init from the constructor it works as expected! I will get the full xml representation of the class but ofcourse without values (all with value 0).
<CustomClass>
<Name>0</Name>
...
</CustomClass>
Also, when I comment out the body of Init, I will get the same as above (the one with default values)
I've tried it with a public method, with a Helper class everything, but it does not work. That is, instead of the expected:
<CustomClass>
<Name>15</Name>
...
</CustomClass>
I will get
<CustomClass />
It seems when I use reflection in this class, serialization is not possible.
Or to summarize: when I call Init or when I fill my properties with reflection -> Serialization fails, when I remove this code part -> Serialization works but of course without my values.
Is this true? And does somebody know an alternative for my solution?
It should automatically get something from the database based on the Description and when this returns nothing it falls back to the DefaultValue...
PS1: I am using the XmlSerializer
PS2: When I set a breakpoint before the serialization, I can see that all the properties are filled with the good values (like 71, 72 etc).
Now the weird thing is: when I try to serialize this class I will get an empty node CustomClass!
XmlSerializer uses DefaultValue to decide which values to serialize - if it matches the default value, it doesn't store it. This approach is consistent with similar models such as data-binding / model-binding.
Frankly, I would say that in this case both DefaultValueAttribute and DescriptionAttribute are poor choices. Write your own - perhaps EavInitAttribute - then use something like:
[EavInit(41, "name")]
public uint Name {get;set;}
Note that there are other ways of controlling this conditional serialization - you could write a method like:
public bool ShouldSerializeName() { return true; }
which will also work to convince it to write the value (this is another pattern recognised by various serialization and data-binding APIs) - but frankly this is even more work (it is per-property, and needs to be public, so it makes a mess of the API).
Finally, I would say that hitting the database multiple times (once per property) for every new object construction is very expensive - especially since many of those values are likely to be assigned values in a moment anyway (so looking them up is wasted effort). I would put a lot of thought into making this both "lazy" and "cached" if it was me.
An example of a lazy and "sparse" implementation:
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Xml.Serialization;
static class Program
{
static void Main()
{
var obj = new CustomClass();
Console.WriteLine(obj.Name);
// show it working via XmlSerializer
new XmlSerializer(obj.GetType()).Serialize(Console.Out, obj);
}
}
public class CustomClass : EavBase
{
[EavInit(42, "name")]
public uint Name
{
get { return GetEav(); }
set { SetEav(value); }
}
}
public abstract class EavBase
{
private Dictionary<string, uint> values;
protected uint GetEav([CallerMemberName] string propertyName = null)
{
if (values == null) values = new Dictionary<string, uint>();
uint value;
if (!values.TryGetValue(propertyName, out value))
{
value = 0;
var prop = GetType().GetProperty(propertyName);
if (prop != null)
{
var attrib = (EavInitAttribute)Attribute.GetCustomAttribute(
prop, typeof(EavInitAttribute));
if (attrib != null)
{
value = attrib.DefaultValue;
if (!string.IsNullOrEmpty(attrib.Key))
{
value = LookupDefaultValueFromDatabase(attrib.Key);
}
}
}
values.Add(propertyName, value);
}
return value;
}
protected void SetEav(uint value, [CallerMemberName] string propertyName = null)
{
(values ?? (values = new Dictionary<string, uint>()))[propertyName] = value;
}
private static uint LookupDefaultValueFromDatabase(string key)
{
// TODO: real code here
switch (key)
{
case "name":
return 7;
default:
return 234;
}
}
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
protected class EavInitAttribute : Attribute
{
public uint DefaultValue { get; private set; }
public string Key { get; private set; }
public EavInitAttribute(uint defaultValue) : this(defaultValue, "") { }
public EavInitAttribute(string key) : this(0, key) { }
public EavInitAttribute(uint defaultValue, string key)
{
DefaultValue = defaultValue;
Key = key ?? "";
}
}
}
I have created a custom Property Selector to accept an array in the constructor to say which properties should be included in the search. The approach works well as long as there are no component types, but how do I deal with those? Here is an example:
public class Customer
{
public virtual int Id { get; private set; }
public virtual Name Name { get; set; }
public virtual bool isPreferred { get; set; }
//...etc
}
public class Name
{
public string Title { get; set; }
public string Firstname { get; set; }
public string Lastname { get; set; }
public string Fullname { get; }
}
public class CustomerPropertySelector : Example.IPropertySelector
{
private string[] _propertiesToInclude = { };
public CustomerPropertySelector(string[] propertiesToInclude)
{
this._propertiesToInclude = propertiesToInclude;
}
public bool Include(object propertyValue, String propertyName, NHibernate.Type.IType type)
{
//...Checking for null and zeros etc, excluded for brevity
if (!_propertiesToInclude.Contains(propertyName))
return false;
return true;
}
}
I would like to be able to search by first name, but not necessarily last. The property name is Name however, so both first and last names seem to be part of the same property, and something like Name.Firstname, which would normally work as a criterion, doesn't seem to work here. What would be the best way around that?
EXAMPLE:
Customer exampleCust = new Customer(FirstName: "Owen");
IList<Customer> matchedCustomers = _custRepo.GetByExample(exampleCust, new string[] { "Name.FirstName" });
Given that there are 2 customers in db, only one named "Owen", but both have isPreferred = false, I would like my query to only return the first one. Standard QBE will return both based on isPreferred property.
SOLUTION:
Thank you for the answers, the solution is mostly based on answer by therealmitchconnors, however I couldn't have done it without Mark Perry's answer either.
The trick was to realise that rather than including Name.FirstName property, I actually want to exclude Name.LastName, since QBE only allows us to exclude properties. I used a method adapted from therealmitchconnors's answer to help me determine fully qualified names of properties. Here is the working code:
public IList<T> GetByExample(T exampleInstance, params string[] propertiesToInclude)
{
ICriteria criteria = _session.CreateCriteria(typeof(T));
Example example = Example.Create(exampleInstance);
var props = typeof(T).GetProperties();
foreach (var prop in props)
{
var childProperties = GetChildProperties(prop);
foreach (var c in childProperties)
{
if (!propertiesToInclude.Contains(c))
example.ExcludeProperty(c);
}
}
criteria.Add(example);
return criteria.List<T>();
}
private IEnumerable<string> GetChildProperties(System.Reflection.PropertyInfo property)
{
var builtInTypes = new List<Type> { typeof(bool), typeof(byte), typeof(sbyte), typeof(char),
typeof(decimal), typeof(double), typeof(float), typeof(int), typeof(uint), typeof(long),
typeof(ulong), typeof(object), typeof(short), typeof(ushort), typeof(string), typeof(DateTime) };
List<string> propertyNames = new List<string>();
if (!builtInTypes.Contains(property.PropertyType) && !property.PropertyType.IsGenericType)
{
foreach (var subprop in property.PropertyType.GetProperties())
{
var childNames = GetChildProperties(subprop);
propertyNames = propertyNames.Union(childNames.Select(r => property.Name + "." + r)).ToList();
}
}
else
propertyNames.Add(property.Name);
return propertyNames;
}
I wasn't sure of the best way to determine whether a property was a component class or not, any suggestions on how to improve the code are very welcome.
The following code would replace the logic you are using to populate propertiesToInclude. I changed it from an array to a list so I could use the Add method because I am lazy, but I think you get the picture. This does only work for one sub-level of properties. For n levels you would need to recurse.
List<string> _propertiesToInclude = new List<string>();
Type t;
var props = t.GetProperties();
foreach (var prop in props)
{
if (prop.PropertyType.IsClass)
foreach (var subprop in prop.PropertyType.GetProperties())
_propertiesToInclude.Add(string.Format("{0}.{1}", prop.Name, subprop.Name));
else
_propertiesToInclude.Add(prop.Name);
}
I thought I had something but reading your question again you want to know why the QBE NHibernate code doesn't work with component properties.
I think you need to create a sub-criteria query for the Name part.
Perhaps something like this:
public IList<Customer> GetByExample(Customer customer, string[] propertiesToExclude){
Example customerQuery = Example.Create(customer);
Criteria nameCriteria = customerQuery.CreateCriteria<Name>();
nameCriteria.Add(Example.create(customer.Name));
propertiesToExclude.ForEach(x=> customerQuery.ExcludeProperty(x));
propertiesToExclude.ForEach(x=> nameCriteria.ExcludeProperty(x));
return customerQuery.list();
}
This is an example from the NHibernate Test Project, it shows how to exclude Component properties.
[Test]
public void TestExcludingQBE()
{
using (ISession s = OpenSession())
using (ITransaction t = s.BeginTransaction())
{
Componentizable master = GetMaster("hibernate", null, "ope%");
ICriteria crit = s.CreateCriteria(typeof(Componentizable));
Example ex = Example.Create(master).EnableLike()
.ExcludeProperty("Component.SubComponent");
crit.Add(ex);
IList result = crit.List();
Assert.IsNotNull(result);
Assert.AreEqual(3, result.Count);
master = GetMaster("hibernate", "ORM tool", "fake stuff");
crit = s.CreateCriteria(typeof(Componentizable));
ex = Example.Create(master).EnableLike()
.ExcludeProperty("Component.SubComponent.SubName1");
crit.Add(ex);
result = crit.List();
Assert.IsNotNull(result);
Assert.AreEqual(1, result.Count);
t.Commit();
}
}
Source code link
Is it possible to modify the attribute of a property at runtime?
let's say I have some class:
public class TheClass
{
[TheAttribute]
public int TheProperty { get; set; }
}
Is there a way to do this?
if (someCondition)
{
// disable attribute. Is this possible and how can this be done?
}
No this is not possible. You cannot modify attribute values from metadata, or metadata in general, at runtime
Strictly speaking the above is not true. There are certain APIs which do allow allow for some metadata generation and modification. But they are very scenario specific, (ENC, profiling, debugging) and should not be used in general purpose programs.
It depends; from a reflection perspective: no. You can't. But if you are talking about attributes used by System.ComponentModel in things like data-binding, they you can use TypeDescriptor.AddAttributes to append extra attributes. Or other customer models involving custom descriptors. So it depends on the use-case.
In the case of xml serialization, it gets more interesting. Firstly, we can use fun object models:
using System;
using System.Xml.Serialization;
public class MyData
{
[XmlAttribute]
public int Id { get; set; }
[XmlAttribute]
public string Name { get; set; }
[XmlIgnore]
public bool NameSpecified { get; set; }
static void Main()
{
var ser = new XmlSerializer(typeof(MyData));
var obj1 = new MyData { Id = 1, Name = "Fred", NameSpecified = true };
ser.Serialize(Console.Out, obj1);
Console.WriteLine();
Console.WriteLine();
var obj2 = new MyData { Id = 2, Name = "Fred", NameSpecified = false };
ser.Serialize(Console.Out, obj2);
}
}
The bool {name}Specified {get;set;} pattern (along with bool ShouldSerialize{name}()) is recognised and used to control which elements to include.
Another alternative is to use the non-default ctor:
using System;
using System.Xml.Serialization;
public class MyData
{
[XmlAttribute]
public int Id { get; set; }
public string Name { get; set; }
static void Main()
{
var obj = new MyData { Id = 1, Name = "Fred" };
XmlAttributeOverrides config1 = new XmlAttributeOverrides();
config1.Add(typeof(MyData),"Name",
new XmlAttributes { XmlIgnore = true});
var ser1 = new XmlSerializer(typeof(MyData),config1);
ser1.Serialize(Console.Out, obj);
Console.WriteLine();
Console.WriteLine();
XmlAttributeOverrides config2 = new XmlAttributeOverrides();
config2.Add(typeof(MyData), "Name",
new XmlAttributes { XmlIgnore = false });
var ser2 = new XmlSerializer(typeof(MyData), config2);
ser2.Serialize(Console.Out, obj);
}
}
Note though that if you use this second approach you need to cache the serializer instance, as it emits an assembly every time you do this. I find the first approach simpler...
Attributes are baked into code at compilation time. The only way you can define new attributes at run time is to generate new code at runtime (using Reflection.Emit, for example). But you cannot change the attributes of existing code.
You can put Boolean variable in the class to disable/enable the property instead of disabling it at run time.
You might want to look at this http://social.msdn.microsoft.com/Forums/en-US/csharpgeneral/thread/5b0d356d-d006-43ff-bfcd-aa90dd8de6db
And Dave Morton's explanation on this blog http://blog.codinglight.com/2008/10/changing-attribute-parameters-at.html
Sounds like you want to consider implementing IXmlSerializable
You can implement IDataErrorInfo, then check range in Validate method.
public string this[string property] {
get { return Validate(property); }
}
public string Error { get; }
protected virtual string Validate(string property) {
var propertyInfo = this.GetType().GetProperty(property);
var results = new List<ValidationResult>();
var result = Validator.TryValidateProperty(
propertyInfo.GetValue(this, null),
new ValidationContext(this, null, null) {
MemberName = property
},
results);
if (!result) {
var validationResult = results.First();
return validationResult.ErrorMessage;
}
return string.Empty;
}
In sub class
protected override string Validate(string property) {
Debug.WriteLine(property);
if (property == nameof(YourProperty)) {
if (_property > 5) {
return "_property out of range";
}
}
return base.Validate(property);
}