Click here to Skip to main content
15,911,715 members
Articles / Programming Languages / C#
Article

Bending the .NET PropertyGrid to Your Will

Rate me:
Please Sign up or sign in to vote.
4.89/5 (284 votes)
19 Dec 200211 min read 1.3M   29.5K   412   317
A set of classes to simplify using custom properties in the .NET PropertyGrid control.

Sample Image - bending_property.png

Introduction

With the initial release of the .NET Framework, I was impressed that Microsoft finally provided one of their coveted custom controls for public use—in this case, the Visual Basic-like property grid, System.Windows.Forms.PropertyGrid.  Upon using it, however, I realized that while the control is very flexible and powerful, it takes quite a bit of work to customize.  Below, I present a set of classes that increase the usability of the property grid.

Background

The property grid provided with the .NET Framework operates via reflection.  By using the SelectedObject or SelectedObjects property, you can select one or more objects into the control.  It then queries the objects for all their properties, organizes them into groups based on their CategoryAttribute, and uses various UITypeEditor and TypeConverter classes to allow the user to edit the properties as strings.

The problem with this is that it's not very flexible out of the box.  To use the grid, you must write a "surrogate class" that exposes properties that you want to show in the grid, instantiate an object of this class, and then select it into the grid.

Many times, however, it may not be desirable to create such a class.  Each surrogate has to be specific to your data, because the runtime merely uses reflection to ask the class what its properties are.  You wouldn't be able to easily write a surrogate class that you could reuse across multiple pieces of data unless they all happened to share the same properties.

Depending on the design of your system and the organization of your data, it may not be convenient to write a surrogate class.  Imagine you have a database of a hundred properties that are stored as key-value pairs.  To create a class to wrap this database, you would have to hand-code the get and set calls for each of the hundred properties.  Even if you wrote an automated tool to generate the code for you, there is still the issue of code bloat in your executable.

Lastly, creating a surrogate class doesn't lend itself well to "beautifying" the contents of the grid.  The standard behavior is to display the actual name of the property from the code.  This makes sense for a component like a form designer, where the developer needs to be able to quickly and easily make the connection between a property in the grid and the same property in code.  If the grid is being used as part of an interface for end-users, however, they should be presented with something a little less obtuse than "UniqueCustID."

Custom Type Descriptors

If you take a look at the project settings dialog in Visual Studio.NET, you'll notice that the grid contains properties with nicer-looking names like "Use of MFC," which opens a drop-down list that contains values such as "Use MFC in a Shared DLL."  Clearly, there is more going on here than a simple property with an enumerated type.

The answer lies in the System.ComponentModel.ICustomTypeDescriptor interface.  This interface allows a class to provide customized type information about itself.  In short, this allows you to modify the properties and events that the property grid sees when it queries the object for this information.

ICustomTypeDescriptor provides a number of functions that look like functions in the standard TypeDescriptor class.  In fact, when you implement an ICustomTypeDescriptor, you want to be able to leverage much of the standard behavior already provided by the framework.  For most functions, you can simply pass control to the standard TypeDescriptor, like so:

C#
AttributeCollection ICustomTypeDescriptor.GetAttributes()
{
     return TypeDescriptor.GetAttributes(this, true);
}

The first parameter for most of these functions is the object on which the information is being requested.  The second parameter tells the TypeDescriptor not to call the custom type descriptor to get the requested information.  Since we are the custom type descriptor, omitting this parameter would result in infinite recursion, as the custom type descriptor would be invoked again and again.

However, note that I originally said that ICustomTypeDescriptor allows a class to provide information about itself.  This would seem to imply that a class must exist about which we can provide type information, and this is true.  Some class must exist that can implement the ICustomTypeDescriptor interface to provide a list of properties to the grid.  But the object's interface does not need to look anything like the properties that it will expose, and that is where the flexibility lies.

The Solution

To this end, I have created the PropertyBag class.  It implements ICustomTypeDescriptor, but in the most generic fashion possible.  Rather than relying on hard-coded properties in the class itself, PropertyBag manages a collection of objects that describe the properties that should be displayed in the grid.  It does this by overriding ICustomTypeDescriptor.GetProperties(), generating its own custom property descriptors, and returning this collection.  Since control is never passed to the standard type descriptor, the grid doesn't even know about the "actual" properties that belong to the PropertyBag class itself.

The benefits of this are that properties can be added and removed at will, whereas a typical surrogate class would be fixed at compile-time.

Type descriptors only provide information about the existence and type of properties, however, because they implement methods that can be applied to any object of that type.  This means there needs to be some way to retrieve and store the values of the properties.  I have implemented the two methods below:

Method 1: Raising Events

The method implemented by the base PropertyBag class raises events whenever property values are queried or changed.  The GetValue event occurs when the property grid needs to know the value of a certain property, and SetValue occurs when the user has interactively changed the value of the property in the grid.

This method is useful in situations dealing with properties that are stored in files and databases.  When the event occurs, you can use the property name to index into the data source, using the same simple lookup code for every property.

Method 2: Storing the Values in a Table

For a simpler approach that might be more appropriate in some cases, I've derived a class from PropertyBag called PropertyTable.  This class provides all the generic functionality of PropertyBag but also contains a hashtable, indexed by property name, to store property values.  When a value is requested, the property is looked up in the table, and when the user updates it, the value in the hashtable is updated accordingly.

Rolling Your Own

Since PropertyBag provides virtual functions OnGetValue and OnSetValue that correspond to the events discussed above, you can also derive a class and provide your own method by overriding these two functions.

What's Included

PropertyBag Class

The PropertyBag class is very basic, exposing only two properties, two events, and two methods.

C#
public string DefaultProperty { get; set; }

The DefaultProperty property specifies the name of the default property in the property bag.  This is the property that is selected by default when the bag is selected into a PropertyGrid.

C#
public PropertySpecCollection Properties { get; }

The Properties collection manages the various properties in the bag.  Like many other .NET Framework collections, it implements IList, so functions like Add, AddRange, and Remove can be used to manipulate the properties in the bag.

C#
public event PropertySpecEventHandler GetValue;
public event PropertySpecEventHandler SetValue;
protected virtual void OnGetValue(PropertySpecEventArgs e);
protected virtual void OnSetValue(PropertySpecEventArgs e);

The GetValue event is raised whenever the property grid needs to request the value of a property.  This can happen for several reasons, such as displaying the property, comparing it to the default value to determine if it can be reset, among others.

The SetValue event is raised whenever the user modifies the value of the property through the grid.

Derived classes can override OnGetValue and OnSetValue as an alternative to adding an event handler.

PropertyTable Class

The PropertyTable class provides all the operations of PropertyBag, as well as an indexer property:

C#
public object this[string key] { get; set; }

