How to use controls from built-in UITypeEditors?

924 views Asked by At

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)
    {
        ...
    }
}
1

There are 1 answers

7
Reza Aghaei On BEST ANSWER

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: