views:

190

answers:

2

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?
+1  A: 

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.

Rodney Richardson
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
You should be able to call this.GetItems() from MyCollectionEditor (although I've not tried this myself).
Rodney Richardson
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
Try collectionForm.EditValue instead. This should contain your collection property.
Rodney Richardson
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
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
Perhaps hooking into events, and enabling/disabling the OK button would be better - see modified code above.
Rodney Richardson
A: 

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

LMS