Search code examples
c#.netwinformspropertygrid

Make a property only editable through the bound Editor(component)


I have a PropertyGrid with some class-properties bound to it. Every property has an EditorAttribute where i defined a custom class to make changes. My wish is to make the string-property only editable through this Editor-class and not by editing through the PropertyGrid-textfield.

I tried changing its ReadOnly-attribute to true and then changing this value inside my editor-class before resetting it inside the properties setter-method with Reflection, but thats not working properly as the textfield stays in focus mode and I can still make changes. Furthermore for my this is more like a workaround, than an acceptable solution.

Is there a way to access the setter of my property only by the EditorComponent-class and not by the PropertyGrid?

My custom Editor-class:

public override object EditValue(ITypeDescriptorContext context, IServiceProvider provider, object value)
{
   using (EditorForm f = new Forms.EditorForm(value.ToString()))
   {
      if (f.ShowDialog() == System.Windows.Forms.DialogResult.OK)
      {
         PropertyDescriptor descriptor = context.PropertyDescriptor;
         if (descriptor != null)
         {
            ReadOnlyAttribute attribute = descriptor.Attributes[typeof(ReadOnlyAttribute)] as ReadOnlyAttribute;
            if (attribute != null)
            {
               System.Reflection.FieldInfo fieldToChange = attribute.GetType().GetField("isReadOnly", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
               if (fieldToChange != null)
               {
                  fieldToChange.SetValue(attribute, false); // setting it to false
                  value = f.Text;
               }
            }
         }
      }
   }
   return value;
}

and after this I rechange it in my setter-method:

private string _myText = String.Empty;
[Editor(typeof(...), typeof(UITypeEditor)),
ReadOnly(true)]
public string MyText
{
   get { return _myText; }
   set
   {
      _myText = value;
      PropertyDescriptor descriptor = TypeDescriptor.GetProperties(this)["MyText"];
      if (descriptor != null)
      {
         ReadOnlyAttribute attribute = descriptor.Attributes[typeof(ReadOnlyAttribute)] as ReadOnlyAttribute;
         if (attribute != null)
         {
            System.Reflection.FieldInfo fieldToChange = attribute.GetType().GetField("isReadOnly", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
             if (fieldToChange != null)
                fieldToChange.SetValue(attribute, true); // resetting it to true
         }
      }
    }
}

Solution

  • You can add additional TypeConverter which will prevent editing (by just discarding any conversions from string to destinatino type (even destination is also a string)).

    After this you can remove all wired stuff with editing ReadOnlyAttribute in rutime.

    public class TestClass
    {
        [Editor(typeof(CustomEditor), typeof(UITypeEditor))]
        [TypeConverter(typeof(ReadOnlyConverter))]
        public string MyText { get; set; }
    }
    
    public class ReadOnlyConverter : TypeConverter
    {
        //just empty class
    }
    
    public class CustomEditor : UITypeEditor
    {
        public override UITypeEditorEditStyle GetEditStyle(ITypeDescriptorContext context) 
               => UITypeEditorEditStyle.Modal;
    
        public override object EditValue(ITypeDescriptorContext context, IServiceProvider provider, object value)
        {
            var strVal = value as string;
            var svc = provider.GetService(typeof(IWindowsFormsEditorService)) as IWindowsFormsEditorService;
    
            using (var editorForm = new EditorForm())
            {
                editorForm.Value = strVal;
    
                svc.ShowDialog(editorForm);
                value = editorForm.Value;
            };
            
    
            return value;
        }
    }
    

    Additionally, you may want to add edditional check in EditValue to be sure service is available, input value is a real string and so on.

    You can also override members of ReadOnlyConverter to explicitly disable string conversion and not rely to default implementation.