Home > Enterprise >  Custom Control deselected in PropertyGrid when setting a Property value using an OpenFileDialog
Custom Control deselected in PropertyGrid when setting a Property value using an OpenFileDialog

Time:11-11

I am making some Custom Controls and UserControls. One of them is derived from Label which exposes an Image property, meant to show an SVG.

My problem is at Design Time, when I set the SVG path in the PropertyGrid. After it gets the SVG file path from an OpenFileDialog, it automatically selects the Form that's under the dialog, so the PropertyGrid switches to the Form's properties.

Any idea why is this happening?

Here is a part of my code...

public partial class LabelSVG : Label
{
    private string svg_image_name     = null;

    [Category("• SVG")]
    [DisplayName("SVGImage •")]
    [Description("...")]
    [DefaultValue(null)]
    [Browsable(true)]
    [Editor(typeof(FileNameEditorSVG), typeof(UITypeEditor))]
    public string SVGImage
    {
        get
        {
            return svg_image_name;
        }
        set
        {
            this.svg_image_name = Path.GetFileName(value); //[ Returns only the name of .svg file and its extension at property field. ]
            this.SVGImage_Method(value); //[ Takes the full path of .svg file and do its stuff. ]
            Invalidate();
        }
    }

    private void SVGImage_Method(string value)
    {
        if (value != null) //[ This value is the full path of .svg file from "SVGImage" property. ]
        {
            if (File.Exists(value))
            {
                this.svg_image_path = value; //[ Stores the full path of .svg file for later use. ]

                XmlDocument xml_document = new XmlDocument { XmlResolver = null };
                xml_document.Load(value); //[ Loads the "XML Document" of .svg file. ]
                this.svg_image_document = xml_document.InnerXml; //[ Stores the "Inner XML" of .svg file for later use. ]

                this.SVGImageRender();
            }
        }
        else
        {
            this.svg_image_path = null;
            this.svg_image_document = null;
            this.Image = null;
        }
    }
}

public class FileNameEditorSVG : FileNameEditor
{
    protected override void InitializeDialog(OpenFileDialog open_file_dialog)
    {
        base.InitializeDialog(open_file_dialog);
        open_file_dialog.Title = "Select an SVG File : ";
        open_file_dialog.Filter = "SVG File (*.svg)|*.svg"; ;
    }
}

CodePudding user response:

After testing this scenario (a different Control becomes active in the Form's Designer when double-clicking a File in an OpenFileDialog), it appears that it's enough to re-select the Control, to restore it as the active Control, so the PropertyGrid can select the Property that is being edited.

Take the instance from the ITypeDescriptorContext object, then call Select() and set Capture = true on that instance (which represents the Control that owns the property that is under editing)

You have to override and rebuild the EditValue method of the UITypeEditor - the class here derives from UITypeEditor directly - to add custom behavior (though you could probably get away with a standard implementation):

public class FileNameEditorSVG : UITypeEditor {

    private OpenFileDialog dialog = null;
    protected virtual void InitializeDialog(OpenFileDialog ofd)
    {
        ofd.Title = "Select an SVG File: ";
        ofd.Filter = "SVG File (*.svg)|*.svg";
    }

    public override UITypeEditorEditStyle GetEditStyle(ITypeDescriptorContext context) => UITypeEditorEditStyle.Modal;

    public override object EditValue(ITypeDescriptorContext context, IServiceProvider provider, object value)
    {
        if (provider is null || provider.GetService(typeof(IWindowsFormsEditorService)) is null) {
            return value;
        }
        var ctl = (Control)context.Instance;
        if (dialog is null) {
            dialog = new OpenFileDialog();
            InitializeDialog(dialog);
        }

        if (value is string s) dialog.FileName = s;
        if (dialog.ShowDialog() == DialogResult.OK) {
            ctl.Select();
            ctl.Capture = true; // PropertyGrid stuff
            return dialog.FileName;
        }
        return value;
    }
}
  • Related