I am not completely satisfied with the “decisions” that I will give. They are probably very far from ideal implementations, but ...
The XAML code I created to try to replicate your object as easily but consistently consisted of a group of draggable Rectangles inside the StackPanel control, plus another StackPanel control where the elements could be pulled into.
<Grid> <Grid.Resources> <Style TargetType="Rectangle"> <Setter Property="Width" Value="300"/> <Setter Property="Height" Value="300"/> <Setter Property="CanDrag" Value="True"/> </Style> </Grid.Resources> <Grid.RowDefinitions> <RowDefinition Height="*"/> <RowDefinition Height="*"/> <RowDefinition Height="*"/> </Grid.RowDefinitions> <StackPanel Name="StackPanelRectangles" Grid.Row="0" Orientation="Horizontal"> <Rectangle x:Name="RedRect" Fill="Red" DragStarting="Rectangle_DragStarting"/> <Rectangle x:Name="GreenRect" Fill="Green" DragStarting="Rectangle_DragStarting"/> <Rectangle x:Name="BlueRect" Fill="Blue" DragStarting="Rectangle_DragStarting"/> </StackPanel> <StackPanel Name="StackPanelDropArea" Background="Azure" AllowDrop="True" DragOver="StackPanel_DragOver" Drop="StackPanel_Drop" Grid.Row="2" Orientation="Horizontal" HorizontalAlignment="Center"> <TextBlock>Drop anywhere in this area area</TextBlock> </StackPanel> </Grid>
1st solution:
I dispatched each DragStarting event from several Rectangles to the same EventHandler. In this EventHandler we have access to the UIElement , which is dragged, so with an open property like UIElement in your page class and you can simply clone the necessary properties when you need to drop it, for example
UIElement dragItem; private void Rectangle_DragStarting(UIElement sender, DragStartingEventArgs args) { dataPackage.RequestedOperation = DataPackageOperation.Copy; dragItem = sender; }
Then, when the item is reset, the EventHandler is called, I just add it to my DropArea.
private void StackPanel_Drop(object sender, DragEventArgs e) { Rectangle newElement = new Rectangle(); newElement.Width = (dragItem as Rectangle).Width; newElement.Height = (dragItem as Rectangle).Height; newElement.Fill = (dragItem as Rectangle).Fill; StackPanelDropArea.Children.Add(newElement); }
You cannot add your new control by reference to refer to an object that is being dragged, because there are properties, such as the corresponding parent, that will throw an exception when you try to add the control to another container.
2nd solution: I was extremely focused on using the DataPackage of the object and one of the supported formats by default, but I do not think that any of them can actually store object data, such as our UIElement.
But each instance of DataPackage supports a set of properties that correspond to the dictionary. We can set the dictionary to store the UIElement there, as long as we specify the key to reference the same object later.
private void Rectangle_DragStarting(UIElement sender, DragStartingEventArgs args) { dataPackage.RequestedOperation = DataPackageOperation.Copy; args.Data.Properties.Add("myRectangle", sender); }
In the Drop event handler, you can get a UIElement, for example:
private async void StackPanel_Drop(object sender, DragEventArgs e) { Rectangle element = e.DataView.Properties["myRectangle"] as Rectangle; ...... ...... }
The third solution:
This solution used the SetText(String) method opened by DataPackage to hold the value of the Name property to drag and drop the UIElement.
private void Rectangle_DragStarting(UIElement sender, DragStartingEventArgs args) { dataPackage = new DataPackage(); dataPackage.RequestedOperation = DataPackageOperation.Copy; Rectangle rectangle = sender as Rectangle; dataPackage.SetText(rectangle.Name); Clipboard.SetContent(dataPackage); }
Knowing the value of the Name property of the UIElement object that is being dragged, I looked for it using the VisualTreeHelper class, for example:
private async void StackPanel_Drop(object sender, DragEventArgs e) { DataPackageView dataPackageView = Clipboard.GetContent(); if (dataPackageView.Contains(StandardDataFormats.Text)) { draggedObject = await dataPackageView.GetTextAsync(); } // Dragged objects come from another one of our Parent Children DependencyObject parent = VisualTreeHelper.GetParent(StackPanelDropArea); int count = VisualTreeHelper.GetChildrenCount(parent); for(int i=0; i< count; i++) { DependencyObject child = VisualTreeHelper.GetChild(parent, i); if(child.GetType().Equals(typeof(StackPanel))) { StackPanel currentStackPanel = child as StackPanel; if(currentStackPanel.Name == "StackPanelRectangles") { int numberOfRectangles = VisualTreeHelper.GetChildrenCount(currentStackPanel); for(int j=0; j<numberOfRectangles; j++) { if(VisualTreeHelper.GetChild(currentStackPanel,j).GetType().Equals(typeof(Rectangle))) { Rectangle currentRectangle = VisualTreeHelper.GetChild(currentStackPanel, j) as Rectangle; if (draggedObject != string.Empty && currentRectangle.Name.Equals(draggedObject)) { Rectangle newRectangle = new Rectangle(); newRectangle.Width = currentRectangle.Width; newRectangle.Height = currentRectangle.Height; newRectangle.Fill = currentRectangle.Fill; StackPanelDropArea.Children.Add(newRectangle); } } } } } } */ }
Result:
