How useful is a Read-Only property
By a rich property object I mean an object with a lot of public and browsable properties that would activate the PropertyGrid’s scrollbar. By just disabling the PropertyGrid, the scrollbar would be also disabled and a full property inspection impossible. Not to mention that if a collection property is exposed, a disabled PropertyGrid wouldn’t be able to open the CollectionEditor for it.How to implement a Read-Only property
The basic idea behind this is to mark each browsable property, which is about to be exposed by our PropertyGrid, with the ReadOnly attribute and to control the current state of this attribute by code using reflections (System.Reflection namespace).If you want to expose collection properties, do not provide a ReadOnly attribute for them, otherwise the CollectionEditor will not be activated!
C# .NET
public class MyObject
{
private string objectAttribute1;
private string objectAttribute2;
private string objectAttribute3;
[ReadOnly(true)]
public string ObjectAttribute1
{
get { return this.objectAttribute1; }
set { this.objectAttribute1 = value; }
}
[ReadOnly(true)]
public string ObjectAttribute2
{
get { return this.objectAttribute2; }
set { this.objectAttribute2 = value; }
}
[ReadOnly(true)]
public string ObjectAttribute3
{
get { return this.objectAttribute3; }
set { this.objectAttribute3 = value; }
}
public MyObject(string attr1, string attr2, string attr3)
{
this.objectAttribute1 = attr1;
this.objectAttribute2 = attr2;
this.objectAttribute3 = attr3;
}
}
In order to do this I will create a new UserControl object which inherits from the original PropertyGrid control.
All logic for updating the ReadOnly attribute states will be implemented in this method:
C# .NET
void SetBrowsablePropertiesAsReadOnly(object selectedObject, bool isReadOnly)
which takes as arguments the current object exposed by the PropertyGrid and the read-only state to be achived.
I will create for this new control a ReadOnly property that will provide full support at design time. Each time the value of this property is changed, the SetBrowsablePropertiesAsReadOnly() method will be called in order to update the PropertyGrid.
C# .NET
public partial class MyPropertyGrid : PropertyGrid
{
private bool isReadOnly;
public bool ReadOnly
{
get { return this.isReadOnly; }
set
{
this.isReadOnly = value;
this.SetBrowsablePropertiesAsReadOnly(this.SelectedObject, value);
}
}
protected override void OnSelectedObjectsChanged(EventArgs e)
{
this.SetBrowsablePropertiesAsReadOnly(this.SelectedObject, this.isReadOnly);
base.OnSelectedObjectsChanged(e);
}
public MyPropertyGrid() : base()
{
InitializeComponent();
}
/// <summary>
/// Chnages the state of the ReadOnly attribute regarding the isReadOnly flag value.
/// </summary>
/// <param name="selectedObject">The current object exposed by the PropertyGrid.</param>
/// <param name="isReadOnly">The current read-only state of the PropertyGrid.</param>
private void SetBrowsablePropertiesAsReadOnly(object selectedObject, bool isReadOnly)
{
if (selectedObject != null)
{
// Get all the properties of the selected object...
PropertyDescriptorCollection props = TypeDescriptor.GetProperties(selectedObject.GetType());
foreach (PropertyDescriptor propDescript in props)
{
// Consider only the properties which are browsable and are not collections...
if (propDescript.IsBrowsable && propDescript.PropertyType.GetInterface("ICollection", true) == null)
{
ReadOnlyAttribute attr = propDescript.Attributes[typeof(ReadOnlyAttribute)] as ReadOnlyAttribute;
// If the current property has a ReadOnly attribute,
// update its state regarding the current ReadOnly state of the PropertyGrid.
if (attr != null)
{
FieldInfo field = attr.GetType().GetField("isReadOnly", BindingFlags.NonPublic | BindingFlags.Instance);
field.SetValue(attr, isReadOnly, BindingFlags.NonPublic | BindingFlags.Instance, null, null);
}
}
}
}
}
}
How to extend the Read-Only functionality to CollectionEditors
Actually is the same basic idea. All you have to do is to implement the SetBrowsablePropertiesAsReadOnly() method into you customized CollectionEditor and make it work in the same way I showed you above.If you want to know more about how to handle the CollectionEditor, please read this article: How to: Take control over the Collection Editor's PropertyGrid
10 comments:
Thanks for this article.
Great and exclusive solution!
It is what I looked for!
Thanks for sharing. Exactly what I need.
I'm using more than one PropertyGrid in my project. When I set MyPropertyGrid.ReadOnly to True, all of the PropertyGrid controls, including Windows form propertyGrid, become read-only. How do I get around this problem. Many thanks.
How to if i would like to dinamic change only some properties attribute instead the whole property grid?
Hi, this is a great solution.
But I'm wondering if there is another way how to achive the read-only effect wihtout the necessary of marking all properties in all classes as [ReadOnly()].
Any ideas?
Thank you.
this is a good attempt but a bad and complex solution that can create issues elsewhere. for example, in our case it was creating problems with radio buttons. a simple solution like the following enables/disables a grid without messing and overloading any default method:
// does not let the user modify the grid content
private void propertyGrid_Enter(object sender, EventArgs e)
{
propertyGrid.Enabled = false;
}
// re-enable the grid and the scrollbars
private void transitTreeView_Click(object sender, EventArgs e)
{
propertyGrid.Enabled = true;
}
Actually, there's a much better and simpler solution:
TypeDescriptor.AddAttributes(this.SelectedObject, new Attribute[] { new ReadOnlyAttribute(true/false) });
Try this:
private void propertyGrid1_PropertyValueChanged(object s, PropertyValueChangedEventArgs e)
{
//PaulB @ BSI -- don't allow user to reset any values (reset to old value)
propertyGrid1.SelectedGridItem.PropertyDescriptor.SetValue(propertyGrid1.SelectedObject, e.OldValue);
//**if we prefer stepping through objects...
//GridItem objGridItem = propertyGrid1.SelectedGridItem;
//PropertyDescriptor objPropertyDescriptor = propertyGrid1.SelectedGridItem.PropertyDescriptor;
//objPropertyDescriptor.SetValue(propertyGrid1.SelectedObject, e.OldValue);
}
Como hago para colocar solo un atributo en REadOnly y no a todo??.
I found this: http://codinglight.blogspot.com/2008/10/changing-attribute-parameters-at.html
Post a Comment