Drag Drop Crashed if Dragging component with x:Name set

May 5, 2009 at 4:54 PM
Edited May 6, 2009 at 2:04 PM
First of all let me congratulate you on the development of this toolkit. It's by far the best free silverlight drag drop manager around (i haven't tried any of  the commercial ones).

I'm building a solution where a user builds a form with user controls. I have a list of user controls that contain a simple drag source or a drag source containing drop target(s) .
When Testing started some drops were happening without any trouble, others just crashed the whole application.
After some testing I found the problem. It seems that if I try to drag a user control, that contains elements with the x:Name Property set, it crashes.

Let me try to explain.
First create a user control that has a drag source containing a drop target:

<Grid x:Name="LayoutRoot">
        <ddm:DragSource Width="200" Height="50">
            <ddm:DropTarget x:Name="asd" Width="200" Height="50"/>
        </ddm:DragSource>
</Grid>

Then in the main page add two or more of these controls and a drop target.

Start the project, drag one of the controls in the drop target and when trying to get the second the project crashes.

If the name is not set it works fine.

Is there a workaround for this problem?

Thanks,
Milagaia

Coordinator
May 7, 2009 at 4:05 PM
Hello Milagaia,

thanks for the compliment! :-)  To be honest, I never ever used the DDM like this, by naming my targets.  If I need a reference, I either add the targets in codebehind, or I add a loaded-handler so I can catch & save the sender (which is the droptarget), and thus have the reference.  Both of these workarounds should work.

However, I will look into this - even with naming, it should continue working, if not, it's a bug.  

One question though, unrelated probably, but it just looks weird :-) for which scenario do have a droptarget on top of a dragsource?  

Greets, 
Kevin.
May 7, 2009 at 5:03 PM
Hello Kevin,

I am building a form builder where i have a number of controls that the user drags and drops into the form. Some of these controls are stackpanels, uniformgrid, expanders, other simpler like a label with a droptarget and another label. I want to open the form (which has a dropTarget) and start dragging onto it controls that have their own droptagets. I modified your toolkit a bit to enable the droptarget to be dropped onto the top drop target but when I started dragging user controls that had named items within them it crashed. I want to build a whole library of controls that have as root a dragsource and have drop targets inside.

I'll be waiting for the next release of this project.

Once again, great work
Milagaia
Coordinator
May 16, 2009 at 6:55 PM

Hello,

 

I've just committed a new version to source control which may address you issue - you can get it via the "source code" tab.

 

Quite a few internal changes have been made, most notably one "breaking change": ShowGhost (bool) has bene replaced by GhostVisibility (Visibility).  Next to that, I've added an "AllowAnyDropTarget"-property, which will override any list of DropTargets to make sure a DragSource can be dropped anywhere.  Another change is some additional functionality: DragSource now has a DragHandleMode-property, defining how you can drag your dragsource around: by the handle, or just by clicking anywhere on the source.  Another one: I've added a DropMode-property, which determines the behaviour when a dragsource is dropped, and the drop is valid: by default, the dragsource is dropped on the droptarget.  If you set DropMode to "ReturnDragSource", the dragsource is returned to where it originated from.  This mode can be useful if you just want to execute some code on dropping (dragsourcedropped event on droptarget is triggered and can be handled if you add a handler to it), but you do not want the dragsource to be removed from the originating collection.

I also checked if I could reproduce the problem with naming the targets, but this didn't same to pose any problems - do you have a small example project so I can reproduce (and solve, hopefully :-)) your problem?

May 18, 2009 at 10:46 AM
Edited May 18, 2009 at 10:52 AM

Hello,

I've downloaded the new source but the naming problem still persists.

I've created a new silverlight Application project. In it added the following user control (with a named control):

<UserControl x:Class="TestDragDropName.NamedDropSource"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:ddm="clr-namespace:SL_Drag_Drop_BaseClasses;assembly=SL_Drag_Drop_BaseClasses">
    <Grid x:Name="LayoutRoot" Background="Red">
        <ddm:DragSource HorizontalAlignment="Stretch" VerticalAlignment="Stretch" AllDropTargetsValid="True" DragHandleMode="FullDragSource" DropMode="DropDragSource">
            <ddm:DropTarget x:Name="asd" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Background="Red" Width="100" Height="50"/>
        </ddm:DragSource>
    </Grid>
</UserControl>

Added the Containing Layout Property to the page.cs:

SL_Drag_Drop_BaseClasses.InitialValues.ContainingLayoutPanel = this.LayoutRoot;

The Page.xaml looks like this:

<UserControl x:Class="TestDragDropName.Page"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:ddm="clr-namespace:SL_Drag_Drop_BaseClasses;assembly=SL_Drag_Drop_BaseClasses"
    xmlns:local="clr-namespace:TestDragDropName"
    Width="400" Height="300">
    <Grid x:Name="LayoutRoot" >
        <StackPanel>
            <local:NamedDropSource/>
            <local:NamedDropSource/>
            <local:NamedDropSource/>
            <local:NamedDropSource/>
            <local:NamedDropSource/>
            <local:NamedDropSource/>
        </StackPanel>
    </Grid>
</UserControl>

By dragging a NamedDropSource into another the program crashes.

 

A feature i needed when working with the latest release was the ability do drop a dragsource onto the top drop target (if you drop one of the NamedDropSources into another, the next NamedDropTarget to be dropped should be dropped in the top drop target) so i changed some code in the DragSource.GetCorrectDropTarget().

Considering that the drop target stretches when a bigger dragsource is dropped onto it the top drop target is the smallest, although this is not the best solution it worked in my particular case:

private DropTarget GetCorrectDropTarget()
        {
            // get my absolute position
            Point offsetMine = this.MainDraggableControl.TransformToVisual(Application.Current.RootVisual as UIElement).Transform(new Point(0, 0));

            IEnumerable<DropTarget> dTargets = GetDropTargetsRecursively(Application.Current.RootVisual);
            List<DropTarget> possibleTargets = new List<DropTarget>();
            foreach (var item in dTargets)//DropTargets)
            {
                // get the absolute position of this droptarget
                Point offsetDrop = item.TransformToVisual(Application.Current.RootVisual as UIElement).Transform(new Point(0, 0));

                // check its bounds against my absolute position
                if (offsetMine.X > offsetDrop.X && offsetMine.X < offsetDrop.X + item.ActualWidth)
                {
                    // X-coordinates are ok
                    if (offsetMine.Y > offsetDrop.Y && offsetMine.Y < offsetDrop.Y + item.ActualHeight)
                    {
                        possibleTargets.Add(item);
                    }
                }
            }

            // if inside only one target return that one
            if (possibleTargets.Count == 1)
            {
                return possibleTargets[0];
            }

            // if inside multiple targets check which is the smallest target, because all targets are inside one another.
            else if (possibleTargets.Count > 1)
            {
                double tempArea = -1;
                double selectedTargetArea = double.MaxValue;
                DropTarget selectedTarget = null;
                foreach (DropTarget item in possibleTargets)
                {
                    tempArea = item.ActualHeight * item.ActualWidth;
                    if (tempArea < selectedTargetArea)
                        selectedTarget = item;                  
                }
                return selectedTarget;
            }
            return null;
        }

Hope this helps.

Thanks again

milagaia