Reputation: 5318
I'm trying to create a visual "designer" that will allow users to drag controls from a toolbox onto a design canvas. The excellent tutorial here has helped me get a basic system up and going - I can drag controls from the toolbox, select and resize them etc. Amongst other things,the code uses a modified Canvas
control, overriding the OnDrop
method.
However, I'd like to give the user the option of defining "panels" within the design: effectively smaller Canvas's containing the toolbox controls - as an example:
So when the user drags the button onto Canvas_1, OnDrop
fires and all is good. However, if the user creates Canvas_2, and then drags the button onto Canvas_2 - itself a child control of Canvas_1 - the parent OnDrop
still fires and the button is added to Canvas_1.
I've tried setting the ZIndex of Canvas_2 to greater than Canvas_1, to no avail - the parent Canvas always gets the event. How can I ensure that Canvas_2 gets the OnDrop
events for controls landing on it? Is there a different approach I should be using?
Upvotes: 2
Views: 1854
Reputation: 8843
The event DragDrop.DragEnter
uses a bubbling routing strategy, that means the event will travel upwards in the visual tree starting from the source element, giving a chance for all the elements along the route to handle the event.
When you drop something in the inner Canvas
, it handles it, but doesn't stop the event propagation, so the parent element (the containing Canvas
) will handle it again.
In the overriden OnDrop
method you have a DragEventArgs
parameter.
Set its Handled
property to true
if you want to prevent containing elements to handle the same event.
Example
I created a simple derived Canvas
as well:
public class MyCanvas : Canvas
{
public static readonly DependencyProperty StopDropPropagationProperty = DependencyProperty.Register(
"StopDropPropagation", typeof (bool), typeof (MyCanvas), new PropertyMetadata(default(bool)));
public bool StopDropPropagation
{
get { return (bool) GetValue(StopDropPropagationProperty); }
set { SetValue(StopDropPropagationProperty, value); }
}
protected override void OnDrop(DragEventArgs e)
{
Children.Add(new TextBlock
{
Text = "Dropped here"
});
e.Handled = StopDropPropagation;
}
}
In the view I've put two of these, one nested inside the other.
<local:MyCanvas AllowDrop="True" Background="DarkGray">
<local:MyCanvas Width="300" Height="300" Canvas.Left="100" Canvas.Top="10" Background="BurlyWood"
StopDropPropagation="True"></local:MyCanvas>
</local:MyCanvas>
The thing to note is that I added the new DependencyProperty
called StopDropPropagation
and set it to true
in the inner Canvas
. That will set the Handled
to true.
So when I drop something in the inner Canvas
the TextBlock
will only be added in that element:
Upvotes: 3