KenD
KenD

Reputation: 5318

Drag/drop controls onto child Canvas without parent Canvas being notified

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:

enter image description here

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

Answers (1)

Szabolcs Dézsi
Szabolcs Dézsi

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:

drop demo

Upvotes: 3

Related Questions