3

I'm using PropertyGrid to edit an object containing a collection. Collection is edited using the CollectionEditor. I have to make sure elements in collection are unique.

How can I add validation to CollectionEditor:

  1. By either overloading CollectionEditor's OnFormClosing
  2. Or adding validation for creating/editing items?
JBeurer
  • 1,707
  • 3
  • 19
  • 38

2 Answers2

4

You can create your own collection editor, and hook into events on the default editor's controls. You can use these events to, say, disable the OK button. Something like:

public class MyCollectionEditor : CollectionEditor
{
    private static Dictionary<CollectionForm, Button> okayButtons 
        = new Dictionary<CollectionForm, Button>();

    // Inherit the default constructor from CollectionEditor
    public MyCollectionEditor(Type type) 
        : base(type) 
    {
    }

    // Override this method in order to access the containing user controls
    // from the default Collection Editor form or to add new ones...
    protected override CollectionForm CreateCollectionForm()
    {
        CollectionForm collectionForm = base.CreateCollectionForm();
        collectionForm.FormClosed += 
            new FormClosedEventHandler(collectionForm_FormClosed);
        collectionForm.Load += new EventHandler(collectionForm_Load);

        if (collectionForm.Controls.Count > 0)
        {
            TableLayoutPanel mainPanel = collectionForm.Controls[0] 
                as TableLayoutPanel;
            if ((mainPanel != null) && (mainPanel.Controls.Count > 7))
            {
                // Get a reference to the inner PropertyGrid and hook 
                // an event handler to it.
                PropertyGrid propertyGrid = mainPanel.Controls[5] 
                    as PropertyGrid;
                if (propertyGrid != null)
                {
                    propertyGrid.PropertyValueChanged += 
                        new PropertyValueChangedEventHandler(
                            propertyGrid_PropertyValueChanged);
                }

                // Also hook to the Add/Remove
                TableLayoutPanel buttonPanel = mainPanel.Controls[1] 
                    as TableLayoutPanel;
                if ((buttonPanel != null) && (buttonPanel.Controls.Count > 1))
                {
                    Button addButton = buttonPanel.Controls[0] as Button;
                    if (addButton != null)
                    {
                        addButton.Click += new EventHandler(addButton_Click);
                    }
                    Button removeButton = buttonPanel.Controls[1] as Button;
                    if (removeButton != null)
                    {
                        removeButton.Click += 
                            new EventHandler(removeButton_Click);
                    }
                }

                // Find the OK button, and hold onto it.
                buttonPanel = mainPanel.Controls[6] as TableLayoutPanel;
                if ((buttonPanel != null) && (buttonPanel.Controls.Count > 1))
                {
                    Button okayButton = buttonPanel.Controls[0] as Button;
                    if (okayButton != null)
                    {
                        okayButtons[collectionForm] = okayButton;
                    }
                }
            }
        }
        return collectionForm;
    }

    private static void collectionForm_FormClosed(object sender, 
        FormClosedEventArgs e)
    {
        CollectionForm collectionForm = (CollectionForm)sender;
        if (okayButtons.ContainsKey(collectionForm))
        {
            okayButtons.Remove(collectionForm);
        }
    }

    private static void collectionForm_Load(object sender, EventArgs e)
    {
        ValidateEditValue((CollectionForm)sender);
    }

    private static void propertyGrid_PropertyValueChanged(object sender,
        PropertyValueChangedEventArgs e)
    {
        ValidateEditValue((CollectionForm)sender);
    }

    private static void addButton_Click(object sender, EventArgs e)
    {
        Button addButton = (Button)sender;
        ValidateEditValue((CollectionForm)addButton.Parent.Parent.Parent);
    }

    private static void removeButton_Click(object sender, EventArgs e)
    {
        Button removeButton = (Button)sender;
        ValidateEditValue((CollectionForm)removeButton.Parent.Parent.Parent);
    }

    private static void ValidateEditValue(CollectionForm collectionForm)
    {
        if (okayButtons.ContainsKey(collectionForm))
        {
            Button okayButton = okayButtons[collectionForm];
            IList<MyClass> items = collectionForm.EditValue as IList<MyClass>;
            okayButton.Enabled = MyCollectionIsValid(items);
        }
    }

    private static bool MyCollectionIsValid(IList<MyClass> items)
    {
        // Perform validation here.
        return (items.Count == 2);
    }

}

You will also need to add an Editor attribute to you collection:

class MyClass
{
  [Editor(typeof(MyCollectionEditor), 
          typeof(System.Drawing.Design.UITypeEditor))]
  List<Foo> MyCollection
  {
    get; set;
  }
}

NOTE: I found that the value of items in removeButton_Click was not correct - so some tweaking may need to take place.

  • How can i access items of collection being edited in function collectionForm_FormClosing? collectionForm.Items are protected :( I need to check if a name property of items is unique. – JBeurer Aug 18 '10 at 12:57
  • You should be able to call this.GetItems() from MyCollectionEditor (although I've not tried this myself). – Rodney Richardson Aug 19 '10 at 15:24
  • There's indeed a GetItems() function in CollectionEditor, however it has one argument, which doesn't make much sense to me. protected virtual Object[] GetItems( Object editValue ) According to MSDN: editValue - The collection to edit. Very verbose documentation. And where to get access to collection is the problem in the first place. :( – JBeurer Aug 20 '10 at 19:27
  • Try collectionForm.EditValue instead. This should contain your collection property. – Rodney Richardson Aug 23 '10 at 11:00
  • Indeed, EditValue contains the collection. But few other issues pop-up.. After setting e.Cancel = true, the collection item list is displayed empty. And pressing OK button again results in DialogResult.Cancel. There's something very wrong about this validation approach. :( – JBeurer Aug 23 '10 at 23:23
  • I think you're right. I don't think the base CollectionForm is expecting anyone to cancel the close. I haven't used this approach to validate, just to hook into change notifications to know if the entities in my collection had been edited/added. Is there an option to create an entirely new CollectionForm, rather than just try to hook into the existing one? – Rodney Richardson Aug 26 '10 at 15:59
  • Perhaps hooking into events, and enabling/disabling the OK button would be better - see modified code above. – Rodney Richardson Aug 26 '10 at 16:32
0

Try collectionForm.Context.Instance and typecast it to your data type this should do the trick.

LMS
  • 1