Fileds to Properties Proxy - c#

let's imagine i have the following classes that i am not allowed to change:
public class BaseType
{
public UInt32 m_baseMember = 1;
public bool m_baseMemberBool = false;
}
public class ComposedType
{
public ComposedType()
{
m_baseData = new BaseType();
}
public UInt32 m_newMember = 2;
public BaseType m_baseData;
}
Now i want to edit those data by putting it in an PropertyGrid. I created two Wrapper classes like those ( http://msdn.microsoft.com/en-us/magazine/cc163816.aspx )
public class FieldsToPropertiesProxyTypeDescriptor : ICustomTypeDescriptor
{
private object _target;
// object to be described
public FieldsToPropertiesProxyTypeDescriptor(object target)
{
if (target == null)
throw new ArgumentNullException("target");
_target = target;
}
public object GetProxiedObject()
{
return _target;
}
object ICustomTypeDescriptor.GetPropertyOwner(PropertyDescriptor pd)
{
return _target;
// properties belong to the target object
}
AttributeCollection ICustomTypeDescriptor.GetAttributes()
{
// Gets the attributes of the target object
return TypeDescriptor.GetAttributes(_target, true);
}
string ICustomTypeDescriptor.GetClassName()
{
// Gets the class name of the target object
return TypeDescriptor.GetClassName(_target, true);
}
string ICustomTypeDescriptor.GetComponentName()
{
return TypeDescriptor.GetComponentName(_target, true);
}
TypeConverter ICustomTypeDescriptor.GetConverter()
{
return TypeDescriptor.GetConverter(_target, true);
}
EventDescriptor ICustomTypeDescriptor.GetDefaultEvent()
{
return TypeDescriptor.GetDefaultEvent(_target, true);
}
PropertyDescriptor ICustomTypeDescriptor.GetDefaultProperty()
{
return TypeDescriptor.GetDefaultProperty(_target, true);
}
object ICustomTypeDescriptor.GetEditor(Type editorBaseType)
{
return TypeDescriptor.GetEditor(_target, editorBaseType);
}
EventDescriptorCollection ICustomTypeDescriptor.GetEvents(Attribute[] attributes)
{
return TypeDescriptor.GetEvents(_target, attributes);
}
EventDescriptorCollection ICustomTypeDescriptor.GetEvents()
{
return TypeDescriptor.GetEvents( _target );
}
PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties()
{
return ((ICustomTypeDescriptor)this).GetProperties(null);
}
PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties( Attribute[] attributes)
{
bool filtering = (attributes != null && attributes.Length > 0);
PropertyDescriptorCollection props = new PropertyDescriptorCollection(null);
foreach (PropertyDescriptor prop in TypeDescriptor.GetProperties(_target, attributes, true))
{
props.Add(prop);
}
foreach (FieldInfo field in _target.GetType().GetFields())
{
FieldPropertyDescriptor fieldDesc = new FieldPropertyDescriptor(field);
if (!filtering || fieldDesc.Attributes.Contains(attributes))
props.Add(fieldDesc);
}
return props;
}
}
public class FieldPropertyDescriptor : PropertyDescriptor
{
private FieldInfo _field;
public FieldPropertyDescriptor(FieldInfo field) : base(field.Name, (Attribute[])field.GetCustomAttributes(typeof(Attribute), true))
{
_field = field;
}
public FieldInfo Field
{
get { return _field; }
}
public override bool Equals(object obj)
{
FieldPropertyDescriptor other = obj as FieldPropertyDescriptor;
return other != null && other._field.Equals(_field);
}
public override int GetHashCode()
{
return _field.GetHashCode();
}
public override bool IsReadOnly
{
get { return false; }
}
public override AttributeCollection Attributes
{
get
{
if (_field.FieldType.IsClass || _field.FieldType.IsArray)
{
Attribute[] expandable = new Attribute[1];
expandable[0] = new ExpandableObjectAttribute();
return AttributeCollection.FromExisting(base.Attributes, expandable);
}
return base.Attributes;
}
}
public override void ResetValue(object component)
{
}
public override bool CanResetValue(object component)
{
return false;
}
public override bool ShouldSerializeValue(object component)
{
return true;
}
public override Type ComponentType
{
get { return _field.DeclaringType; }
}
public override Type PropertyType
{
get { return _field.FieldType; }
}
public override object GetValue(object component)
{
if (component is FieldsToPropertiesProxyTypeDescriptor)
{
FieldsToPropertiesProxyTypeDescriptor proxy = (FieldsToPropertiesProxyTypeDescriptor)component;
return _field.GetValue(proxy.GetProxiedObject());
}
return _field.GetValue(component);
}
public override void SetValue(object component, object value)
{
if (component is FieldsToPropertiesProxyTypeDescriptor)
{
FieldsToPropertiesProxyTypeDescriptor proxy = (FieldsToPropertiesProxyTypeDescriptor)component;
_field.SetValue(proxy.GetProxiedObject(), value);
OnValueChanged(proxy.GetProxiedObject(), EventArgs.Empty);
return;
}
_field.SetValue(component, value);
OnValueChanged(component, EventArgs.Empty);
}
}
I can see and edit the 'm_newMember' in the PropertyGrid but i need to wrap the access to 'm_baseData' via FieldsToPropertiesProxyTypeDescriptor. How could i achieve this. Or is there a better way to wrap fields into Properties?

You can change attributes of a given class at runtime without changing that class. So you could write a custom TypeConverter and set it to your classes, something like this:
TypeDescriptor.AddAttributes(typeof(ComposedType), new TypeConverterAttribute(typeof(FieldsExpandableObjectConverter)));
TypeDescriptor.AddAttributes(typeof(BaseType), new TypeConverterAttribute(typeof(FieldsExpandableObjectConverter)));
With the following TypeConverter (re-using your FieldDescriptor class):
public class FieldsExpandableObjectConverter : ExpandableObjectConverter
{
public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext context, object value, Attribute[] attributes)
{
List<PropertyDescriptor> properties = new List<PropertyDescriptor>(base.GetProperties(context, value, attributes).OfType<PropertyDescriptor>());
if (value != null)
{
foreach (FieldInfo field in value.GetType().GetFields())
{
FieldPropertyDescriptor fieldDesc = new FieldPropertyDescriptor(field);
{
properties.Add(fieldDesc);
}
}
}
return new PropertyDescriptorCollection(properties.ToArray());
}
}

Related

Property Grid : How to set Display name label of each property to the right of grid?

I have implemented a custom property grid, I want to know if I can change position of label of each property from left to the right.
My custom property grid shows property as you can see in the picture.
I want to change it to be like this:
This is main code of Custom Property grid
public class CustomPropertyGrid : PropertyGrid
{
private System.ComponentModel.Container components = null;
private ReperesentAttr representAttr;
private myTab tab;
public CustomPropertyGrid()
{
this.representAttr = new ReperesentAttr("", "");
tab.SetRepresentAttr = this.representAttr;
InitializeComponent();
this.PropertySort = PropertySort.Alphabetical;
this.RightToLeft = RightToLeft.Yes;
// this.
}
public ReperesentAttr SetRepresentAttr
{
set
{
representAttr = value;
tab.SetRepresentAttr = this.representAttr;
}
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
if (components != null)
{
components.Dispose();
}
}
base.Dispose(disposing);
}
#region Codice generato da Progettazione componenti
/// <summary>
/// Metodo necessario per il supporto della finestra di progettazione. Non modificare
/// il contenuto del metodo con l'editor di codice.
/// </summary>
private void InitializeComponent()
{
//
// UserControl1
//
this.Name = "myPropertyGrid";
}
#endregion
protected override PropertyTab CreatePropertyTab(Type tabType)
{
tab = new myTab(representAttr);
return tab;
}
}
public class myTab : PropertyTab
{
private ReperesentAttr representAttr;
public myTab(ReperesentAttr representAttr)
{
this.representAttr = representAttr;
}
public ReperesentAttr SetRepresentAttr
{
set
{
representAttr = value;
}
}
// get the properties of the selected component
public override System.ComponentModel.PropertyDescriptorCollection GetProperties(object component, System.Attribute[] attributes)
{
PropertyDescriptorCollection properties;
if (attributes != null)
properties = TypeDescriptor.GetProperties(component, attributes);
else
properties = TypeDescriptor.GetProperties(component);
//Componet must implement the ICUSTOMCLASS interface.
if (component is ICustomClass )
{
ICustomClass bclass = (ICustomClass)component;
//The new array of properties, based on the PublicProperties properties of "model"
PropertyDescriptor[] arrProp = new PropertyDescriptor[bclass.PublicProperties.Count];
for (int i = 0; i < bclass.PublicProperties.Count; i++)
{
//Find the properties in the array of the propertis which neme is in the PubliCProperties
PropertyDescriptor prop = properties.Find(bclass.PublicProperties[i].Name, true);
//Build a new properties
arrProp[i] = TypeDescriptor.CreateProperty(prop.ComponentType, prop, new CategoryAttribute(this.representAttr.Category));
}
return new PropertyDescriptorCollection(arrProp);
}
else
{
return properties;
}
}
public override System.ComponentModel.PropertyDescriptorCollection GetProperties(object component)
{
return this.GetProperties(component, null);
}
// PropertyTab Name
public override string TabName
{
get
{
return "Properties";
}
}
//Image of the property tab (return a blank 16x16 Bitmap)
public override System.Drawing.Bitmap Bitmap
{
get
{
return new Bitmap(16, 16);
}
}
}
public class ReperesentAttr
{
string category = string.Empty;
string name = string.Empty;
public ReperesentAttr(string name, string category)
{
this.category = category;
this.name = name;
}
public string Category
{
set { category = value; }
get { return category; }
}
public string Name
{
set { name = value; }
get { return name; }
}
}
public interface ICustomClass
{
PropertyList PublicProperties
{
get;
set;
}
}
public class PropertyList : NameObjectCollectionBase
{
public void Add(Object value)
{
//The key for the object is taken from the object to insert
this.BaseAdd(((CustomProperty)value).Name, value);
}
public void Remove(String key)
{
this.BaseRemove(key);
}
public void Remove(int index)
{
this.BaseRemoveAt(index);
}
public void Clear()
{
this.BaseClear();
}
public CustomProperty this[String key]
{
get
{
return (CustomProperty)(this.BaseGet(key));
}
set
{
this.BaseSet(key, value);
}
}
public CustomProperty this[int indice]
{
get
{
return (CustomProperty)(this.BaseGet(indice));
}
set
{
this.BaseSet(indice, value);
}
}
public bool HasKey(String key)
{
foreach(String item in this.BaseGetAllKeys())
{
if(key == item)
return true;
}
return false;
}
}
Add this to your CustomPropertyGrid, this may get you started:
const int WS_EX_LAYOUTRTL = 0x400000;
private bool _RTL = false;
[Description("Change to the right-to-left layout."), DefaultValue(false),
Localizable(true), Category("Appearance"), Browsable(true)]
public bool Mirrored
{
get
{
return _RTL;
}
set
{
if (_RTL != value)
_RTL = value;
base.OnRightToLeftChanged(EventArgs.Empty);
}
}
protected override CreateParams CreateParams
{
get
{
CreateParams CP;
CP = base.CreateParams;
if (this.Mirrored)
CP.ExStyle = CP.ExStyle | WS_EX_LAYOUTRTL;
return CP;
}
}
From https://www.microsoft.com/middleeast/msdn/mirror.aspx

