Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Enable or disable PropertyGrid item

824 views
Skip to first unread message

David W. Simmonds

unread,
May 12, 2003, 9:59:26 AM5/12/03
to
VS.NET 2003 IDE has functionality that allows a property grid item to be
disabled (grayed out) whenever if another property item is changed. An
example of this is for the Edit control property "Auto VScroll". It is
grayed out/disabled when the property "Multiline" is False. It is enabled
when the "Multiline" property is True. How is this done? I don't see a way
to enable or disable an item programmatically.


Bharat Patel [MSFT]

unread,
May 12, 2003, 6:07:45 PM5/12/03
to
Hi David,

Not sure if you can disable the property in the PropertyGrid but you can hide
it using the directions provided by Shawn in the follwoing post.

http://groups.google.com/groups?hl=en&lr=&ie=UTF-8&oe=UTF-8&selm=OfCi2jcrBHA.225
2%40tkmsftngp03


Hope this helps!
Bharat Patel
Microsoft, Visual Basic .NET

This posting is provided "AS IS" with no warranties, and confers no rights.
Please reply to newsgroups only. Thanks.

David W. Simmonds

unread,
May 12, 2003, 10:30:51 PM5/12/03
to
Visual Studio .NET 2002 and 2003 have this capability. How did they pull it
off?

""Bharat Patel [MSFT]"" <bha...@online.microsoft.com> wrote in message
news:fuBpyLNG...@cpmsftngxa06.phx.gbl...

David Jackman

unread,
May 13, 2003, 10:16:35 AM5/13/03
to
My guess would be that they are using custom PropertyDescriptor classes
for these properties. The property descriptor for the "Auto VScroll"
property returns true for the ReadOnly property when the "Multiline" is
true. Then the property descriptor for "Multiline" has a
RefreshPropertiesAttribute set to All (which causes the property grid to
re-get property descriptor information for other properties).

I did this kind of trick to get a property to use a drop-down editor
when another property had one value, then change to a modal dialog
editor when that property had a different value.

(If this explanation doesn't make sense, I can try to concoct some
sample code around this)

..David..

David W. Simmonds

unread,
May 13, 2003, 10:33:10 AM5/13/03
to
If you could send me some sample code, that would be great. I'm running on a
short schedule here and any help would be greatly appreciated.

"David Jackman" <david....@nextpage.com.nospam> wrote in message
news:3EC0FE43...@nextpage.com.nospam...

Jay B. Harlow [MVP - Outlook]

unread,
May 13, 2003, 5:32:52 PM5/13/03
to
David,
Could you possible post an example on the .NET Client Web

http://www.windowsforms.net/Default.aspx?tabindex=4&tabid=49

As I'm sure others (including me) would like to see an example...

Just a thought
Jay

"David Jackman" <david....@nextpage.com.nospam> wrote in message
news:3EC0FE43...@nextpage.com.nospam...

David W. Simmonds

unread,
May 13, 2003, 6:23:37 PM5/13/03
to
I would if I had a sample. I don't yet have a solution for this.

"Jay B. Harlow [MVP - Outlook]" <Jay_H...@email.msn.com> wrote in message
news:uIEK3cZG...@TK2MSFTNGP10.phx.gbl...

Jay B. Harlow [MVP - Outlook]

unread,
May 14, 2003, 11:11:38 AM5/14/03
to
David,
:-)

I was actually addressing David Jackman with my statement, as he suggested
he does have a solution...

Jay

"David W. Simmonds" <da...@simmonds.ca> wrote in message
news:Jfewa.164230$ya.50...@news1.calgary.shaw.ca...

David Jackman

unread,
May 19, 2003, 10:37:31 AM5/19/03
to
Coming up with a sample I can post will take some time, so be patient.
From the windowsforms.net page I can't readily tell how to submit a
sample. Is there a different page for this?
Also, would it be better to attach sample code as an attachment to the
post, or inside the post text?

Jay B. Harlow [MVP - Outlook]

unread,
May 19, 2003, 11:46:51 AM5/19/03
to
David,
If the sample is small (15 - 20 lines) I tend to do it inline. If the sample
has many files and many lines I attach a zip file.

There is a link that creates an email to submit a article/sample to
www.windowsforms.net.

http://www.windowsforms.net/Default.aspx?tabindex=10&tabid=52

Jay

"David Jackman" <david....@nextpage.com.nospam> wrote in message

news:3EC8EC2B...@nextpage.com.nospam...

David Jackman

unread,
May 19, 2003, 1:21:15 PM5/19/03
to
A complete example would have several different classes (though they
would be small). I'll probably attach a single file with all of the
classes in it here, and a .zip of files (one per class) on the
windowsforms.net website. (Hopefully in the next couple of days)

