See the question and my original answer on StackOverflow

You could use a TypeConverter that would restrict the text once it's been entered. That would work only the textbox looses the focus. Let's suppose I have this class I want to edit using the property grid:

public class MyClass
{
    [TypeConverter(typeof(MyTypeConverter))]
    public string MyText { get; set; }
}

Here is a type converter code that does this:

public class MyTypeConverter : TypeConverter
{
    public const int MaxLength = 10;

    public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
    {
        return sourceType == typeof(string);
    }

    public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
    {
        return destinationType == typeof(string);
    }

    public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
    {
        return Truncate(value as string, MaxLength);
    }

    public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
    {
        return Truncate(value as string, MaxLength);
    }

    private static string Truncate(string value, int maxLength)
    {
        if (value == null)
            return null;

        return value.Length <= maxLength ? value : value.Substring(0, maxLength);
    }
}

Otherwise, you can hack the grid, like I demonstrate here. It's not a UITypeEditor because the underlying textbox is shared for all items. The following approach is based on selection events. Again, this is another class I want to edit:

public class MyClass
{
    [Editor(typeof(NoneEditor), typeof(UITypeEditor))]
    public string MyText { get; set; }

    public string MyOtherText { get; set; }
}

Note the MyText property is decorated with a "marker" editor that just does nothing:

public class NoneEditor : UITypeEditor
{
    public override UITypeEditorEditStyle GetEditStyle(ITypeDescriptorContext context)
    {
        return UITypeEditorEditStyle.None;
    }
}

I can just hook the grid, like this:

propertyGrid1.SelectedGridItemChanged += OnPropertyGridSelectedGridItemChanged;

    public static void OnPropertyGridSelectedGridItemChanged(object sender, SelectedGridItemChangedEventArgs e)
    {
        PropertyGrid pg = (PropertyGrid)sender;
        GridItem item = e.NewSelection;

        // yes, a grid item is also an IServiceProvider
        IServiceProvider sp = (IServiceProvider)item;

        // get the property grid view control
        IWindowsFormsEditorService svc = (IWindowsFormsEditorService)sp.GetService(typeof(IWindowsFormsEditorService));

        // WARNING: hack time! this uses private members, so use at your own risks...
        TextBox edit = (TextBox)svc.GetType().GetProperty("Edit", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(svc, null);

        // is this our funky stuff?
        if (item.PropertyDescriptor.GetEditor(typeof(UITypeEditor)) is NoneEditor)
        {
            edit.MaxLength = 10;
            edit.BackColor = Color.Blue;
        }
        else // don't forget to reset the edit box here
        {
            edit.MaxLength = int.MaxValue;
            edit.BackColor = Color.White;
        }
    }