How to bind a DataGrid to a list of columns in WPF

I searched this, but I didn't find the answer to this specific question (although there are many similar ones).
I have a Column class in my ViewModel like this:
public class Column
{
public string Header { get; set; }
public ObservableCollection<double> Data { get; private set; }
public DummyColumn()
{
Data = new ObservableCollection<double>();
}
}
In MyViewModel class itself, I have Columns property:
public class MyViewModel
{
public ObservableCollection<Column> { get; private set; }
public MyViewModel()
{
Columns = new ObservableCollection<DummyColumn>();
var c1 = new DummyColumn() { Header = "A" };
c1.Data.Add(5);
c1.Data.Add(6);
Columns.Add(c1);
var c2 = new DummyColumn() { Header = "B" };
c2.Data.Add(51);
c2.Data.Add(61);
Columns.Add(c2);
}
}
I want to bind the Columns property of the latter class to the columns of a DataGrid in view. For each Column instance, I want to show the Header property as the column's header and its Data property as cells' values. How can I do that?
I reached this goal using ideas from here and here. I implemented three classes: Table, Row and RowPropertyDescriptor in my VM. Here is the code:
class RowPropertyDescriptor : PropertyDescriptor
{
private int index;
public RowPropertyDescriptor(string name, int index)
: base(name, null)
{
this.index = index;
}
#region PropertyDescriptor
public override string DisplayName { get { return Name; } }
public override Type ComponentType { get { return typeof(double); } }
public override bool IsReadOnly { get { return false; } }
public override Type PropertyType { get { return typeof(double); } }
public override object GetValue(object component)
{
return ((Row)component)[index];
}
public override void SetValue(object component, object value)
{
((Row)component)[index] = (double)value;
}
public override bool CanResetValue(object component)
{
return false;
}
public override void ResetValue(object component)
{
}
public override bool ShouldSerializeValue(object component)
{
return false;
}
#endregion
}
class Row : DynamicObject
{
private Table table;
private int row;
public Row(Table namedArraysView, int row)
{
this.table = namedArraysView;
this.row = row;
}
public double this[int col]
{
get { return table.RawData[col].Data[row]; }
set { table.RawData[col].Data[row] = value; }
}
public override bool TrySetMember(SetMemberBinder binder, object value)
{
int idx;
bool found = table.PropertiesIndex.TryGetValue(binder.Name, out idx);
if (found)
{
try
{
this[idx] = Convert.ToDouble(value);
return true;
}
catch (Exception ex)
{
return false;
}
}
return base.TrySetMember(binder, value);
}
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
int idx;
bool found = table.PropertiesIndex.TryGetValue(binder.Name, out idx);
if (found)
{
result = this[idx];
return true;
}
return base.TryGetMember(binder, out result);
}
public override IEnumerable<string> GetDynamicMemberNames()
{
return table.PropertyNames;
}
}
class Table : BindingList<Row>, ITypedList
{
public ObservableCollection<INamedArray> RawData { get; private set; }
internal List<string> PropertyNames { get; private set; }
internal Dictionary<string, int> PropertiesIndex { get; private set; }
public Table(ObservableCollection<INamedArray> headeredArrays)
{
bind(headeredArrays);
headeredArrays.CollectionChanged += (object sender, NotifyCollectionChangedEventArgs e) => { bind(headeredArrays); };
}
private void bind(ObservableCollection<INamedArray> headeredArrays)
{
Clear();
if (headeredArrays == null)
{
RawData = null;
PropertyNames = null;
PropertiesIndex = null;
return;
}
RawData = headeredArrays;
PropertyNames = RawData.Select(d => d.Name).ToList();
PropertiesIndex = new Dictionary<string, int>();
for (int i = 0; i < RawData.Count; i++)
PropertiesIndex.Add(RawData[i].Name, i);
int nRows = headeredArrays[0].Data.Count;
for (int i = 0; i < nRows; i++)
Add(new Row(this, i));
}
#region ITypedList
public PropertyDescriptorCollection GetItemProperties(PropertyDescriptor[] listAccessors)
{
var dynamicDescriptors = new List<PropertyDescriptor>();
if (this[0].GetDynamicMemberNames() == null) return new PropertyDescriptorCollection(new PropertyDescriptor[] { });
var memberNames = this[0].GetDynamicMemberNames().ToArray();
for (int i = 0; i < memberNames.Length; i++)
dynamicDescriptors.Add(new RowPropertyDescriptor(memberNames[i], i));
return new PropertyDescriptorCollection(dynamicDescriptors.ToArray());
}
public string GetListName(PropertyDescriptor[] listAccessors)
{
return null;
}
#endregion
}
Then, one can easily create a Table with passing his/her columns to this class. This Table can be visualised correctly in view. The only limitation is that binding is one way which is not very hard to work around.