This indexer is used to get and set property values that are stored in the table's internal hashtable.  Properties are indexed by name.

PropertySpec Class

PropertySpec provides 16 constructor overloads—too many to describe in detail here.  The overloads are various combinations of the following parameters, listed with the property to which it corresponds:

  • name (Name): The name of the property.
  • type (TypeName): The type of the property.  In the constructor, this can be either a Type object (such as one returned by the typeof() operator), or it can be a string representing the fully qualified type name (i.e., "System.Boolean").
  • category (Category): A string indicating the category to which the property belongs.  If this is null, the default category is used (usually "Misc").  This parameter has the same effect as attaching a CategoryAttribute to a property.
  • description (Description): A help string that is displayed in the description area at the bottom of the property grid.  If this is null, no description is displayed.  This parameter has the same effect as attaching a DescriptionAttribute to a property.
  • defaultValue (DefaultValue): The default value of the property.  If the current value of the property is not equal to the default value, the property is displayed in bold to indicate that it has been changed.  This property has the same effect as attaching a DefaultValueAttribute to a property.
  • editor (EditorTypeName): Indicates the type (derived from UITypeEditor) used to edit the property.  This can be used to provide a custom editor for a type that does not have one explicitly associated with it, or to override the editor associated with the type.  In the constructor, this parameter can be specified either as a Type object or a fully qualified type name.  It has the same effect as attaching an EditorAttribute to a property.
  • typeConverter (ConverterTypeName): Indicates the type converter (derived from TypeConverter) used to convert the property value to and from a string.  In the constructor, it can be specified either as a Type object or a fully qualified type name.  This parameter has the same effect as attaching a TypeConverterAttribute to the property.

Additionally, the following property is provided:

C#
public Attribute[] Attributes { get; set; }

With the Attributes property, you can include any additional attributes not supported directly by PropertySpecReadOnlyAttribute, for example.

Using the Code

Using a PropertyBag is a simple process:

  1. Instantiate an object of the PropertyBag class.
  2. Add a PropertySpec object to the PropertyBag.Properties collection for each property that you want to display in the grid.
  3. Add event handlers for the GetValue and SetValue events of the property bag, and set them up to access whichever data source you are using to store the property values.
  4. Select the property bag into a PropertyGrid control.

If you are using a PropertyTable, you would not add event handlers to the bag in step 3, but instead specify appropriate initial values to the properties, using the table's indexer.

A basic example:

PropertyBag bag = new PropertyBag();
bag.GetValue += new PropertySpecEventHandler(this.bag_GetValue);
bag.SetValue += new PropertySpecEventHandler(this.bag_SetValue);
bag.Properties.Add(new PropertySpec("Some Number", typeof(int)));
// ... add other properties ...
propertyGrid.SelectedObject = bag;

The project included with the article shows a more robust demonstration of the classes.

Points of Interest

One interesting benefit that requires no extra work at all is shown in the demo application, if you select multiple objects from the list.  Multiple selection is handled entirely by the property grid, so even if the objects are property bags with completely custom properties, the grid still works as expected—only the properties and property values that are shared among all the objects are displayed.

Future Plans

This class will eventually be absorbed into a larger user-interface utility library that I'm writing (hence the awkward namespace name in the source code).

One enhancement I plan to add in the future is to make it easier to specify a list of string values for a property, instead of requiring an enumerated type for such a list.  For example, the project properties dialog mentioned before has a "Use of MFC" property with the values "Use Standard Windows Libraries," "Use MFC in a Static Library," and "Use MFC in a Shared DLL."  Currently, the way you could do this is to write a TypeConverter for the property and override the three GetStandardValues* methods to provide a list of allowable values.  Optimally, I would like to have this built-in to the property bag so the user can simply provide a list of strings for a property, without requiring any external type converters to be explicitly written.  One particularly interesting way to accomplish this may be to use the System.Reflection.Emit functionality to generate an appropriate type converter in memory at runtime.

Secondly, I plan to make the available properties dynamic at runtime as well, in the sense that the property bag would fire an event when it builds the property list for the grid.  Any objects listening to the event could provide additional properties at that time.  This would make sense for dynamic data sources where it might be inconvenient to constantly add and remove properties from the bag or create a new bag when the selected object changes.

Of course, I'm open to suggestions for other possible enhancements and extensions to these classes.

Version History

  • 12/20/2002(v1.0): Initial release.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
United States United States
Tony is a graduate student in Computer Science at Virginia Tech, and also a teaching assistant for the department. Currently pursuing a Ph.D., he plans to teach at the university level after leaving school.

His research interest areas are digital image processing, programming languages, and compilers.

Comments and Discussions

 
GeneralDynamic Types Pin
jonansie1-Oct-03 2:52
jonansie1-Oct-03 2:52 
GeneralBlank display Pin
Member 54393118-Sep-03 13:19
Member 54393118-Sep-03 13:19 
GeneralCollection Pin
mikelb28-Aug-03 8:03
mikelb28-Aug-03 8:03 
Questionhow can I create remote object from the same class and assign it to ? Pin
caoyg4-Aug-03 21:21
caoyg4-Aug-03 21:21 
GeneralExpandableObjectConverter Pin
Shutty25-Jul-03 3:17
Shutty25-Jul-03 3:17 
GeneralPropertyBag Collection question Pin
mel_sadek24-Jul-03 15:45
mel_sadek24-Jul-03 15:45 
QuestionCategory Description? Pin
fredb24-Jul-03 3:07
fredb24-Jul-03 3:07 
GeneralHere is the VB.net version Pin
lbarbou17-Jul-03 9:45
lbarbou17-Jul-03 9:45 
'*******************************************************************
' *
' * PropertyBag.vb
' * --------------
' * Copyright (C) 2003 Tony Allowatt / Loic Barbou
' * THE SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS", WITHOUT WARRANTY
' * OF ANY KIND, EXPRESS OR IMPLIED. IN NO EVENT SHALL THE AUTHOR BE
' * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY ARISING FROM,
' * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OF THIS
' * SOFTWARE.
' *
' * Public types defined in this file:
' * ----------------------------------
' * class PropertySpec
' * class PropertySpecEventArgs
' * delegate PropertySpecEventHandler
' * class PropertyBag
' * class PropertyBag.PropertySpecCollection
' * class PropertyTable
' *
' *******************************************************************

Imports System
Imports System.Collections
Imports System.ComponentModel
Imports System.Drawing.Design



_
'/
'/ Represents a single property in a PropertySpec.
'/

Public Class PropertySpec
Private m_attributes() As Attribute
Private m_category As String
Private m_defaultValue As Object
Private m_description As String
Private editor As String
Private m_name As String
Private type As String
Private typeConverter As String


