Search code examples
c#winformswindows-forms-designeruitypeeditor

How to use controls from built-in UITypeEditors?


I want to use "Select resource" dialog in my dropdown UITypeEditor for custom struct property. I already have TestEditorControl:UserControl, which contains a button1 with event handler for Click:

btn.Click+=(s,a)=>{
    OpenFileDialog oDlg = new OpenFileDialog();
    if (oDlg.ShowDialog() == DialogResult.OK)
    {
        ...
    }
}

How to replace "OpenFileDialog" with "Select resource" dialog? I tried this code (based on Visual Studio "Select Resource" dialog replacement):

private Form resDialog;
public TestEditorControl()
{
    InitializeComponent();
    var property = TypeDescriptor.GetProperties(button1)["Image"];
    var resourceEditorSwitch = property.GetEditor(typeof(UITypeEditor)) as UITypeEditor;
    var editorToUseField = resourceEditorSwitch.GetType().GetProperty("EditorToUse",
        System.Reflection.BindingFlags.Instance |
        System.Reflection.BindingFlags.NonPublic);
    var editorToUse = editorToUseField.GetValue(resourceEditorSwitch);

    //System.NullReferenceException     (editorToUseField == null)

    var resourcePickerUIField = editorToUse.GetType().GetField("resourcePickerUI",
        System.Reflection.BindingFlags.Instance |
        System.Reflection.BindingFlags.NonPublic);
    var resDialog= (Form)Activator.CreateInstance(resourcePickerUIField.FieldType);
}

btn.Click+=(s,a)=>{
    if (resDialog.ShowDialog() == DialogResult.OK)
    {
        ...
    }
}

Solution

  • To show editor of a property, you need to get the UITypeEditor of the property and call its EditValue:

    var editor = (UITypeEditor)propertyDescriptor.GetEditor(typeof(UITypeEditor));
    var editedValue = editor.EditValue(context, provider, propertyValue);
    

    The values you need to pass to the method, depend to the context of the code, in addition to the example in this post, I've shared a few other links at bottom of this post.

    Example - Show Select Image dialog for a nested property

    In this example I have created a MyTestControl, which has a property called MyTestProperty which is of MyTestClass type, which has a MyTestImage property of Image type. I'm going to show a UITypeEditor for MyTestProperty, and a button inside the editor which shows select image dialog and changes the image property:

    enter image description here

    The Control

    using System;
    using System.ComponentModel;
    using System.ComponentModel.Design;
    using System.Drawing;
    using System.Drawing.Design;
    using System.Windows.Forms;
    using System.Windows.Forms.Design;
    public class MyTestControl : Control
    {
        [Editor(typeof(MyTestClassEditor), typeof(UITypeEditor))]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
        public MyTestClass MyTestProperty { get; set; } = new MyTestClass();
    
        protected override void OnPaint(PaintEventArgs e)
        {
            base.OnPaint(e);
            if (MyTestProperty != null &&
                MyTestProperty.MyTestImageProperty != null)
                e.Graphics.DrawImage(MyTestProperty.MyTestImageProperty,
                    ClientRectangle.Location);
        }
    }
    public class MyTestClass
    {
        public Image MyTestImageProperty { get; set; }
    }
    

    UITypeEditor

    public class MyTestClassEditor : UITypeEditor
    {
        public override UITypeEditorEditStyle GetEditStyle(
            ITypeDescriptorContext context)
        {
            return UITypeEditorEditStyle.DropDown;
        }
        public override object EditValue(ITypeDescriptorContext context, 
            IServiceProvider provider, object value)
        {
            var svc = (IWindowsFormsEditorService)provider
                .GetService(typeof(IWindowsFormsEditorService));
            var propertyToEdit = TypeDescriptor.GetProperties(value)
                [nameof(MyTestClass.MyTestImageProperty)];
            var ctx = new TypeDescriptionContext(
                (Control)context.Instance, provider, value, propertyToEdit);
            var editorControl = new MyTestClassEditorControl(svc, ctx, provider);
            svc.DropDownControl(editorControl);
            return editorControl.Result;
        }
    }
    

    Editor control

    public class MyTestClassEditorControl : Control
    {
        public Object Result { get; private set; }
        public MyTestClassEditorControl(IWindowsFormsEditorService service,
            ITypeDescriptorContext context, IServiceProvider provider) 
        {
            Result = context.Instance;
            var button = new Button() { Text = "Choose Image",  AutoSize = true };
            button.Click += (sender, e) =>
            {
                try
                {
                    var imageProp = context.PropertyDescriptor;
                    var imageValue = imageProp.GetValue(context.Instance);
                    var editor = (UITypeEditor)imageProp
                        .GetEditor(typeof(UITypeEditor));
                    var selectedImage = editor.EditValue(
                        context, provider, imageValue);
                    imageProp.SetValue(context.Instance, selectedImage);
                    context.OnComponentChanging();
                    context.OnComponentChanged();
                    service.CloseDropDown();
                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.ToString());
                }
            };
            this.Controls.Add(button);
            this.Height = 100;
        }
    }
    

    TypeDescriptionContext

    public class TypeDescriptionContext : ITypeDescriptorContext
    {
        Control control;
        IServiceProvider provider;
        object instannce;
        PropertyDescriptor property;
        public TypeDescriptionContext(Control control,
            IServiceProvider provider, object instannce,
            PropertyDescriptor property)
        {
            this.control = control;
            this.provider = provider;
            this.instannce = instannce;
            this.property = property;
        }
        public IContainer Container => control.Site?.Container;
        public object Instance => instannce;
        public void OnComponentChanged() =>
            GetService<IComponentChangeService>()
            ?.OnComponentChanged(control, null, null, null);
        public bool OnComponentChanging() => true;
        public PropertyDescriptor PropertyDescriptor => property;
        public object GetService(Type type) => provider?.GetService(type);
        public T GetService<T>() => (T)this.GetService(typeof(T));
    }
    

    More example

    You may want to look into following posts for more example: