![]() |
Desktop Development »
Grid & Data Controls »
PropertyGrid
Intermediate
License: The Code Project Open License (CPOL)
DynamicProperties for PropertyGridBy Mizan RahmanTell PropertyGrid what to display, when to display, and how to display |
C# (C# 2.0), Windows, .NET (.NET 2.0, .NET 3.0, .NET 3.5), GDI+, Dev
|
|
Advanced Search Add to IE Search |
|
|
|
||||||||||||||||
PropertyGrid control is one of most coveted controls provided by Microsoft. Just by assigning your object to its SelectObject property allows the user to modify the object's public properties. Using it, we can be less dependent on dialogs based user interface (Edit controls, ListBox, ComboBox, etc.) to get user input, rather now we can use this generic user interface to get user input. But wait, in dialog box I could disable a control or hide a control based on user selection of another control or controls. How do I do this with PropertyGrid or can I do this? The answer is yes, you can do it. But it requires filling in lots of bits and pieces at the appropriate places and one has to be quite knowledgeable about how PropertyGrid works. Well I got my knowledge on this by searching the net and by reading MSDN over and over. In this article, I will show you how you can have these following features in your class:
System.FlagsAttribute SortOrderAttribute allows you specify the sort order of each properties.All these can be done at runtime. During my search, I found some articles which I have listed at end of this article. They have few of these features in limited form. What I tried to do with my solution was to make a generic solution for the developer so she/he can tell PropertyGrid what to display, when to display, and how to display from within their class.
After looking at the articles, I defined a set of design goals:
PropertyGrid, derived from System.Windows.Forms.PropertyGrid. Here are the classes that make up the solution:
I will now briefly explain how these classes work. You can of course look at the code to know about these classes in detail. Code for these classes is fairly straight forward and with comments:).
This class maintain a collection of all the properties of your class and caches for performance gain. The collection also contains any property you create on-the-fly. This class is used by DynamicTypeDescriptorProvider class.
This is the class that makes it all possible. You use this class type in the construction of System.ComponentMode.TypeDescriptionProviderAttribute attribute.You must attach this attribute to your class for your class to be able to show/hide/create properties on-the-fly. And you must have a callback function with the following signature in your class:
public void ModifyDynamicProperties
(System.ComponentModel.PropertyDescriptorCollection pdc)
This class is used from within your class' callback function, allowing you to create a new property of your object.
This class allow you to display your enumeration data type in a fancy way where you could have a description of each member of the enumeration. For enumeration with System.FlagsAttribute attribute, it will display CheckListBox control instead of a simple ListBox control. This class will make use of EnumTypeConverter and/or EnumItemAttribute classes if they are present with the enumeration type.
You use this class type in the construction System.ComponentModel.EditorAttribute. You must have this attribute on the enumeration type itself or a property with the enumeration type to make use of the EnumEditorUI.
This attribute is used on the member of an enumeration. It has three properties: DisplayName, Browsable, and Description. This class is used by both EnumTypeConverter and/or EnumItemAttribute classes if present. The Description is only used by EnumTypeEditor class and in turn by EnumEditorUI class.
If you want to hide a member of an enumeration, you must attach this converter through System.ComponentModel.TypeConverterAttribute attribute on your enumeration.
Below is a code snippet that shows how to use the solution for a sample class:
[TypeConverter(typeof(EnumTypeConverter))]
[Editor(typeof(EnumTypeEditor), typeof(UITypeEditor))]
[Flags]
public enum Days
{
[EnumItem("Not Selected", true, "Event will not reoccur.")]
None = 0,
[EnumItem("Monday", true, "Day of the Moon.")]
Mon = 1,
[EnumItem("Tuesday", true, "Day of Mars.")]
Tue = 2,
[EnumItem("Wednesday", true, "Day of Mercury.")]
Wed = 4,
[EnumItem("Thursday", true, "Day of Jupiter.")]
Thr = 8,
[EnumItem("Friday", true, "Venus's day.")]
Fri = 16,
// hide this one, just for the sake of it
[EnumItem("Saturday", false, "Day of Saturn.")]
Sat = 32,
// hide this one, just for the sake of it
[EnumItem("Sunday", false, "Day of the sun.")]
Sun = 64,
[EnumItem("Weekdays", true, "All days except Saturday and Sunday.")]
Work = Days.Mon | Days.Tue | Days.Wed | Days.Thr | Days.Fri,
[EnumItem("Weekend", true, "Only Saturday and Sunday.")]
NoWork = Days.Sat | Days.Sun,
}
public enum Action
{
Hide,
Show,
MakeReadOnly,
}
[TypeDescriptionProvider(typeof(DynamicTypeDescriptionProvider))]
public class TestClass
{
public TestClass()
{
}
private Days m_PropertyA = Days.None;
[Description("Demonstrate how to work with enumeration data type in PropertyGrid.")]
public Days PropertyA
{
get
{
return m_PropertyA;
}
set
{
m_PropertyA = value;
}
}
private Action m_PropertyB = Action.Show;
[Description("Changing the value of this property will effect PropertyC")]
[RefreshProperties(RefreshProperties.All)] // this attribute is needed
public Action PropertyB
{
get
{
return m_PropertyB;
}
set
{
m_PropertyB = value;
}
}
private string m_PropertyC = String.Empty;
[Description("This property's behaviour is controlled by PropertyB.")]
public string PropertyC
{
get
{
return m_PropertyC;
}
set
{
m_PropertyC = value;
}
}
private bool m_PropertyD = false;
[Description("Setting this value to true will show a
new property of type boolean with name 'PropertyE'.")]
[RefreshProperties(RefreshProperties.All)] // this attribute is needed
public bool PropertyD
{
get
{
return m_PropertyD;
}
set
{
m_PropertyD = value;
}
}
/// <summary>
/// This is a callback function for DynamicTypeDescriptionProvider.
/// You can modify the collection in this method.
/// Things you can do in this method:
/// Hide a property
/// Show a property
/// Add/Remove attributes of a property
/// Create a new property on the fly
/// </summary>
/// <param name="pdc">
public void ModifyDynamicProperties(PropertyDescriptorList pdl)
{
PropertyDescriptor pd = pdl.Find("PropertyC", false);
Debug.Assert(pd != null);
pdl.Remove(pd);
switch (PropertyB)
{
case Action.Show:
pdl.Add( TypeDescriptor.CreateProperty( this.GetType(),
pd, new BrowsableAttribute(true),
new ReadOnlyAttribute(false)));
break;
case Action.Hide:
pdl.Add(TypeDescriptor.CreateProperty(this.GetType(),
pd, new BrowsableAttribute(false)));
break;
case Action.MakeReadOnly:
pdl.Add(TypeDescriptor.CreateProperty(this.GetType(),
pd, new BrowsableAttribute(true),
new ReadOnlyAttribute(true)));
break;
}
PropertyDescriptor pdOnFlyE = pdl.Find("PropertyE", false);
if (PropertyD == true && pdOnFlyE == null)
{
PropertyDescriptor pdNew = new PseudoPropertyDescriptor
(this, true, "PropertyE", typeof(bool),
new BrowsableAttribute(true),
new ReadOnlyAttribute(false),
new DescriptionAttribute("This property was
created on the fly(Pseudo)."),
new SortOrderAttribute(1));
pdNew.AddValueChanged(this, new EventHandler(this.OnChanged)); // we want a
// change notification
// NOTE: In you practical scnerio, you would probably
// keep a reference of this new PropertyDescriptor
// one class level memeber variable.
// This way you can access its info whenever you need it
pdl.Add(pdNew);
}
else if (PropertyD == false && pdOnFlyE != null)
{
pdl.Remove(pdOnFlyE);
}
pdl.SortUsingSortAttribute();
}
private void OnChanged(object sender, EventArgs e)
{
PsudoPropertyDescriptor ppd = (PsudoPropertyDescriptor)sender;
Console.WriteLine("OnChanged for property '" + ppd.Name + "',
new value is: " + ppd.GetValue(this).ToString() );
}
}
When you get into the System.ComponentModel.TypeDescriptor class, it is interesting to the number of classes that play a role in providing a description of a type.
General
News
Question
Answer
Joke
Rant
Admin
|
PermaLink |
Privacy |
Terms of Use
Last Updated: 23 Jun 2009 Editor: Sean Ewington |
Copyright 2009 by Mizan Rahman Everything else Copyright © CodeProject, 1999-2009 Web17 | Advertise on the Code Project |