'/
'/ Initializes a new instance of the PropertySpec class.
'/

'/ <param name="name" />The name of the property displayed in the property grid.
'/ <param name="type" />The fully qualified name of the type of the property.
Public Sub New(ByVal name As String, ByVal type As String)
MyClass.New(name, type, Nothing, Nothing, Nothing)
End Sub 'New

'/
'/ Initializes a new instance of the PropertySpec class.
'/

'/ <param name="name" />The name of the property displayed in the property grid.
'/ <param name="type" />A Type that represents the type of the property.
Public Sub New(ByVal name As String, ByVal type As type)
MyClass.New(name, type.AssemblyQualifiedName, Nothing, Nothing, Nothing)
End Sub 'New

'/
'/ Initializes a new instance of the PropertySpec class.
'/

'/ <param name="name" />The name of the property displayed in the property grid.
'/ <param name="type" />The fully qualified name of the type of the property.
'/ <param name="category" />The category under which the property is displayed in the
'/ property grid.
Public Sub New(ByVal name As String, ByVal type As String, ByVal category As String)
MyClass.New(name, type, category, Nothing, Nothing)
End Sub 'New

'/
'/ Initializes a new instance of the PropertySpec class.
'/

'/ <param name="name" />The name of the property displayed in the property grid.
'/ <param name="type" />A Type that represents the type of the property.
'/ <param name="category" />
Public Sub New(ByVal name As String, ByVal type As type, ByVal category As String)
MyClass.New(name, type.AssemblyQualifiedName, category, Nothing, Nothing)
End Sub 'New

'/
'/ Initializes a new instance of the PropertySpec class.
'/

'/ <param name="name" />The name of the property displayed in the property grid.
'/ <param name="type" />The fully qualified name of the type of the property.
'/ <param name="category" />The category under which the property is displayed in the
'/ property grid.
'/ <param name="description" />A string that is displayed in the help area of the
'/ property grid.
Public Sub New(ByVal name As String, ByVal type As String, ByVal category As String, ByVal description As String)
MyClass.New(name, type, category, description, Nothing)
End Sub 'New

'/
'/ Initializes a new instance of the PropertySpec class.
'/

'/ <param name="name" />The name of the property displayed in the property grid.
'/ <param name="type" />A Type that represents the type of the property.
'/ <param name="category" />The category under which the property is displayed in the
'/ property grid.
'/ <param name="description" />A string that is displayed in the help area of the
'/ property grid.
Public Sub New(ByVal name As String, ByVal type As type, ByVal category As String, ByVal description As String)
MyClass.New(name, type.AssemblyQualifiedName, category, description, Nothing)
End Sub 'New

'/
'/ Initializes a new instance of the PropertySpec class.
'/

'/ <param name="name" />The name of the property displayed in the property grid.
'/ <param name="type" />The fully qualified name of the type of the property.
'/ <param name="category" />The category under which the property is displayed in the
'/ property grid.
'/ <param name="description" />A string that is displayed in the help area of the
'/ property grid.
'/ <param name="defaultValue" />The default value of the property, or null if there is
'/ no default value.
Public Sub New(ByVal name As String, ByVal type As String, ByVal category As String, ByVal description As String, ByVal defaultValue As Object)
Me.m_name = name
Me.type = type
Me.m_category = category
Me.m_description = description
Me.m_defaultValue = defaultValue
Me.m_attributes = Nothing
End Sub 'New


'/
'/ Initializes a new instance of the PropertySpec class.
'/

'/ <param name="name" />The name of the property displayed in the property grid.
'/ <param name="type" />A Type that represents the type of the property.
'/ <param name="category" />The category under which the property is displayed in the
'/ property grid.
'/ <param name="description" />A string that is displayed in the help area of the
'/ property grid.
'/ <param name="defaultValue" />The default value of the property, or null if there is
'/ no default value.
Public Sub New(ByVal name As String, ByVal type As type, ByVal category As String, ByVal description As String, ByVal defaultValue As Object)
MyClass.New(name, type.AssemblyQualifiedName, category, description, defaultValue)
End Sub 'New

'/
'/ Initializes a new instance of the PropertySpec class.
'/

'/ <param name="name" />The name of the property displayed in the property grid.
'/ <param name="type" />The fully qualified name of the type of the property.
'/ <param name="category" />The category under which the property is displayed in the
'/ property grid.
'/ <param name="description" />A string that is displayed in the help area of the
'/ property grid.
'/ <param name="defaultValue" />The default value of the property, or null if there is
'/ no default value.
'/ <param name="editor" />The fully qualified name of the type of the editor for this
'/ property. This type must derive from UITypeEditor.
'/ <param name="typeConverter" />The fully qualified name of the type of the type
'/ converter for this property. This type must derive from TypeConverter.
Public Sub New(ByVal name As String, ByVal type As String, ByVal category As String, ByVal description As String, ByVal defaultValue As Object, ByVal editor As String, ByVal typeConverter As String)
MyClass.New(name, type, category, description, defaultValue)
Me.editor = editor
Me.typeConverter = typeConverter
End Sub 'New


'/
'/ Initializes a new instance of the PropertySpec class.
'/

'/ <param name="name" />The name of the property displayed in the property grid.
'/ <param name="type" />A Type that represents the type of the property.
'/ <param name="category" />The category under which the property is displayed in the
'/ property grid.
'/ <param name="description" />A string that is displayed in the help area of the
'/ property grid.
'/ <param name="defaultValue" />The default value of the property, or null if there is
'/ no default value.
'/ <param name="editor" />The fully qualified name of the type of the editor for this
'/ property. This type must derive from UITypeEditor.
'/ <param name="typeConverter" />The fully qualified name of the type of the type
'/ converter for this property. This type must derive from TypeConverter.
Public Sub New(ByVal name As String, ByVal type As type, ByVal category As String, ByVal description As String, ByVal defaultValue As Object, ByVal editor As String, ByVal typeConverter As String)
MyClass.New(name, type.AssemblyQualifiedName, category, description, defaultValue, editor, typeConverter)
End Sub 'New

'/
'/ Initializes a new instance of the PropertySpec class.
'/

'/ <param name="name" />The name of the property displayed in the property grid.
'/ <param name="type" />The fully qualified name of the type of the property.
'/ <param name="category" />The category under which the property is displayed in the
'/ property grid.
'/ <param name="description" />A string that is displayed in the help area of the
'/ property grid.
'/ <param name="defaultValue" />The default value of the property, or null if there is
'/ no default value.
'/ <param name="editor" />The Type that represents the type of the editor for this
'/ property. This type must derive from UITypeEditor.
'/ <param name="typeConverter" />The fully qualified name of the type of the type
'/ converter for this property. This type must derive from TypeConverter.
Public Sub New(ByVal name As String, ByVal type As String, ByVal category As String, ByVal description As String, ByVal defaultValue As Object, ByVal editor As type, ByVal typeConverter As String)
MyClass.New(name, type, category, description, defaultValue, editor.AssemblyQualifiedName, typeConverter)
End Sub 'New


'/
'/ Initializes a new instance of the PropertySpec class.
'/

'/ <param name="name" />The name of the property displayed in the property grid.
'/ <param name="type" />A Type that represents the type of the property.
'/ <param name="category" />The category under which the property is displayed in the
'/ property grid.
'/ <param name="description" />A string that is displayed in the help area of the
'/ property grid.
'/ <param name="defaultValue" />The default value of the property, or null if there is
'/ no default value.
'/ <param name="editor" />The Type that represents the type of the editor for this
'/ property. This type must derive from UITypeEditor.
'/ <param name="typeConverter" />The fully qualified name of the type of the type
'/ converter for this property. This type must derive from TypeConverter.
Public Sub New(ByVal name As String, ByVal type As type, ByVal category As String, ByVal description As String, ByVal defaultValue As Object, ByVal editor As type, ByVal typeConverter As String)
MyClass.New(name, type.AssemblyQualifiedName, category, description, defaultValue, editor.AssemblyQualifiedName, typeConverter)
End Sub 'New


'/
'/ Initializes a new instance of the PropertySpec class.
'/

'/ <param name="name" />The name of the property displayed in the property grid.
'/ <param name="type" />The fully qualified name of the type of the property.
'/ <param name="category" />The category under which the property is displayed in the
'/ property grid.
'/ <param name="description" />A string that is displayed in the help area of the
'/ property grid.
'/ <param name="defaultValue" />The default value of the property, or null if there is
'/ no default value.
'/ <param name="editor" />The fully qualified name of the type of the editor for this
'/ property. This type must derive from UITypeEditor.
'/ <param name="typeConverter" />The Type that represents the type of the type
'/ converter for this property. This type must derive from TypeConverter.
Public Sub New(ByVal name As String, ByVal type As String, ByVal category As String, ByVal description As String, ByVal defaultValue As Object, ByVal editor As String, ByVal typeConverter As type)
MyClass.New(name, type, category, description, defaultValue, editor, typeConverter.AssemblyQualifiedName)
End Sub 'New

'/
'/ Initializes a new instance of the PropertySpec class.
'/

'/ <param name="name" />The name of the property displayed in the property grid.
'/ <param name="type" />A Type that represents the type of the property.
'/ <param name="category" />The category under which the property is displayed in the
'/ property grid.
'/ <param name="description" />A string that is displayed in the help area of the
'/ property grid.
'/ <param name="defaultValue" />The default value of the property, or null if there is
'/ no default value.
'/ <param name="editor" />The fully qualified name of the type of the editor for this
'/ property. This type must derive from UITypeEditor.
'/ <param name="typeConverter" />The Type that represents the type of the type
'/ converter for this property. This type must derive from TypeConverter.
Public Sub New(ByVal name As String, ByVal type As type, ByVal category As String, ByVal description As String, ByVal defaultValue As Object, ByVal editor As String, ByVal typeConverter As type)
MyClass.New(name, type.AssemblyQualifiedName, category, description, defaultValue, editor, typeConverter.AssemblyQualifiedName)
End Sub 'New


'/
'/ Initializes a new instance of the PropertySpec class.
'/

'/ <param name="name" />The name of the property displayed in the property grid.
'/ <param name="type" />The fully qualified name of the type of the property.
'/ <param name="category" />The category under which the property is displayed in the
'/ property grid.
'/ <param name="description" />A string that is displayed in the help area of the
'/ property grid.
'/ <param name="defaultValue" />The default value of the property, or null if there is
'/ no default value.
'/ <param name="editor" />The Type that represents the type of the editor for this
'/ property. This type must derive from UITypeEditor.
'/ <param name="typeConverter" />The Type that represents the type of the type
'/ converter for this property. This type must derive from TypeConverter.
Public Sub New(ByVal name As String, ByVal type As String, ByVal category As String, ByVal description As String, ByVal defaultValue As Object, ByVal editor As type, ByVal typeConverter As type)
MyClass.New(name, type, category, description, defaultValue, editor.AssemblyQualifiedName, typeConverter.AssemblyQualifiedName)
End Sub 'New


'/
'/ Initializes a new instance of the PropertySpec class.
'/

'/ <param name="name" />The name of the property displayed in the property grid.
'/ <param name="type" />A Type that represents the type of the property.
'/ <param name="category" />The category under which the property is displayed in the
'/ property grid.
'/ <param name="description" />A string that is displayed in the help area of the
'/ property grid.
'/ <param name="defaultValue" />The default value of the property, or null if there is
'/ no default value.
'/ <param name="editor" />The Type that represents the type of the editor for this
'/ property. This type must derive from UITypeEditor.
'/ <param name="typeConverter" />The Type that represents the type of the type
'/ converter for this property. This type must derive from TypeConverter.
Public Sub New(ByVal name As String, ByVal type As type, ByVal category As String, ByVal description As String, ByVal defaultValue As Object, ByVal editor As type, ByVal typeConverter As type)
MyClass.New(name, type.AssemblyQualifiedName, category, description, defaultValue, editor.AssemblyQualifiedName, typeConverter.AssemblyQualifiedName)
End Sub 'New

'/
'/ Gets or sets a collection of additional Attributes for this property. This can
'/ be used to specify attributes beyond those supported intrinsically by the
'/ PropertySpec class, such as ReadOnly and Browsable.
'/


Public Property Attributes() As Attribute()
Get
Return m_attributes
End Get
Set(ByVal Value As Attribute())
m_attributes = Value
End Set
End Property
'/
'/ Gets or sets the category name of this property.
'/


Public Property Category() As String
Get
Return m_category
End Get
Set(ByVal Value As String)
m_category = Value
End Set
End Property
'/
'/ Gets or sets the fully qualified name of the type converter
'/ type for this property.
'/


Public Property ConverterTypeName() As String
Get
Return typeConverter
End Get
Set(ByVal Value As String)
typeConverter = Value
End Set
End Property
'/
'/ Gets or sets the default value of this property.
'/


Public Property DefaultValue() As Object
Get
Return m_defaultValue
End Get
Set(ByVal Value As Object)
m_defaultValue = Value
End Set
End Property
'/
'/ Gets or sets the help text description of this property.
'/


Public Property Description() As String
Get
Return m_description
End Get
Set(ByVal Value As String)
m_description = Value
End Set
End Property
'/
'/ Gets or sets the fully qualified name of the editor type for
'/ this property.
'/


Public Property EditorTypeName() As String
Get
Return editor
End Get
Set(ByVal Value As String)
editor = Value
End Set
End Property
'/
'/ Gets or sets the name of this property.
'/


Public Property Name() As String
Get
Return m_name
End Get
Set(ByVal Value As String)
m_name = Value
End Set
End Property
'/
'/ Gets or sets the fully qualfied name of the type of this
'/ property.
'/


Public Property TypeName() As String
Get
Return type
End Get
Set(ByVal Value As String)
type = Value
End Set
End Property
End Class 'PropertySpec
_
'/
'/ Provides data for the GetValue and SetValue events of the PropertyBag class.
'/

Public Class PropertySpecEventArgs
Inherits EventArgs
Private m_property As PropertySpec
Private val As Object


'/
'/ Initializes a new instance of the PropertySpecEventArgs class.
'/

'/ <param name="property" />The PropertySpec that represents the property whose
'/ value is being requested or set.
'/ <param name="val" />The current value of the property.
Public Sub New(ByVal [property] As PropertySpec, ByVal val As Object)
Me.m_property = [property]
Me.val = val
End Sub 'New

'/
'/ Gets the PropertySpec that represents the property whose value is being
'/ requested or set.
'/


Public ReadOnly Property [Property]() As PropertySpec
Get
Return m_property
End Get
End Property
'/
'/ Gets or sets the current value of the property.
'/


Public Property Value() As Object
Get
Return val
End Get
Set(ByVal Value As Object)
val = Value
End Set
End Property
End Class 'PropertySpecEventArgs

'/
'/ Represents the method that will handle the GetValue and SetValue events of the
'/ PropertyBag class.
'/

Public Delegate Sub PropertySpecEventHandler(ByVal sender As Object, ByVal e As PropertySpecEventArgs)


'_

'/
'/ Represents a collection of custom properties that can be selected into a
'/ PropertyGrid to provide functionality beyond that of the simple reflection
'/ normally used to query an object's properties.
'/

Public Class PropertyBag
Implements ICustomTypeDescriptor 'ToDo: Add Implements Clauses for implementation methods of these interface(s)

'
'ToDo: Error processing original source shown below
'
'
'-----^--- Pre-processor directives not translated
'/
'
'ToDo: Error processing original source shown below
'
'
'--^--- Unexpected pre-processor directive
'/ Encapsulates a collection of PropertySpec objects.
'/
\
<serializable()> _
Public Class PropertySpecCollection
Implements IList 'ToDo: Add Implements Clauses for implementation methods of these interface(s)
Private innerArray As ArrayList


'/
'/ Initializes a new instance of the PropertySpecCollection class.
'/

Public Sub New()
innerArray = New ArrayList
End Sub 'New

'/
'/ Gets the number of elements in the PropertySpecCollection.
'/

'/ <value>
'/ The number of elements contained in the PropertySpecCollection.
'/

Public ReadOnly Property Count() As Integer Implements IList.count
Get
Return innerArray.Count
End Get
End Property
'/
'/ Gets a value indicating whether the PropertySpecCollection has a fixed size.
'/

'/ <value>
'/ true if the PropertySpecCollection has a fixed size; otherwise, false.
'/

Public ReadOnly Property IsFixedSize() As Boolean Implements IList.isfixedsize
Get
Return False
End Get
End Property
'/
'/ Gets a value indicating whether the PropertySpecCollection is read-only.
'/


Public ReadOnly Property IsReadOnly() As Boolean Implements IList.isreadonly
Get
Return False
End Get
End Property
'/
'/ Gets a value indicating whether access to the collection is synchronized (thread-safe).
'/

'/ <value>
'/ true if access to the PropertySpecCollection is synchronized (thread-safe); otherwise, false.
'/

Public ReadOnly Property IsSynchronized() As Boolean Implements IList.IsSynchronized
Get
Return False
End Get
End Property
'/
'/ Gets an object that can be used to synchronize access to the collection.
'/

'/ <value>
'/ An object that can be used to synchronize access to the collection.
'/

'ReadOnly Property ICollection.SyncRoot() As Object
ReadOnly Property SyncRoot() As Object Implements ICollection.SyncRoot
Get
Return Nothing
End Get
End Property
'/
'/ Gets or sets the element at the specified index.
'/ In C#, this property is the indexer for the PropertySpecCollection class.
'/

'/ <param name="index" />The zero-based index of the element to get or set.
'/ <value>
'/ The element at the specified index.
'/

'Default Public Property Item(ByVal index As Integer) As PropertySpec
' Get
' Return CType(innerArray(index), PropertySpec)
' End Get
' Set(ByVal Value As PropertySpec)
' innerArray(index) = Value
' End Set
'End Property
Default Public Property Item(ByVal index As Integer) As Object Implements IList.Item
Get
Return CType(innerArray(index), PropertySpec)
End Get
Set(ByVal Value As Object)
innerArray(index) = Value
End Set
End Property

'Public Property Item(ByVal index As Integer) As Object Implements IList.item
' Get
' Return CType(innerArray(index), Object)
' End Get
' Set(ByVal Value As Object)
' innerArray(index) = Value
' End Set
'End Property


'/
'/ Adds a PropertySpec to the end of the PropertySpecCollection.
'/

'/ <param name="value" />The PropertySpec to be added to the end of the PropertySpecCollection.
'/ <returns>The PropertySpecCollection index at which the value has been added.
Public Function Add(ByVal value As PropertySpec) As Integer
Dim index As Integer = innerArray.Add(value)

Return index
End Function 'Add


'/
'/ Adds the elements of an array of PropertySpec objects to the end of the PropertySpecCollection.
'/

'/ <param name="array" />The PropertySpec array whose elements should be added to the end of the
'/ PropertySpecCollection.
Public Sub AddRange(ByVal array() As PropertySpec)
innerArray.AddRange(array)
End Sub 'AddRange


'/
'/ Removes all elements from the PropertySpecCollection.
'/

Public Sub Clear() Implements IList.clear
innerArray.Clear()
End Sub 'Clear


'/
'/ Determines whether a PropertySpec is in the PropertySpecCollection.
'/

'/ <param name="item" />The PropertySpec to locate in the PropertySpecCollection. The element to locate
'/ can be a null reference (Nothing in Visual Basic).
'/ <returns>true if item is found in the PropertySpecCollection; otherwise, false.
Public Overloads Function Contains(ByVal item As PropertySpec) As Boolean
Return innerArray.Contains(item)
End Function 'Contains


'/
'/ Determines whether a PropertySpec with the specified name is in the PropertySpecCollection.
'/

'/ <param name="name" />The name of the PropertySpec to locate in the PropertySpecCollection.
'/ <returns>true if item is found in the PropertySpecCollection; otherwise, false.
Public Overloads Function Contains(ByVal name As String) As Boolean
Dim spec As PropertySpec
For Each spec In innerArray
If spec.Name = name Then
Return True
End If
Next spec
Return False
End Function 'Contains


'/
'/ Copies the entire PropertySpecCollection to a compatible one-dimensional Array, starting at the
'/ beginning of the target array.
'/

'/ <param name="array" />The one-dimensional Array that is the destination of the elements copied
'/ from PropertySpecCollection. The Array must have zero-based indexing.
Public Overloads Sub CopyTo(ByVal array() As PropertySpec)
innerArray.CopyTo(array)
End Sub 'CopyTo


'/
'/ Copies the PropertySpecCollection or a portion of it to a one-dimensional array.
'/

'/ <param name="array" />The one-dimensional Array that is the destination of the elements copied
'/ from the collection.
'/ <param name="index" />The zero-based index in array at which copying begins.
Public Overloads Sub CopyTo(ByVal array() As PropertySpec, ByVal index As Integer)
innerArray.CopyTo(array, index)
End Sub 'CopyTo


'/
'/ Returns an enumerator that can iterate through the PropertySpecCollection.
'/

'/ <returns>An IEnumerator for the entire PropertySpecCollection.
Public Function GetEnumerator() As IEnumerator Implements IList.getenumerator
Return innerArray.GetEnumerator()
End Function 'GetEnumerator


'/
'/ Searches for the specified PropertySpec and returns the zero-based index of the first
'/ occurrence within the entire PropertySpecCollection.
'/

'/ <param name="value" />The PropertySpec to locate in the PropertySpecCollection.
'/ <returns>The zero-based index of the first occurrence of value within the entire PropertySpecCollection,
'/ if found; otherwise, -1.
Public Overloads Function IndexOf(ByVal value As PropertySpec) As Integer
Return innerArray.IndexOf(value)
End Function 'IndexOf


'/
'/ Searches for the PropertySpec with the specified name and returns the zero-based index of
'/ the first occurrence within the entire PropertySpecCollection.
'/

'/ <param name="name" />The name of the PropertySpec to locate in the PropertySpecCollection.
'/ <returns>The zero-based index of the first occurrence of value within the entire PropertySpecCollection,
'/ if found; otherwise, -1.
Public Overloads Function IndexOf(ByVal name As String) As Integer
Dim i As Integer = 0

Dim spec As PropertySpec
For Each spec In innerArray
If spec.Name = name Then
Return i
End If
i += 1
Next spec

Return -1
End Function 'IndexOf


'/
'/ Inserts a PropertySpec object into the PropertySpecCollection at the specified index.
'/

'/ <param name="index" />The zero-based index at which value should be inserted.
'/ <param name="value" />The PropertySpec to insert.
Public Sub Insert(ByVal index As Integer, ByVal value As PropertySpec)
innerArray.Insert(index, value)
End Sub 'Insert


'/
'/ Removes the first occurrence of a specific object from the PropertySpecCollection.
'/

'/ <param name="obj" />The PropertySpec to remove from the PropertySpecCollection.
Public Overloads Sub Remove(ByVal obj As PropertySpec)
innerArray.Remove(obj)
End Sub 'Remove


'/
'/ Removes the property with the specified name from the PropertySpecCollection.
'/

'/ <param name="name" />The name of the PropertySpec to remove from the PropertySpecCollection.
Public Overloads Sub Remove(ByVal name As String)
Dim index As Integer = IndexOf(name)
RemoveAt(index)
End Sub 'Remove


'/
'/ Removes the object at the specified index of the PropertySpecCollection.
'/

'/ <param name="index" />The zero-based index of the element to remove.
Public Sub RemoveAt(ByVal index As Integer) Implements IList.removeat
innerArray.RemoveAt(index)
End Sub 'RemoveAt


'/
'/ Copies the elements of the PropertySpecCollection to a new PropertySpec array.
'/

'/ <returns>A PropertySpec array containing copies of the elements of the PropertySpecCollection.
Public Function ToArray() As PropertySpec()
Return CType(innerArray.ToArray(GetType(PropertySpec)), PropertySpec())
End Function 'ToArray


'
'ToDo: Error processing original source shown below
'
'
'------^--- Pre-processor directives not translated
'/
'
'ToDo: Error processing original source shown below
'
'
'--^--- Unexpected pre-processor directive
'/ This member supports the .NET Framework infrastructure and is not intended to be used directly from your code.
'/

'Sub ICollection.CopyTo(array As Array, index As Integer)
Overloads Sub CopyTo(ByVal array As Array, ByVal index As Integer) Implements ICollection.CopyTo
CopyTo(CType(array, PropertySpec()), index)
End Sub 'ICollection.CopyTo


'/
'/ This member supports the .NET Framework infrastructure and is not intended to be used directly from your code.
'/

Function Add(ByVal value As Object) As Integer Implements IList.add
Return Add(CType(value, PropertySpec))
End Function 'IList.Add


'/
'/ This member supports the .NET Framework infrastructure and is not intended to be used directly from your code.
'/

Overloads Function Contains(ByVal obj As Object) As Boolean Implements IList.contains
Return Contains(CType(obj, PropertySpec))
End Function 'IList.Contains

'/
'/ This member supports the .NET Framework infrastructure and is not intended to be used directly from your code.
'/


Property IList(ByVal index As Integer) As Object
Get
Return CType(Me, PropertySpecCollection)(index)
End Get
Set(ByVal Value As Object)
CType(Me, PropertySpecCollection)(index) = CType(Value, PropertySpec)
End Set
End Property


'/
'/ This member supports the .NET Framework infrastructure and is not intended to be used directly from your code.
'/

Overloads Function IndexOf(ByVal obj As Object) As Integer Implements IList.IndexOf
Return IndexOf(CType(obj, PropertySpec))
End Function 'IList.IndexOf


'/
'/ This member supports the .NET Framework infrastructure and is not intended to be used directly from your code.
'/

Sub Insert(ByVal index As Integer, ByVal value As Object) Implements IList.insert
Insert(index, CType(value, PropertySpec))
End Sub 'IList.Insert


'/
'/ This member supports the .NET Framework infrastructure and is not intended to be used directly from your code.
'/

Overloads Sub Remove(ByVal value As Object) Implements IList.Remove
Remove(CType(value, PropertySpec))