..David..

David Jackman

unread,
May 21, 2003, 11:01:17 AM5/21/03
to
Okay, I've come up with some sample code that does what the original post
was asking for: using the value of one property to determine the read-only
state of another property. I'll put a few more things into the sample
before I post it on the windowsforms.net site (like sample type converters
and UI type editors).
The bad news is that to accomplish the task at hand, it requires quite a
bit of customized infrastructure, so this may not work for you if you don't
have control of the property grid itself.

To start, here is the class I'll use for the SelectedObject in the grid:
[PropertyTab(typeof(CustomPropertyTab), PropertyTabScope.Component)]
public class SelectedObject
{
private string message1 = "initial value";
private bool message1readonly = false;

[Description("This property is read only if the Message1ReadOnly property is true.")]
public string Message1 { get { return message1; } set { message1 = value; } }

[RefreshProperties(RefreshProperties.All)]
[Description("Determines the read only state for the Message 1 property.")]
public bool Message1ReadOnly { get { return message1readonly; } set { message1readonly = value; } }
}

The class attribute specifies a custom property tab, which is necessary for
getting a custom property descriptor for the properties in the class. Here
is the custom property tab class:
public class CustomPropertyTab : PropertyTab
{
public override string TabName { get { return "CustomPropertyTab"; } }

public override Bitmap Bitmap { get { return new Bitmap(16, 16); } }

public override PropertyDescriptorCollection GetProperties(object component)
{
return GetProperties(component, null);
}

public override PropertyDescriptorCollection GetProperties(object component, Attribute[] attributes)
{
PropertyDescriptorCollection coll = new PropertyDescriptorCollection(null);
foreach (PropertyInfo prop in component.GetType().GetProperties())
{
bool add = true;
foreach (Attribute att in attributes)
{
Attribute[] propAtts = (Attribute[])prop.GetCustomAttributes(att.GetType(), true);
if (propAtts == null || propAtts.Length == 0)
{
if (att.IsDefaultAttribute())
{
continue;
}
else
{
add = false;
break;
}
}
foreach (Attribute propAtt in propAtts)
{
if (!att.Match(propAtt))
{
add = false;
break;
}
}
if (!add)
break;
}
if (add)
{
coll.Add(new CustomPropertyDescriptor(component, prop));
}
}
return coll;
}
}