Displaying data on property grid

Would like to display extracted data from xml file on property grid. XML file looks something like this:
<?xml version="1.0" encoding="utf-8" ?>
<customer>
<cust id="1">
<id>120</id>
<name>hello</name>
</cust>
<cust id="2">
<name>hello12</name>
<id>12012</id>
</cust>
</customer>
Now I want to extract data from XML on a property grid with server id as category(i.e it has to display cust id="1" in one category and cust id="2" in second category)
Try this code sample:
[TypeConverter(typeof(CustomerObjectConverter))]
public class Customer
{
internal readonly int ServerId;
public int Id { get; set; }
public string Name { get; set; }
public Customer(int serverId)
{
ServerId = serverId;
}
public override string ToString()
{
return Id + ", " + Name;
}
}
public class CustomerCollection : CollectionBase, ICustomTypeDescriptor
{
public CustomerCollection()
{
}
public CustomerCollection(IEnumerable<Customer> collection)
{
foreach (var item in collection)
Add(item);
}
public void Add(Customer emp)
{
List.Add(emp);
}
public void Remove(Customer emp)
{
List.Remove(emp);
}
public Customer this[int index]
{
get { return (Customer)List[index]; }
}
public String GetClassName()
{
return TypeDescriptor.GetClassName(this, true);
}
public AttributeCollection GetAttributes()
{
return TypeDescriptor.GetAttributes(this, true);
}
public String GetComponentName()
{
return TypeDescriptor.GetComponentName(this, true);
}
public TypeConverter GetConverter()
{
return TypeDescriptor.GetConverter(this, true);
}
public EventDescriptor GetDefaultEvent()
{
return TypeDescriptor.GetDefaultEvent(this, true);
}
public PropertyDescriptor GetDefaultProperty()
{
return TypeDescriptor.GetDefaultProperty(this, true);
}
public object GetEditor(Type editorBaseType)
{
return TypeDescriptor.GetEditor(this, editorBaseType, true);
}
public EventDescriptorCollection GetEvents(Attribute[] attributes)
{
return TypeDescriptor.GetEvents(this, attributes, true);
}
public EventDescriptorCollection GetEvents()
{
return TypeDescriptor.GetEvents(this, true);
}
public object GetPropertyOwner(PropertyDescriptor pd)
{
return this;
}
public PropertyDescriptorCollection GetProperties(Attribute[] attributes)
{
return GetProperties();
}
public PropertyDescriptorCollection GetProperties()
{
PropertyDescriptorCollection pds = new PropertyDescriptorCollection(null);
for (int i = 0; i < List.Count; i++)
{
CustomersCollectionPropertyDescriptor pd = new CustomersCollectionPropertyDescriptor(this, i);
pds.Add(pd);
}
return pds;
}
}
internal class CustomersCollectionPropertyDescriptor : PropertyDescriptor
{
private readonly CustomerCollection collection;
private readonly int index = -1;
public CustomersCollectionPropertyDescriptor(CustomerCollection coll, int idx)
: base("#" + idx.ToString(), null)
{
collection = coll;
index = idx;
}
public override AttributeCollection Attributes
{
get { return new AttributeCollection(null); }
}
public override string Category
{
get { return "Server " + collection[index].ServerId; }
}
public override bool CanResetValue(object component)
{
return true;
}
public override Type ComponentType
{
get { return collection.GetType(); }
}
public override string DisplayName
{
get { return "Customer " + (index + 1); }
}
public override string Description
{
get { return "Customer"; }
}
public override object GetValue(object component)
{
return collection[index];
}
public override bool IsReadOnly
{
get { return true; }
}
public override string Name
{
get { return "#" + index.ToString(); }
}
public override Type PropertyType
{
get { return collection[index].GetType(); }
}
public override void ResetValue(object component) {}
public override bool ShouldSerializeValue(object component)
{
return false;
}
public override void SetValue(object component, object value)
{
}
}
internal class CustomerObjectConverter : ExpandableObjectConverter
{
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
{
return false;
}
public override object ConvertTo(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, Type destinationType)
{
return destinationType == typeof(string) && value is Customer
? value.ToString()
: base.ConvertTo(context, culture, value, destinationType);
}
}
Usage. Paste the following code into constructor of the form containing PropertyGrid with the name propertyGrid:
const string xmlString = "<customer><cust id=\"1\"><id>120</id><name>hello</name></cust><cust id=\"2\"><name>hello12</name><id>12012</id></cust></customer>";
XmlDocument xml = new XmlDocument();
xml.LoadXml(xmlString);
XmlNode root = xml["customer"];
CustomerCollection customers = new CustomerCollection();
foreach (XmlNode node in root.ChildNodes)
customers.Add(new Customer(int.Parse(node.Attributes["id"].Value))
{
Id = int.Parse(node["id"].InnerText),
Name = node["name"].InnerText
});
propertyGrid.SelectedObject = customers;

