Dynamic Property Grid

This entry is part 1 of 5 in the series C#: Property Grid
(Last Updated On: )

There are a bunch of resources around for adding property grid to your .NET 4.5 project however I often find that they are really bits and pieces of the solution and very rarely an entire overview. There have been examples of using a class as your property grids object. But what if you are working with dynamic data in which a static class may not be the answer? I will provide an example below of just such a case which includes a parent type static class as well. If you have any questions please feel free to ask.

The first step is to create our type builder and to do that we must create the assembly, builder and module.

 AssemblyName assemblyName = new AssemblyName("ASSEMBLY_NAME_YOU_WANT");

AssemblyBuilder assemblyBuilder = Thread.GetDomain().DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run);

ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule"MODULE_NAME_YOU_WANT");

Here we need to define our type. Give it any name you want and what types it will extend. The third option is optional. If you have a parent type that you also want to add to the dynamic type this is where you do it.

 TypeBuilder typeBuilder = moduleBuilder.DefineType("TYPE_NAME_YOU_WANT", TypeAttributes.Public | 
                              TypeAttributes.Class | TypeAttributes.AutoClass | TypeAttributes.AnsiClass | 
                              TypeAttributes.BeforeFieldInit | TypeAttributes.AutoLayout, 
                              Type.GetType("NAMESPACE.CLASSNAME"));

If we used the parent type this is what it could look like. Up to you really.

 namespace AwesomeProject.PropertyGrid
{    
	public class MyName
	{
		public MyName() { }

		[Browsable(true)]
		[Category(##MYCATEGORY##)]
		[ReadOnly(false)]
		[Description(##MYDESCRIPTION##)]
		public DropDownList MyAwesomeProperty 
		{
			get;
			set;
		}
	}
}

If the property in your parent type is using a custom editor than you need to add it using the following.

 [Editor(typeof(CustomEditor), typeof(UITypeEditor))]

Here is where we need to dynamically add to our type builder. You probably have some sort of call to database which returns your options. So just loop through them and add to the builder.
Create the private field. Field Type can be anything. String, DateTime, Int32, Double. It could even be a type you create which I will explain later.

 FieldBuilder fieldBuilder = typeBuilder.DefineField(##FIELDNAME##, ##FIELDTYPE##, FieldAttributes.Private);

Now we create the public property.

 PropertyBuilder propertyBuilder = typeBuilder.DefineProperty(##FIELDNAME##, System.Reflection.PropertyAttributes.None, ##FIELDTYPE##, new[] { ##FIELDTYPE## });

Next we need to define the required set of property attributes for this property we created.

 MethodAttributes propertyAttributes = MethodAttributes.Public | 
                  MethodAttributes.SpecialName | 
                  MethodAttributes.HideBySig;

This part is really important. Let’s say we created our own type. Like a dropdown list or check box list. We need to define is TypeDescriptor here.

 TypeDescriptor.AddAttributes(typeof(##CLASS##), new EditorAttribute(typeof(##CUSTOMEDITOR##), typeof(System.Drawing.Design.UITypeEditor)));

Next we define the getter.

 MethodBuilder getter = typeBuilder.DefineMethod("get_" + ##PROPERTYNAME##, propertyAttributes, ##FIELDTYPE##, Type.EmptyTypes);

ILGenerator getterIlGen = getter.GetILGenerator();

getterIlGen.Emit(Oppres.Ldarg_0);

getterIlGen.Emit(Oppres.Ldfld, fieldBuilder);

getterIlGen.Emit(Oppres.Ret);

Next we define the setter.

 MethodBuilder setter = typeBuilder.DefineMethod("set_" + ##PROPERTYNAME##, propertyAttributes, null, new Type[] { ##FIELDTYPE## });

ILGenerator setterIlGen = setter.GetILGenerator();

setterIlGen.Emit(Oppres.Ldarg_0);

setterIlGen.Emit(Oppres.Ldarg_1);

setterIlGen.Emit(Oppres.Stfld, fieldBuilder);

setterIlGen.Emit(Oppres.Ret);

Bind the setter and getter to the property builder

 propertyBuilder.SetGetMethod(getter);
propertyBuilder.SetSetMethod(setter);

At this point we can also set the category attribute.

 propertyBuilder.SetCustomAttribute(new CustomAttributeBuilder(
typeof(CategoryAttribute).GetConstructor(
new Type[] { typeof(string) }), new object[] { ##CATEGORYNAME## }));

At this point we can also set the description attribute.

 propertyBuilder.SetCustomAttribute(new CustomAttributeBuilder(
typeof(DescriptionAttribute).GetConstructor(
new Type[] { typeof(string) }), new object[] { ##DESCRIPTION##}));

At this point we can also set the read only attribute.

 propertyBuilder.SetCustomAttribute(new CustomAttributeBuilder(typeof(ReadOnlyAttribute).GetConstructor(new Type[] { typeof(bool) }), new object[] { ##ISREADONLY## }));

Next we need to create and instantiate the dynamic type.

 Type type = typeBuilder.CreateType();
dynamicType = Activator.CreateInstance(type, new object[] { });

Here is where we can set the values for each property we added.

 foreach (PropertyInfo pi in properties)
{
      pi.SetValue(dynamicType, ##THEVALUE##, null);
}
PROPERTY_GRID.SelectedObject = dynamicType;

Side Notes:

If you want to change readonly attributes adding all the fields and before rendering to the property grid you can do that after the Activator.CreateInstance line.

 ReadOnlyAttribute attrib = (ReadOnlyAttribute)TypeDescriptor.GetProperties(dynamicType)["PROPERTY_NAME"].Attributes[typeof(ReadOnlyAttribute)];
FieldInfo isReadOnly = attrib.GetType().GetField("isReadOnly", BindingFlags.NonPublic | BindingFlags.Instance);
isReadOnly.SetValue(attrib, true);


If you want o collapse all categories there is a property on the property grid for that but there isn’t one for selectively only having one category show with the remaining closed. I can’t recall the url where I got some assistance on this so if anyone knows it please link it in the comments.

 GridItem root = pgRequest.SelectedGridItem;

if (root != null)
{
      do
      {
            root = root.Parent;

            if (root == null)
                  break;
      } while (root.GridItemType != GridItemType.Root);

      if (root != null)
      {
            foreach (GridItem category in root.GridItems)
            {
                  //really you can do anything you want here this is just an example.
                  //The important part is the Expanded = false.
                  if (category.Label == "CATEGORY_TO_CLOSE")
                  {
                        category.Expanded = false;
                        break;
                  }
            }
      }
}
Series NavigationPropertyGrid: DropDown Editor >>