Here is the custom property descriptor class. This class is pretty straightforward,
and just does the obvious things based on the property info, which was obtained by
the property tab through reflection. The exception is for the ReadOnly property,
which gets its value from the other property in the special case.
public class CustomPropertyDescriptor : PropertyDescriptor
{
protected object m_editObject;
protected PropertyInfo m_propInfo;


public CustomPropertyDescriptor(object component, PropertyInfo propInfo)
: base(propInfo.Name, new Attribute[]{CategoryAttribute.Default})
{
m_editObject = component;
m_propInfo = propInfo;
}


/// <summary>
/// Indicates the category that the property will appear in. The value will be the value given for the <see cref="CategoryAttribute"/>
/// attribute for the property, if one exists. If there is no CategoryAttribute, then this will be <see cref="c_DefaultCategoryName"/>.
/// </summary>
public override string Category
{
get
{
object[] atts = m_propInfo.GetCustomAttributes(typeof(CategoryAttribute), true);
return atts.Length > 0 ? ((CategoryAttribute)atts[0]).Category : "General";
}
}


/// <summary>
/// Indicates the name for the property that will be displayed in the PropertyGrid control. The value will be the value given for the
/// <see cref="DisplayNameAttribute"/> attribute for the property. If there is no DisplayNameAttribute, then this will be the name of
/// the property.
/// </summary>
public override string DisplayName
{
get
{
object[] atts = m_propInfo.GetCustomAttributes(typeof(DisplayNameAttribute), true);
return atts.Length > 0 ? ((DisplayNameAttribute)atts[0]).Name : m_propInfo.Name;
}
}


/// <summary>
/// Indicates the description for the property. The value will be the value given for the <see cref="DescriptionAttribute"/>
/// attribute for the property, if one exists. If there is no DescriptionAttribute, then this will be null.
/// </summary>
public override string Description
{
get
{
object[] atts = m_propInfo.GetCustomAttributes(typeof(DescriptionAttribute), true);
return atts.Length > 0 ? ((DescriptionAttribute)atts[0]).Description : null;
}
}


public override bool IsBrowsable { get { return (m_propInfo.Name.Equals("DontShow")) ? false : true; } }

/// <summary>
/// Implementation of abstract <see cref="PropertyDescriptor.ComponentType"/>.
/// </summary>
public override Type ComponentType { get { return m_propInfo.DeclaringType; } }

/// <summary>
/// Implementation of abstract <see cref="PropertyDescriptor.IsReadOnly"/>.
/// </summary>
public override bool IsReadOnly
{
get
{
if (m_editObject is SelectedObject && m_propInfo.Name.Equals("Message1"))
{
return ((SelectedObject)m_editObject).Message1ReadOnly;
}
return !m_propInfo.CanWrite;
}
}

/// <summary>
/// Implementation of abstract <see cref="PropertyDescriptor.PropertyType"/>.
/// </summary>
public override Type PropertyType { get { return m_propInfo.PropertyType; } }


/// <summary>
/// Implementation of abstract <see cref="PropertyDescriptor.CanResetValue"/>.
/// </summary>
public override bool CanResetValue(object component)
{
return true;
}


/// <summary>
/// Implementation of abstract <see cref="PropertyDescriptor.GetValue"/>.
/// </summary>
public override object GetValue(object component)
{
return m_propInfo.GetValue(component, null);
}


/// <summary>
/// Implementation of abstract <see cref="PropertyDescriptor.ResetValue"/>.
/// </summary>
public override void ResetValue(object component)
{
}


/// <summary>
/// Implementation of abstract <see cref="PropertyDescriptor.SetValue"/>.
/// </summary>
public override void SetValue(object component, object value)
{
m_propInfo.SetValue(component, value, null);
}

/// <summary>
/// Implementation of abstract <see cref="PropertyDescriptor.ShouldSerializeValue"/>.
/// </summary>
public override bool ShouldSerializeValue(object component)
{
return false;
}


public override object GetEditor(Type editorBaseType)
{
object[] atts = m_propInfo.GetCustomAttributes(typeof(EditorAttribute), true);
foreach (EditorAttribute editatt in atts)
{
if (editatt.EditorBaseTypeName == editorBaseType.AssemblyQualifiedName)
{
Type editortype = Type.GetType(editatt.EditorTypeName);
return Activator.CreateInstance(editortype);
}
}
return base.GetEditor(editorBaseType);
}


protected override void FillAttributes(IList attList)
{
base.FillAttributes(attList);
object[] atts = m_propInfo.GetCustomAttributes(typeof(RefreshPropertiesAttribute), true);
foreach (Attribute att in atts)
{
attList.Add(att);
}
atts = m_propInfo.GetCustomAttributes(typeof(TypeConverterAttribute), true);
foreach (Attribute att in atts)
{
attList.Add(att);
}
}
}

Unfortunately, we also need to subclass the property grid so it will use the
custom property tab by default:
public class CustomPropertyGrid : PropertyGrid
{
protected override Type DefaultTabType { get { return typeof(CustomPropertyTab); } }
}

Finally, here's the main window form to hold the whole thing together:
public class MainForm : System.Windows.Forms.Form
{
private CustomPropertyGrid propertyGrid;
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.Container components = null;

public MainForm()
{
//
// Required for Windows Form Designer support
//
InitializeComponent();

SelectedObject target = new SelectedObject();
propertyGrid.SelectedObject = target;
}

/// <summary>
/// Clean up any resources being used.
/// </summary>
protected override void Dispose(bool disposing)
{
if (disposing)
{
if (components != null)
{
components.Dispose();
}
}
base.Dispose(disposing);
}

#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.propertyGrid = new CustomPropertyGrid();
this.SuspendLayout();
//
// propertyGrid
//
this.propertyGrid.CommandsVisibleIfAvailable = true;
this.propertyGrid.Dock = System.Windows.Forms.DockStyle.Fill;
this.propertyGrid.LargeButtons = false;
this.propertyGrid.LineColor = System.Drawing.SystemColors.ScrollBar;
this.propertyGrid.Name = "propertyGrid";
this.propertyGrid.Size = new System.Drawing.Size(292, 271);
this.propertyGrid.TabIndex = 0;
this.propertyGrid.Text = "propertyGrid";
this.propertyGrid.ViewBackColor = System.Drawing.SystemColors.Window;
this.propertyGrid.ViewForeColor = System.Drawing.SystemColors.WindowText;
//
// MainForm
//
this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
this.ClientSize = new System.Drawing.Size(292, 271);
this.Controls.AddRange(new System.Windows.Forms.Control[] {
this.propertyGrid});
this.Name = "MainForm";
this.Text = "Dynamic Property Grid";
this.ResumeLayout(false);

}
#endregion

/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.Run(new MainForm());
}
}

0 new messages