See the question and my original answer on StackOverflow

Here is the code of a custom property tab:

public class CustomTab : PropertyTab
{
    private Bitmap _bitmap;

    // the tooltip displayed in the grid
    public override string TabName
    {
        get
        {
            return "CustomTab";
        }
    }

    // the icon displayed in the grid
    public override Bitmap Bitmap
    {
        get
        {
            if (_bitmap == null)
            {
                // 1. create a file named "CustomTab.bmp". It must be a 16x16, 8-bit bitmap. Transparency pixel is magenta.
                // 2. place it in the project aside this .cs
                // 3. configure its build action to "Embedded Resource"
                _bitmap = new Bitmap(GetType(), GetType().Name + ".bmp");
            }
            return _bitmap;
        }
    }

    // TODO: return the descriptor corresponding to the properties you want to show
    public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext context, object component, Attribute[] attributes)
    {
        return new PropertyDescriptorCollection(null);
    }

    public override PropertyDescriptorCollection GetProperties(object component, Attribute[] attributes)
    {
        return GetProperties(null, component, attributes);
    }
}

And here is how you can add it to the property grid:

CustomTab tab = new CustomTab();
grid.PropertyTabs.AddTabType(typeof(CustomTab), PropertyTabScope.Global);

It will display nothing because I don't return any PropertyDescriptor in the GetProperties overload, but this is where you need to defined what descriptor you want to use.

OP Edit:

It appears that the crux of making property tabs work, clearing up the confusion of the information available on the web:

  • The displayed object need not inherit anything like Control or Component

  • The PropertyGrid.Site property need not be set

  • The property grid need not be extended with a custom class

  • The property grid need not even explicitly add the tab if it is called out in the SelectedObject by an attribute. Of course a persistent tab would need to be added.

  • Ensure all three PropertiesTab.GetProperties methods are overridden and point to the required method and not the base method.

  • The tab's bitmap override must actually do something!