DragDrop on nested elements

Jun 20, 2008 at 12:30 PM
Hi,

I want to create a nestable control like this:

I have a user control which is a border with some info in it plus a wrap panel. I want to dynamically nest the same user control by putting it inside the wrap panel by letting the user drop it from an item list on the screen. Then I want to let the user drop other items or other controls to either the root control or the nested control. So the structure could be something like this:

- Root user control
-- Some item 1
-- Some item 2
-- Nested user control 1
-- Nested user control 2
----Some subitem 1
----Some subitem 2
----Next level user control 1

So the question is: If I start out with only the root user control and let the end user drag and drop to build this tree, how do I wire up the DragDrop advisors to the new, nested controls (Nested user control 1 and Nested user control 2 so that they can receive dropped items (Some subitem 1,Some subitem 2 and Next level user control 1)? Would this mean that I need to somehow dynamically set what is returned in the GetTopContainer method? And if this needs for example a constructor parameter, how would I assign this to the user control?

Sorry if some of what I ask is blatantly obvious. I blame it on little WPF experience and advanced UI task...

Thanks in advance,

Morten
Jun 23, 2008 at 7:05 AM
I have made some progress with this problem, but I have a new obstacle. I need to programmatically assign new advisors to a nested user control, because reuse of the advisor on the root doesn't work (drops everything in the root container). How do I correctly assign a new advisor to a programmatically created user control?

Morten
Jun 23, 2008 at 9:03 AM
Edited Jun 23, 2008 at 9:42 AM
I am unable to enable dropping in nested controls such as my scenario above. Is this a limitation in FluidKit's implementation or in the WPF way of doing things?

Here is the code I am testing with (this works, when I add another drop advisor on the outer wrap panel in cell2, only the outer wrap panel gets the items):

<WINDOW title="Drag and Drop testing" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:DragDrop="clr-namespace:FluidKit.Helpers.DragDrop;assembly=FluidKit" xmlns:local="clr-namespace:DragDropSpike" Height="480" Width="640" x:Name="Window" x:Class="DragDropSpike.ApplicationWindow" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

    <WINDOW.RESOURCES>
        <RESOURCEDICTIONARY>
            <local:DefaultDragSourceAdvisor x:Key="sourceAdvisor1"></local:DefaultDragSourceAdvisor>
            <local:DefaultDropTargetAdvisor x:Key="targetAdvisor1"></local:DefaultDropTargetAdvisor>
            <local:DefaultDropTargetAdvisor x:Key="targetAdvisor2"></local:DefaultDropTargetAdvisor>
        </RESOURCEDICTIONARY>
    </WINDOW.RESOURCES>
    
    <GRID x:Name="LayoutRoot">
		<GRID.COLUMNDEFINITIONS>
			<COLUMNDEFINITION Width="0.261*">
			<COLUMNDEFINITION Width="2px">
			<COLUMNDEFINITION Width="0.543*">
			<COLUMNDEFINITION Width="2px">
			<COLUMNDEFINITION Width="0.187*">
		</GRID.COLUMNDEFINITIONS>
		<GRID.ROWDEFINITIONS>
			<ROWDEFINITION Height="0.126*">
			<ROWDEFINITION Height="0.874*">
		</GRID.ROWDEFINITIONS>
		<GRIDSPLITTER RenderTransformOrigin="0.5,0.5" Grid.Row="1" Grid.Column="1" HorizontalAlignment="Stretch">
			<GRIDSPLITTER.RENDERTRANSFORM>
				<TRANSFORMGROUP>
					<SCALETRANSFORM ScaleY="1" ScaleX="1">
					<SKEWTRANSFORM AngleY="0" AngleX="0">
					<ROTATETRANSFORM Angle="0">
					<TRANSLATETRANSFORM Y="0" X="0">
				</TRANSFORMGROUP>
			</GRIDSPLITTER.RENDERTRANSFORM>
		</GRIDSPLITTER>
		<GRIDSPLITTER RenderTransformOrigin="0.5,0.5" Grid.Row="1" Grid.Column="3" HorizontalAlignment="Stretch" Margin="0,0,0,0">
			<GRIDSPLITTER.RENDERTRANSFORM>
				<TRANSFORMGROUP>
					<SCALETRANSFORM ScaleY="1" ScaleX="1">
					<SKEWTRANSFORM AngleY="0" AngleX="0">
					<ROTATETRANSFORM Angle="0">
					<TRANSLATETRANSFORM Y="0" X="0">
				</TRANSFORMGROUP>
			</GRIDSPLITTER.RENDERTRANSFORM>
		</GRIDSPLITTER>
		<WRAPPANEL Grid.Row="1" Grid.Column="2" Margin="8,8,8,8">
			<WRAPPANEL.BACKGROUND>
				<LINEARGRADIENTBRUSH StartPoint="0.5,0" EndPoint="0.5,1">
					<GRADIENTSTOP Offset="0" Color="#FFFCDADA">
					<GRADIENTSTOP Offset="1" Color="#FFCD6A6A">
				</LINEARGRADIENTBRUSH>
			</WRAPPANEL.BACKGROUND>
			<TEXTBOX Height="28" Width="49.2433333333333" Margin="4,4,4,4" TextWrapping="Wrap" Text="Item 2">
			<TEXTBOX Height="28" Width="49.243" Margin="4,4,4,4" TextWrapping="Wrap" Text="Item 1">
			<WRAPPANEL Height="300" Width="320" x:Name="innerPanel" Margin="4,4,4,4" DragDrop:DragDropManager.DropTargetAdvisor="{StaticResource targetAdvisor2}" Opacity="0.2" Background="#FF000000">
				<TEXTBOX Height="19.2766666666667" Width="49.243" Margin="4,4,4,4" TextWrapping="Wrap" Text="Item 2">
				<TEXTBOX Height="19.2766666666667" Width="42.25" Margin="4,4,4,4" TextWrapping="Wrap" Text="Item 1">
			</WRAPPANEL>
		</WRAPPANEL>
		<WRAPPANEL RenderTransformOrigin="0.723,0.805" Grid.Row="1" Margin="8,8,8,8" DragDrop:DragDropManager.DragSourceAdvisor="{StaticResource sourceAdvisor1}">
			<WRAPPANEL.BACKGROUND>
				<RADIALGRADIENTBRUSH>
					<GRADIENTSTOP Offset="0" Color="#FFDEE8E2">
					<GRADIENTSTOP Offset="1" Color="#FF9FB4A7">
				</RADIALGRADIENTBRUSH>
			</WRAPPANEL.BACKGROUND>
			<TEXTBOX Height="19.2766666666667" Width="49.243" Margin="4,4,4,4" TextWrapping="Wrap" Text="Item 2">
			<BUTTON Height="21.2766666666667" Width="60.09" Content="DragItem1">
			<BUTTON Content="DragItem2">
			<BUTTON Content="DragItem3">
		</WRAPPANEL>
	</GRID>
</WINDOW>

Coordinator
Jun 23, 2008 at 2:07 PM
You can use the SetValue() API on your control to do the assignment. Something like:

myControl.SetValue(DragDropManager.DragSourceAdvisor, advisor);


petteroe wrote:
I have made some progress with this problem, but I have a new obstacle. I need to programmatically assign new advisors to a nested user control, because reuse of the advisor on the root doesn't work (drops everything in the root container). How do I correctly assign a new advisor to a programmatically created user control?

Morten


Coordinator
Jun 23, 2008 at 2:11 PM
This could possibly be a problem with FluidKit's implementation since it uses Preview events instead of Bubbled events. Some people have tried doing extra hit-testing on the mouse-position to determine the exact drop location.

petteroe wrote:
I am unable to enable dropping in nested controls such as my scenario above. Is this a limitation in FluidKit's implementation or in the WPF way of doing things?

Here is the code I am testing with (this works, when I add another drop advisor on the outer wrap panel in cell2, only the outer wrap panel gets the items):

<WINDOW title="Drag and Drop testing" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:DragDrop="clr-namespace:FluidKit.Helpers.DragDrop;assembly=FluidKit" xmlns:local="clr-namespace:DragDropSpike" Height="480" Width="640" x:Name="Window" x:Class="DragDropSpike.ApplicationWindow" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

<WINDOW.RESOURCES>
<RESOURCEDICTIONARY>
<local:DefaultDragSourceAdvisor x:Key="sourceAdvisor1"></local:DefaultDragSourceAdvisor>
<local:DefaultDropTargetAdvisor x:Key="targetAdvisor1"></local:DefaultDropTargetAdvisor>
<local:DefaultDropTargetAdvisor x:Key="targetAdvisor2"></local:DefaultDropTargetAdvisor>
</RESOURCEDICTIONARY>
</WINDOW.RESOURCES>

<GRID x:Name="LayoutRoot">
<GRID.COLUMNDEFINITIONS>
<COLUMNDEFINITION Width="0.261*">
<COLUMNDEFINITION Width="2px">
<COLUMNDEFINITION Width="0.543*">
<COLUMNDEFINITION Width="2px">
<COLUMNDEFINITION Width="0.187*">
</GRID.COLUMNDEFINITIONS>
<GRID.ROWDEFINITIONS>
<ROWDEFINITION Height="0.126*">
<ROWDEFINITION Height="0.874*">
</GRID.ROWDEFINITIONS>
<GRIDSPLITTER RenderTransformOrigin="0.5,0.5" Grid.Row="1" Grid.Column="1" HorizontalAlignment="Stretch">
<GRIDSPLITTER.RENDERTRANSFORM>
<TRANSFORMGROUP>
<SCALETRANSFORM ScaleY="1" ScaleX="1">
<SKEWTRANSFORM AngleY="0" AngleX="0">
<ROTATETRANSFORM Angle="0">
<TRANSLATETRANSFORM Y="0" X="0">
</TRANSFORMGROUP>
</GRIDSPLITTER.RENDERTRANSFORM>
</GRIDSPLITTER>
<GRIDSPLITTER RenderTransformOrigin="0.5,0.5" Grid.Row="1" Grid.Column="3" HorizontalAlignment="Stretch" Margin="0,0,0,0">
<GRIDSPLITTER.RENDERTRANSFORM>
<TRANSFORMGROUP>
<SCALETRANSFORM ScaleY="1" ScaleX="1">
<SKEWTRANSFORM AngleY="0" AngleX="0">
<ROTATETRANSFORM Angle="0">
<TRANSLATETRANSFORM Y="0" X="0">
</TRANSFORMGROUP>
</GRIDSPLITTER.RENDERTRANSFORM>
</GRIDSPLITTER>
<WRAPPANEL Grid.Row="1" Grid.Column="2" Margin="8,8,8,8">
<WRAPPANEL.BACKGROUND>
<LINEARGRADIENTBRUSH StartPoint="0.5,0" EndPoint="0.5,1">
<GRADIENTSTOP Offset="0" Color="#FFFCDADA">
<GRADIENTSTOP Offset="1" Color="#FFCD6A6A">
</LINEARGRADIENTBRUSH>
</WRAPPANEL.BACKGROUND>
<TEXTBOX Height="28" Width="49.2433333333333" Margin="4,4,4,4" TextWrapping="Wrap" Text="Item 2">
<TEXTBOX Height="28" Width="49.243" Margin="4,4,4,4" TextWrapping="Wrap" Text="Item 1">
<WRAPPANEL Height="300" Width="320" x:Name="innerPanel" Margin="4,4,4,4" DragDrop:DragDropManager.DropTargetAdvisor="{StaticResource targetAdvisor2}" Opacity="0.2" Background="#FF000000">
<TEXTBOX Height="19.2766666666667" Width="49.243" Margin="4,4,4,4" TextWrapping="Wrap" Text="Item 2">
<TEXTBOX Height="19.2766666666667" Width="42.25" Margin="4,4,4,4" TextWrapping="Wrap" Text="Item 1">
</WRAPPANEL>
</WRAPPANEL>
<WRAPPANEL RenderTransformOrigin="0.723,0.805" Grid.Row="1" Margin="8,8,8,8" DragDrop:DragDropManager.DragSourceAdvisor="{StaticResource sourceAdvisor1}">
<WRAPPANEL.BACKGROUND>
<RADIALGRADIENTBRUSH>
<GRADIENTSTOP Offset="0" Color="#FFDEE8E2">
<GRADIENTSTOP Offset="1" Color="#FF9FB4A7">
</RADIALGRADIENTBRUSH>
</WRAPPANEL.BACKGROUND>
<TEXTBOX Height="19.2766666666667" Width="49.243" Margin="4,4,4,4" TextWrapping="Wrap" Text="Item 2">
<BUTTON Height="21.2766666666667" Width="60.09" Content="DragItem1">
<BUTTON Content="DragItem2">
<BUTTON Content="DragItem3">
</WRAPPANEL>
</GRID>
</WINDOW>




Jun 23, 2008 at 7:17 PM

Hi Pavan,

As a test, I'm able to set the target and source properties on the individual treeview items on each tree.  I pass in the source tree to this method:

 

private static void SetSourceOnTree(DependencyObject TargetParent)

 

{

 

for (var i = 0; i < VisualTreeHelper.GetChildrenCount(TargetParent); i++)

 

{

 

var child = VisualTreeHelper.GetChild(TargetParent, i);

 

 

if (child is TreeViewItem)

 

{

 

var source = new DefaultDragSourceAdvisor();

 

child.SetValue(

DragDropManager.DragSourceAdvisorProperty, source);

 

}

SetSourceOnTree(child);

}

}


Then I pass in the target tree to this method:

 

 

private static void SetTargetOnTree(DependencyObject TargetParent)

 

{

 

for (var i = 0; i < VisualTreeHelper.GetChildrenCount(TargetParent); i++)

 

{

 

var child = VisualTreeHelper.GetChild(TargetParent, i);

 

 

if (child is TreeViewItem)

 

{

 

var source = new DefaultDropTargetAdvisor();

 

child.SetValue(

DragDropManager.DropTargetAdvisorProperty, source);

 

}

SetTargetOnTree(child);

}

}

When I complete a drag I get the following InvalidCast exception:

 

“Unable to cast object of type 'System.Windows.Controls.TreeViewItem' to type 'System.Windows.Controls.Panel'.”

 

…during the do drag drop call in DragDropManager.cs<SUP>[2]</SUP> at line 262:

            var effects = DragDrop.DoDragDrop(_draggedElt, data, supportedEffects);

 

 

Any ideas what I’m doing wrong?

Coordinator
Jun 23, 2008 at 9:21 PM
Hey Roland,
     Would it be possible to send in your solution files ? I think I would have to debug a bit to know what's going on.

Cheers!
Pavan