End Sub 'IList.Remove
End Class 'PropertySpecCollection
'_ '
'ToDo: Error processing original source shown below
'
'
'------^--- Pre-processor directives not translated
'
'ToDo: Error processing original source shown below
'
'
'--^--- Unexpected pre-processor directive
'
'ToDo: Error processing original source shown below
'
'
'-----^--- Pre-processor directives not translated
Private Class PropertySpecDescriptor
Inherits PropertyDescriptor '
'ToDo: Error processing original source shown below
'
'
'--^--- Unexpected pre-processor directive
'
'ToDo: Error processing original source shown below
'
'
'-----^--- Pre-processor directives not translated
'
'ToDo: Error processing original source shown below
'
'
'--^--- Unexpected pre-processor directive
Private bag As PropertyBag
Private item As PropertySpec


Public Sub New(ByVal item As PropertySpec, ByVal bag As PropertyBag, ByVal name As String, ByVal attrs() As Attribute)
MyBase.New(name, attrs)
Me.bag = bag
Me.item = item
End Sub 'New


Public Overrides ReadOnly Property ComponentType() As Type
Get
Return item.GetType()
End Get
End Property

Public Overrides ReadOnly Property IsReadOnly() As Boolean
Get
Return Attributes.Matches(ReadOnlyAttribute.Yes)
End Get
End Property

Public Overrides ReadOnly Property PropertyType() As Type
Get
Return Type.GetType(item.TypeName)
End Get
End Property

Public Overrides Function CanResetValue(ByVal component As Object) As Boolean
If item.DefaultValue Is Nothing Then
Return False
Else
Return Not Me.GetValue(component).Equals(item.DefaultValue)
End If
End Function 'CanResetValue

Public Overrides Function GetValue(ByVal component As Object) As Object
' Have the property bag raise an event to get the current value
' of the property.
Dim e As New PropertySpecEventArgs(item, Nothing)
bag.OnGetValue(e)
Return e.Value
End Function 'GetValue


Public Overrides Sub ResetValue(ByVal component As Object)
SetValue(component, item.DefaultValue)
End Sub 'ResetValue


Public Overrides Sub SetValue(ByVal component As Object, ByVal value As Object)
' Have the property bag raise an event to set the current value
' of the property.
Dim e As New PropertySpecEventArgs(item, value)
bag.OnSetValue(e)
End Sub 'SetValue


Public Overrides Function ShouldSerializeValue(ByVal component As Object) As Boolean
Dim val As Object = Me.GetValue(component)

If item.DefaultValue Is Nothing And val Is Nothing Then
Return False
Else
Return Not val.Equals(item.DefaultValue)
End If
End Function 'ShouldSerializeValue
End Class 'PropertySpecDescriptor '
'ToDo: Error processing original source shown below
'
'
'-----^--- Pre-processor directives not translated
Private m_defaultProperty As String '
'ToDo: Error processing original source shown below
'
'
'--^--- Unexpected pre-processor directive
Private m_properties As PropertySpecCollection


'/
'/ Initializes a new instance of the PropertyBag class.
'/

Public Sub New()
m_defaultProperty = Nothing
m_properties = New PropertySpecCollection
End Sub 'New

'/
'/ Gets or sets the name of the default property in the collection.
'/


Public Property DefaultProperty() As String
Get
Return m_defaultProperty
End Get
Set(ByVal Value As String)
m_defaultProperty = Value
End Set
End Property
'/
'/ Gets the collection of properties contained within this PropertyBag.
'/


Public ReadOnly Property Properties() As PropertySpecCollection
Get
Return m_properties
End Get
End Property
'/
'/ Occurs when a PropertyGrid requests the value of a property.
'/

Public Event GetValue As PropertySpecEventHandler

'/
'/ Occurs when the user changes the value of a property in a PropertyGrid.
'/

Public Event SetValue As PropertySpecEventHandler


'/
'/ Raises the GetValue event.
'/

'/ <param name="e" />A PropertySpecEventArgs that contains the event data.
Protected Overridable Sub OnGetValue(ByVal e As PropertySpecEventArgs)
' If Not (GetValue Is Nothing) Then
RaiseEvent GetValue(Me, e)
'End If
End Sub 'OnGetValue

'/
'/ Raises the SetValue event.
'/

'/ <param name="e" />A PropertySpecEventArgs that contains the event data.
Protected Overridable Sub OnSetValue(ByVal e As PropertySpecEventArgs)
' If Not (SetValue Is Nothing) Then
RaiseEvent SetValue(Me, e)
' End If
End Sub 'OnSetValue

'
'ToDo: Error processing original source shown below
'
'
'-----^--- Pre-processor directives not translated
' Most of the functions required by the ICustomTypeDescriptor are
'
'ToDo: Error processing original source shown below
'
'
'--^--- Unexpected pre-processor directive
' merely pssed on to the default TypeDescriptor for this type,
' which will do something appropriate. The exceptions are noted
' below.
Function GetAttributes() As AttributeCollection Implements ICustomTypeDescriptor.GetAttributes
Return TypeDescriptor.GetAttributes(Me, True)
End Function 'ICustomTypeDescriptor.GetAttributes


Function GetClassName() As String Implements ICustomTypeDescriptor.GetClassName
Return TypeDescriptor.GetClassName(Me, True)
End Function 'ICustomTypeDescriptor.GetClassName


Function GetComponentName() As String Implements ICustomTypeDescriptor.GetComponentName
Return TypeDescriptor.GetComponentName(Me, True)
End Function 'ICustomTypeDescriptor.GetComponentName


Function GetConverter() As TypeConverter Implements ICustomTypeDescriptor.GetConverter
Return TypeDescriptor.GetConverter(Me, True)
End Function 'ICustomTypeDescriptor.GetConverter


Function GetDefaultEvent() As EventDescriptor Implements ICustomTypeDescriptor.GetDefaultEvent
Return TypeDescriptor.GetDefaultEvent(Me, True)
End Function 'ICustomTypeDescriptor.GetDefaultEvent


Function GetDefaultProperty() As PropertyDescriptor Implements ICustomTypeDescriptor.GetDefaultProperty
' This function searches the property list for the property
' with the same name as the DefaultProperty specified, and
' returns a property descriptor for it. If no property is
' found that matches DefaultProperty, a null reference is
' returned instead.
Dim propertySpec As PropertySpec = Nothing
If Not (DefaultProperty Is Nothing) Then
Dim index As Integer = Properties.IndexOf(DefaultProperty)
propertySpec = Properties(index)
End If

If Not (propertySpec Is Nothing) Then
Return New PropertySpecDescriptor(propertySpec, Me, propertySpec.Name, Nothing)
Else
Return Nothing
End If
End Function 'ICustomTypeDescriptor.GetDefaultProperty

Function GetEditor(ByVal editorBaseType As Type) As Object Implements ICustomTypeDescriptor.GetEditor
Return TypeDescriptor.GetEditor(Me, editorBaseType, True)
End Function 'ICustomTypeDescriptor.GetEditor


Overloads Function GetEvents() As EventDescriptorCollection Implements ICustomTypeDescriptor.GetEvents
Return TypeDescriptor.GetEvents(Me, True)
End Function 'ICustomTypeDescriptor.GetEvents


Overloads Function GetEvents(ByVal attributes() As Attribute) As EventDescriptorCollection Implements ICustomTypeDescriptor.GetEvents
Return TypeDescriptor.GetEvents(Me, attributes, True)
End Function 'ICustomTypeDescriptor.GetEvents


Overloads Function GetProperties() As PropertyDescriptorCollection Implements ICustomTypeDescriptor.GetProperties
Return CType(Me, ICustomTypeDescriptor).GetProperties(New Attribute(0) {})
End Function 'ICustomTypeDescriptor.GetProperties


Overloads Function GetProperties(ByVal attributes() As Attribute) As PropertyDescriptorCollection Implements ICustomTypeDescriptor.GetProperties
' Rather than passing this function on to the default TypeDescriptor,
' which would return the actual properties of PropertyBag, I construct
' a list here that contains property descriptors for the elements of the
' Properties list in the bag.
Dim props As New ArrayList

Dim mproperty As PropertySpec
For Each mproperty In Properties
Dim attrs As New ArrayList

' If a category, description, editor, or type converter are specified
' in the PropertySpec, create attributes to define that relationship.
If Not (mproperty.Category Is Nothing) Then
attrs.Add(New CategoryAttribute(mproperty.Category))
End If
If Not (mproperty.Description Is Nothing) Then
attrs.Add(New DescriptionAttribute(mproperty.Description))
End If
If Not (mproperty.EditorTypeName Is Nothing) Then
attrs.Add(New EditorAttribute(mproperty.EditorTypeName, GetType(UITypeEditor)))
End If
If Not (mproperty.ConverterTypeName Is Nothing) Then
attrs.Add(New TypeConverterAttribute(mproperty.ConverterTypeName))
End If
' Additionally, append the custom attributes associated with the
' PropertySpec, if any.
If Not (mproperty.Attributes Is Nothing) Then
attrs.AddRange(mproperty.Attributes)
End If
Dim attrArray As Attribute() = CType(attrs.ToArray(GetType(Attribute)), Attribute())

' Create a new property descriptor for the property item, and add
' it to the list.
Dim pd As New PropertySpecDescriptor(mproperty, Me, mproperty.Name, attrArray)
props.Add(pd)
Next mproperty

' Convert the list of PropertyDescriptors to a collection that the
' ICustomTypeDescriptor can use, and return it.
Dim propArray As PropertyDescriptor() = CType(props.ToArray(GetType(PropertyDescriptor)), PropertyDescriptor())
Return New PropertyDescriptorCollection(propArray)
End Function 'ICustomTypeDescriptor.GetProperties


Function GetPropertyOwner(ByVal pd As PropertyDescriptor) As Object Implements ICustomTypeDescriptor.GetPropertyOwner
Return Me
End Function 'ICustomTypeDescriptor.GetPropertyOwner
End Class 'PropertyBag
'_ '
'ToDo: Error processing original source shown below
'
'
'-----^--- Pre-processor directives not translated
'
'ToDo: Error processing original source shown below
'
'
'--^--- Unexpected pre-processor directive

'/
'/ An extension of PropertyBag that manages a table of property values, in
'/ addition to firing events when property values are requested or set.
'/

Public Class PropertyTable
Inherits PropertyBag
Private propValues As Hashtable


'/
'/ Initializes a new instance of the PropertyTable class.
'/

Public Sub New()
propValues = New Hashtable
End Sub 'New

'/
'/ Gets or sets the value of the property with the specified name.
'/

In C#, this property is the indexer of the PropertyTable class.


'/


Default Public Property Item(ByVal key As String) As Object
Get
Return propValues(key)
End Get
Set(ByVal Value As Object)
propValues(key) = Value
End Set
End Property

'/
'/ This member overrides PropertyBag.OnGetValue.
'/

Protected Overrides Sub OnGetValue(ByVal e As PropertySpecEventArgs)
e.Value = propValues(e.Property.Name)
MyBase.OnGetValue(e)
End Sub 'OnGetValue


'/
'/ This member overrides PropertyBag.OnSetValue.
'/

Protected Overrides Sub OnSetValue(ByVal e As PropertySpecEventArgs)
propValues(e.Property.Name) = e.Value
MyBase.OnSetValue(e)
End Sub 'OnSetValue
End Class 'PropertyTable
'
GeneralRe: Here is the VB.net version Pin
jcontract22-Aug-03 9:58
jcontract22-Aug-03 9:58 
GeneralRe: Here is the VB.net version Pin
segaa30-Nov-03 3:05
segaa30-Nov-03 3:05 
Generalmultiline textbox in the propertygrid Pin
Anonymous24-Jun-03 2:05
Anonymous24-Jun-03 2:05 
GeneralRe: multiline textbox in the propertygrid Pin
artzi25-Jun-03 4:13
artzi25-Jun-03 4:13 
GeneralGet Properties By Category (New Method) Pin
spook19-Jun-03 10:16
spook19-Jun-03 10:16 
GeneralUsing TypeConverters Pin
spook18-Jun-03 3:38
spook18-Jun-03 3:38 
GeneralRe: Using TypeConverters Pin
Anonymous30-Jun-03 13:27
Anonymous30-Jun-03 13:27 
GeneralRe: Using TypeConverters Pin
Naqsh5-Oct-03 15:23
sussNaqsh5-Oct-03 15:23 
GeneralList of Values Pin
karuppasamy natarajan13-May-03 4:22
karuppasamy natarajan13-May-03 4:22 
GeneralRe: List of Values Pin
Member 22727328-May-03 4:30
Member 22727328-May-03 4:30 
GeneralRe: List of Values Pin
Jeff Bramwell28-May-03 7:32
Jeff Bramwell28-May-03 7:32 
GeneralRe: List of Values Pin
Member 22727328-May-03 8:21
Member 22727328-May-03 8:21 
GeneralRe: List of Values Pin
Rajesh Sadashivan28-May-03 13:26
Rajesh Sadashivan28-May-03 13:26 
GeneralTony, you ROCK!!!!!!!!!!!! :-D Thanks! -NT Pin
spazzzmo6-May-03 13:10
spazzzmo6-May-03 13:10 
QuestionHow to display multilayer category??? Pin
chenjian2-May-03 21:32
chenjian2-May-03 21:32 
GeneralLinked labels in the PropertyGrid Pin
Raphy30-Apr-03 4:58
Raphy30-Apr-03 4:58 
QuestionRequired / optional properties rendered differently? Pin
gapcio6-Apr-03 18:07
gapcio6-Apr-03 18:07 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.