Search code examples
c#event-handlingpropertygrid

Click events from PropertyGrid (e.g. open file/folder dialog)


I am using a WinForms PropertyGrid to display various configuration settings for a program. The PropertyGrid is bound to an XML document which has been Xmlserializer.Deserialize-ed. This allows a user to type in new values, which then get serialized back into the config.xml file. In some cases, these properties are just numbers, and typing in values makes sense. However, in other cases, the values are file names or directory paths, so it makes much more sense to have these entered through an OpenFileDialog or FolderBrowserDialog.

What I'd like to have happen is that the if user clicks on a folder or filename cell in the PropertyGrid, the UI will open the appropriate dialog, get a result, and enter that result into the grid, replacing the existing value. The trouble is, PropertyGrid doesn't seem to allow access to the controls inside it, so I can't respond to an OnClicked event.

Here's how I would like the code to work (EDIT: updated code):

private void propertyGrid_config_Click(object sender, EventArgs e)
{
    PropertyGrid grid = (PropertyGrid)sender;
    PropertyDescriptor selectedItem = grid.SelectedGridItem.PropertyDescriptor;
    if (selectedItem.Category == "Files & Folders")
    {
        if (selectedItem.DisplayName.Contains("directory"))
        {
            FolderBrowserDialog folder = new FolderBrowserDialog();
            if (folder.ShowDialog() == DialogResult.OK)
            {
                selectedItem.SetValue(grid.SelectedObject, folder.SelectedPath);
                grid.Refresh();
            }
        }
        else if (selectedItem.DisplayName.Contains("file"))
        {
            OpenFileDialog file = new OpenFileDialog();
            if (file.ShowDialog() == DialogResult.OK)
            {
                selectedItem.SetValue(grid.SelectedObject, file.FileName);
                grid.Refresh();
            }
        }
    }
}

I've set the grid's "Clicked" event to this handler, but obviously that doesn't work since that only handles the container and not what's in it. (Note this handler works fine if I base it on the "PropertyChanged" event, but that's obviously not what I'm looking for.)

Is there some way to get access to the components and create the events I want? How would you conquer this issue?


In case it's relevant, here's some of the code for the PropertyGrid:

The grid exists in a class called "Configuration" which defines all the properties like this:

[Description("Folder for storing Bonding Key logs")]
[Category("Files & Folders")]
[DisplayName("Log output directory")]
public string dirLogOutput { get; set; }

The XML file will have a corresponding entry for each Property like this:

<dirLogOutput>C:\Users\AHoffman\Desktop\TestData</dirLogOutput>

The Serializer does a good job of matching data from the XML file to the grid, and vice-versa:

public Configuration TryLoadConfiguration(Configuration thisConfig)
{
    string filename = GetConfigFilename();
    try
    {
        if (!File.Exists(filename))
        {
            thisConfig.setDefaults();
        }
        else
        {
            using (var stream = File.Open(filename, FileMode.Open, FileAccess.Read))
            {
                var serializer = new XmlSerializer(typeof(Configuration));
                thisConfig = (Configuration)serializer.Deserialize(stream);
            }
        }
    }
    catch (Exception ex)
    {
        MessageBox.Show("Failed to load configuration file during startup: " + ex.Message);
        thisConfig.setDefaults();
    }
    return thisConfig;
}

private void SaveConfiguration(string filename, Configuration thisConfig)
{
    try
    {
        using (var stream = File.Open(filename, FileMode.Create, FileAccess.Write))
        {
            var serializer = new XmlSerializer(typeof(Configuration));
            serializer.Serialize(stream, thisConfig);
        }
    }
    catch (Exception ex)
    {
        MessageBox.Show("Failed to save configuration file: " + ex.Message);
    }
}

I note that a question like this has been asked before here but with no answers. Hopefully I'm giving you enough info to get something back.


Solution

  • OK never mind. I found answers to my question here (for files) and here (for folders).

    It all relies in using a custom UITypeEditor for each type.

    [EditorAttribute(typeof(OpenFileNameEditor), typeof(System.Drawing.Design.UITypeEditor))]
    
    [EditorAttribute(typeof(FolderNameEditor2), typeof(System.Drawing.Design.UITypeEditor))]
    

    Thanks greatly to @Simon Mourier, @Stewy, and @tzup for their answers.