I ran into a bit of a problem yesterday, when trying to implement a control designer (derived from ParentControlDesigner) for a Windows Forms control derived from GroupBox, which should only hold a certain kind of control as children (say CheckBoxes). So I thought I'd just override the designer's OnDragDrop method, check if the dropped control is a CheckBox and I'd be all set, something like:
if (de.Data.GetDataPresent(DataFormats.Serializable) )
{
CheckBox checkbox = de.Data.GetData(DataFormats.Serializable) as CheckBox;
this.Control.AllowDrop = checkbox != null;
}
It *should* be working, right? Wrong! Unfortunately, it seems that the actual IDataObject implementation that is returned when calling de.Data is some internal framework class from which I've found no way to get the actual winforms control that's being dropped (but if you know how this could be done, please drop a line). So, unfortunately, I had to resort to a two step workaround, which is not very nice, but at least seems to do the trick:
- override the designer's CanParent method
- subscribe to the designed control's ControlAdded event and check that the control being added is of a type you want.
The code for these two methods is pretty simple:
public override bool CanParent(Control control)
{
return (control is CheckBox);
}
private void Control_ControlAdded(object sender, ControlEventArgs e)
{
CheckBox checkBox = e.Control as CheckBox;
if (checkBox == null)
{
throw new ArgumentException("This control only supports CheckBox controls as children",
"control");
}
}
Why the need to do both these steps, you might ask. Here's why: the designer's CanParent method is called whenever an
existing control on the design surface gets dragged/dropped into your container, and the result is a "forbidden" icon and the control can't be dropped, if it's not of the right type. However, if a control is dragged/dropped into your container
directly from the toolbox, this method is not called at all. Here's where the ControlAdded handler comes in handy, since it gets called and, by throwing the exception, denies the drop.
So why not use just the ControlAdded event handler and leave CanParent alone? Well, from what I've seen, if you try to drag/drop an existing control onto your container (so not directly from the toolbox, but from somewhere on the form), it doesn't get added to the container (which is fine), but then...it dissapears. The trick here is that when dragging/dropping an existing control on the design surface, the CanParent method gets called first, it denies the drop, and, when the user releases the mouse, the control gets back to its previous place on the design surface.
If this all seems a bit contrived its...well...because it is. I'd really like to think there's a better way to do this, so, if you know of one, please drop a line :)