Additional Infos: PropertyGrid: How to catch an exception?

I use a PropertyGrid to show a business object in my application. The business object throws exceptions if the user tries to enter an invalid string inside the PropertyGrid. The PropertyGrid shows two different behaviors in this case:

  1. If a default TypeConverter is used the PropertyGrid shows an builtin error message dialog.
  2. If a custom TypeConverter is used the PropertyGrid doesn't handle the exception. This happens only in Debug mode.

How can I catch these exceptions and suppress the builtin error message dialog of the PropertyGrid



Answer this question

Additional Infos: PropertyGrid: How to catch an exception?

  • sennekeennes

    I used the IServiceProvider/IUIService method to show a custom error dialog to the user in a PropertyGrid in .NET 2.0. Below you will find a generic example of what I did...

    1. I created MyUIService, which implements IUIService:

    internal class MyUIService : IUIService
    {
    private PropertyGrid propertyGrid;

    internal MyUIService(PropertyGrid grid)
    {
    this.propertyGrid = grid;
    }

    public bool CanShowComponentEditor(object component)
    {
    throw new NotImplementedException("The method or operation is not implemented.");
    }

    public IWin32Window GetDialogOwnerWindow()
    {
    throw new NotImplementedException("The method or operation is not implemented.");
    }

    public void SetUIDirty()
    {
    throw new NotImplementedException("The method or operation is not implemented.");
    }

    public bool ShowComponentEditor(object component, IWin32Window parent)
    {
    throw new NotImplementedException("The method or operation is not implemented.");
    }

    public DialogResult ShowDialog(Form form)
    {
    PropertyDescriptor property = this.propertyGrid.SelectedGridItem.PropertyDescriptor;

    // extract the error details from the GridErrorDlg instance
    FieldInfo detailsTextBoxField = form.GetType().GetField("details", BindingFlags.NonPublic | BindingFlags.Instance);
    TextBox detailsTextBox = detailsTextBoxField.GetValue(form) as TextBox;

    return MessageBox.Show(this.propertyGrid,
    string.Format(CultureInfo.InvariantCulture,
    "{0}{1}{1}{2}",
    detailsTextBox.Text,
    Environment.NewLine,
    "You may select Cancel to use the previous valid value."),
    property.DisplayName,
    MessageBoxButtons.OKCancel,
    MessageBoxIcon.Warning);
    }

    public void ShowError(Exception ex, string message)
    {
    throw new NotImplementedException("The method or operation is not implemented.");
    }

    public void ShowError(Exception ex)
    {
    throw new NotImplementedException("The method or operation is not implemented.");
    }

    public void ShowError(string message)
    {
    throw new NotImplementedException("The method or operation is not implemented.");
    }

    public DialogResult ShowMessage(string message, string caption, MessageBoxButtons buttons)
    {
    throw new NotImplementedException("The method or operation is not implemented.");
    }

    public void ShowMessage(string message, string caption)
    {
    throw new NotImplementedException("The method or operation is not implemented.");
    }

    public void ShowMessage(string message)
    {
    throw new NotImplementedException("The method or operation is not implemented.");
    }

    public bool ShowToolWindow(Guid toolWindow)
    {
    throw new NotImplementedException("The method or operation is not implemented.");
    }

    public IDictionary Styles
    {
    get
    {
    throw new NotImplementedException("The method or operation is not implemented.");
    }
    }
    }

    2. I created MyServiceProvider, which implements IServiceProvider:

    internal class MyServiceProvider : IServiceProvider
    {
    private MyUIService service;

    internal MyServiceProvider(PropertyGrid grid)
    {
    this.service = new MyUIService(grid);
    }

    public object GetService(Type serviceType)
    {
    if (serviceType == typeof(IUIService))
    {
    return this.service;
    }

    return null;
    }
    }

    3. I used reflection to set the IServiceProvider instance on a PropertyGrid:

    foreach (Control c in propertyGrid.Controls)
    {
    if(string.Compare(c.GetType().FullName, "System.Windows.Forms.PropertyGridInternal.PropertyGridView", true, CultureInfo.InvariantCulture) == 0)
    {
    // add a custom service provider to give us control over the property value error dialog shown to the user
    FieldInfo errorDialogField = c.GetType().GetField("serviceProvider", BindingFlags.Instance | BindingFlags.NonPublic);
    errorDialogField.SetValue(c, new MyServiceProvider(propertyGrid));
    }
    }

    I hope that helps you. If not, I'm sure someone else will find it useful.

    -Jason


  • DevDiver

    Here is the updated MyUIService class. I also forgot to mention that ShowDialog(Form) gets called to display the Collection Editor. As a result, I updated the ShowDialog(Form) method to handle those calls.

    internal class MyUIService : IUIService
    {
    private PropertyGrid propertyGrid;
    private IDictionary styles;

    internal MyUIService(PropertyGrid grid)
    {
    this.propertyGrid = grid;
    this.styles = new Hashtable();
    }

    public bool CanShowComponentEditor(object component)
    { // return value doesn't appear to matter
    return false;
    }

    public IWin32Window GetDialogOwnerWindow()
    {
    return this.propertyGrid;
    }

    public void SetUIDirty()
    {
    throw new NotImplementedException();
    }

    public bool ShowComponentEditor(object component, IWin32Window parent)
    {
    throw new NotImplementedException();
    }

    public DialogResult ShowDialog(Form form)
    {
    if (form != null)
    {
    if (string.Compare(form.GetType().FullName, "System.Windows.Forms.PropertyGridInternal.GridErrorDlg", true, CultureInfo.InvariantCulture) == 0)
    { // show custom error message dialog
    DynamicProperty property = this.propertyGrid.SelectedGridItem.PropertyDescriptor as DynamicProperty;

    FieldInfo detailsTextBoxField = form.GetType().GetField("details", BindingFlags.NonPublic | BindingFlags.Instance);
    TextBox detailsTextBox = detailsTextBoxField.GetValue(form) as TextBox;

    return MessageBox.Show(this.propertyGrid,
    string.Format(CultureInfo.InvariantCulture, "{0}{1}{1}{2}", detailsTextBox.Text, Environment.NewLine, "Select Cancel to revert to the previously valid value."),
    property.Name.Trim(),
    MessageBoxButtons.OKCancel,
    MessageBoxIcon.Warning);
    }
    else
    { // show collection editor
    return form.ShowDialog(this.propertyGrid);
    }
    }

    return DialogResult.Cancel;
    }

    public void ShowError(Exception ex, string message)
    {
    throw new NotImplementedException();
    }

    public void ShowError(Exception ex)
    {
    throw new NotImplementedException();
    }

    public void ShowError(string message)
    {
    throw new NotImplementedException();
    }

    public DialogResult ShowMessage(string message, string caption, MessageBoxButtons buttons)
    {
    throw new NotImplementedException();
    }

    public void ShowMessage(string message, string caption)
    {
    throw new NotImplementedException();
    }

    public void ShowMessage(string message)
    {
    throw new NotImplementedException();
    }

    public bool ShowToolWindow(Guid toolWindow)
    {
    throw new NotImplementedException();
    }

    public IDictionary Styles
    {
    get
    {
    return this.styles;
    }
    }
    }


  • Vladimir Chtepa

    Well done Jason, this is a nice hack.
    Will add it to the PropertyGrid Resource List.

    Best regards,

    Nicolas Cadilhac
    Smart PropertyGrid.Net @ VisualHint
    Microsoft PropertyGrid Resource List
    Free PropertyGrid for MFC

  • Penicillin

    I use the PropertyGrid in my own application to show the properties of my business objects. This way my user interface is independent of my business objects.
  • MA2005

    I'm unable to reproduce your problem, so no, I can't tell you why.
  • Labm1ce

    Thanks.

    Please note that this specific implementation will not work with the Collection Editor; however, it can be made to work by modifying the Styles property and the CanShowComponentEditor(object) method. MyUIService will need to return an empty Hashtable (or something else that implements IDictionary) from the Styles property and true or false (it doesn't appear to matter) from the CanShowComponentEditor(object) method.


  • mschelstrate

    you mean you're trying to replace the property grid being used by visual studio for what exactly are these requirements


  • SoniqStylz

    The exception thrown by the PropertyGrid looks like this:

    System.Exception was unhandled by user code
    Message=
    "r is not a valid value for Double."
    Source="System"
    StackTrace:
    at System.ComponentModel.BaseNumberConverter.ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, Object value)
    at System.ComponentModel.TypeConverter.ConvertFromString(ITypeDescriptorContext context, String text)
    at System.Windows.Forms.PropertyGridInternal.GridEntry.ConvertTextToValue(String text)
    at System.Windows.Forms.PropertyGridInternal.PropertyGridView.CommitText(String text)

    The PropertyGridView.CommitText method catch this exception and calls the ShowDialog(Form) method of the IUIService. I tried to set my own IUIService to the PropertyGrid.

    class MyPropertyGrid : PropertyGrid
    {
    protected override object GetService(Type service)
    {
    if (service == typeof(IUIService))
    {
    object obj = new UIServiceTestDesigner();
    return obj;
    }
    return base.GetService(service);
    }
    }

    But this didn't worked. The UIServiceTestDesigner.ShowDialog(Form) was never called by the PropertyGrid.

    How can I use my own custom error handling in combination with the PropertyGrid


  • AlterEgo

     

    Above Code not working when you copy the value and paste in to Property Grid using Clipboard Operations.

    Can you tell me why is it so

    Regards

    Varun Garg


  • Additional Infos: PropertyGrid: How to catch an exception?