MVC2 model validation mutually exclusive required annotation

Does anybody know of a good algorithm to mutually exclusively check two properties using a ModelValidator?
Something like:
[EitherPropertyRequired("BuildingNumber","BuildingName"]
public class Address{
public int BuildingNumber { get; set; }
public string BuildingName { get; set; }
}
I ended up creating an attribute and manually checking it with a custom ModelValidator. This custom model validator is checked using an AssociatedValidatorProvider which is registered in Application_Start().
protected void Application_Start()
{
ModelValidatorProviders.Providers.Add(new ZipValidationProvider());
}
public class ZipValidationProvider:AssociatedValidatorProvider
{
protected override IEnumerable<ModelValidator> GetValidators(ModelMetadata metadata, ControllerContext context, IEnumerable<Attribute> attributes)
{
foreach (var attribute in attributes.OfType<EitherPropertyRequiredAttribute>())
{
yield return new EitherPropertyRequiredValidator(metadata,
context, attribute.FirstProperty, attribute.SecondProperty, attribute.Message);
}
}
}
[AttributeUsage(AttributeTargets.Class)]
public class EitherPropertyRequiredAttribute : Attribute
{
public readonly string FirstProperty;
public readonly string SecondProperty;
public readonly string Message;
public EitherPropertyRequiredAttribute(string firstProperty, string secondProperty,
string message)
{
FirstProperty = firstProperty;
SecondProperty = secondProperty;
Message = message;
}
}
public class EitherPropertyRequiredValidator:ModelValidator
{
private readonly string firstProperty;
private readonly string secondProperty;
private readonly string message;
public EitherPropertyRequiredValidator(ModelMetadata metadata,
ControllerContext context,
string firstProperty,
string secondProperty,
string message)
:base(metadata,context)
{
this.firstProperty = firstProperty;
this.secondProperty = secondProperty;
this.message = message;
}
private PropertyInfo GetPropertyInfoRecursive(Type type, string property)
{
var prop = type.GetProperty(property);
if (prop != null) return prop;
foreach (var p in type.GetProperties())
{
if (p.PropertyType.Assembly == typeof (object).Assembly)
continue;
return GetPropertyInfoRecursive(p.PropertyType, property);
}
return null;
}
private object GetPropertyValueRecursive(object obj, PropertyInfo propertyInfo)
{
Type objectType = obj.GetType();
if(objectType.GetProperty(propertyInfo.Name) != null)
return propertyInfo.GetValue(obj, null);
foreach (var p in objectType.GetProperties())
{
if (p.PropertyType.Assembly == typeof(object).Assembly)
continue;
var o = p.GetValue(obj,null);
return GetPropertyValueRecursive(o, propertyInfo);
}
return null;
}
public override IEnumerable<ModelValidationResult> Validate(object container)
{
if (Metadata.Model == null)
yield break;
var firstPropertyInfo = GetPropertyInfoRecursive(Metadata.Model.GetType(),firstProperty);
if(firstPropertyInfo == null)
throw new InvalidOperationException("Unknown property:" + firstProperty);
var secondPropertyInfo = GetPropertyInfoRecursive(Metadata.Model.GetType(),secondProperty);
if(secondPropertyInfo == null)
throw new InvalidOperationException("Unknown property:" + secondProperty);
var firstPropertyValue = GetPropertyValueRecursive(Metadata.Model, firstPropertyInfo);
var secondPropertyValue = GetPropertyValueRecursive(Metadata.Model, secondPropertyInfo);
bool firstPropertyIsEmpty = firstPropertyValue == null ||
firstPropertyValue.ToString().Length == 0;
bool secondPropertyIsEmpty = secondPropertyValue == null ||
secondPropertyValue.ToString().Length == 0;
if (firstPropertyIsEmpty && secondPropertyIsEmpty)
{
yield return new ModelValidationResult
{
MemberName = firstProperty,
Message = message
};
}
}
}
[AttributeUsage(AttributeTargets.Class)]
public class EitherPropertyRequiredAttribute : ValidationAttribute
{
public override bool IsValid(object value)
{
// value will be the model
Address address = (Address)value;
// TODO: Check the properties of address here and return true or false
return true;
}
}
You could make this more generic by avoiding it casting to Address and using attribute properties and reflection.

Using a Dictionary in a propertygrid

I'd like to edit a list of key value(string, string) items using a propertygrid. When I use a Dictionary<string,string> as type the propertygrid will show a GUI, but it does not seem "enabled", ie. I can't add any items.
Is the Dictionary object supported, or is there any other object with which I could solve this problem?
I have done it following this code in the past:
class DictionaryPropertyGridAdapter : ICustomTypeDescriptor
{
IDictionary _dictionary;
public DictionaryPropertyGridAdapter(IDictionary d)
{
_dictionary = d;
}
public string GetComponentName()
{
return TypeDescriptor.GetComponentName(this, true);
}
public EventDescriptor GetDefaultEvent()
{
return TypeDescriptor.GetDefaultEvent(this, true);
}
public string GetClassName()
{
return TypeDescriptor.GetClassName(this, true);
}
public EventDescriptorCollection GetEvents(Attribute[] attributes)
{
return TypeDescriptor.GetEvents(this, attributes, true);
}
EventDescriptorCollection System.ComponentModel.ICustomTypeDescriptor.GetEvents()
{
return TypeDescriptor.GetEvents(this, true);
}
public TypeConverter GetConverter()
{
return TypeDescriptor.GetConverter(this, true);
}
public object GetPropertyOwner(PropertyDescriptor pd)
{
return _dictionary;
}
public AttributeCollection GetAttributes()
{
return TypeDescriptor.GetAttributes(this, true);
}
public object GetEditor(Type editorBaseType)
{
return TypeDescriptor.GetEditor(this, editorBaseType, true);
}
public PropertyDescriptor GetDefaultProperty()
{
return null;
}
PropertyDescriptorCollection
System.ComponentModel.ICustomTypeDescriptor.GetProperties()
{
return ((ICustomTypeDescriptor)this).GetProperties(new Attribute[0]);
}
public PropertyDescriptorCollection GetProperties(Attribute[] attributes)
{
ArrayList properties = new ArrayList();
foreach (DictionaryEntry e in _dictionary)
{
properties.Add(new DictionaryPropertyDescriptor(_dictionary, e.Key));
}
PropertyDescriptor[] props =
(PropertyDescriptor[])properties.ToArray(typeof(PropertyDescriptor));
return new PropertyDescriptorCollection(props);
}
}
class DictionaryPropertyDescriptor : PropertyDescriptor
{
IDictionary _dictionary;
object _key;
internal DictionaryPropertyDescriptor(IDictionary d, object key)
: base(key.ToString(), null)
{
_dictionary = d;
_key = key;
}
public override Type PropertyType
{
get { return _dictionary[_key].GetType(); }
}
public override void SetValue(object component, object value)
{
_dictionary[_key] = value;
}
public override object GetValue(object component)
{
return _dictionary[_key];
}
public override bool IsReadOnly
{
get { return false; }
}
public override Type ComponentType
{
get { return null; }
}
public override bool CanResetValue(object component)
{
return false;
}
public override void ResetValue(object component)
{
}
public override bool ShouldSerializeValue(object component)
{
return false;
}
}
private void Form1_Load(object sender, System.EventArgs e)
{
IDictionary d = new Hashtable();
d["Hello"] = "World";
d["Meaning"] = 42;
d["Shade"] = Color.ForestGreen;
propertyGrid1.SelectedObject = new DictionaryPropertyGridAdapter(d);
}
Worked well for us.
If you are trying to bind a class that contains a Dictionary property to a PropertyGrid, rather than binding the Dictionary itself directly, then here is a free (LGPL licensed) component that does exactly that: http://gendictedit.codeplex.com/

Categories