Sunil
Sunil

Reputation: 25

Drag and drop multiple instances of user controls in WPF

The given code works fine with dragging and dropping one instance of control. If I try to drop the same instance again it throws an exception:

Specified element is already the logical child of another element. Disconnect it first.

How do I drop multiple instances of user controls on my Canvas, similar to how Visual Studio toolbox does?

public MainWindow()
{
    InitializeComponent();
    LoadUsercontrols();
}

private void LoadUsercontrols()
{
    List<string> userControlKeys = new List<string>();
    userControlKeys.Add("testCtrl1");
    userControlKeys.Add("testCtrl2");

    Type type = this.GetType();
    Assembly assembly = type.Assembly;
    foreach (string userControlKey in userControlKeys)
    {
        userControlFullName = String.Format("{0}.TestControls.{1}", type.Namespace, userControlKey);
        UserControl userControl = new UserControl();
        userControl = (UserControl)assembly.CreateInstance(userControlFullName);
        _userControls.Add(userControlKey, userControl);
    }
}

private void TreeViewItem_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
    startPoint = e.GetPosition(null);
}

private void TreeViewItem_PreviewMouseMove(object sender, MouseEventArgs e)
{
    // Get the current mouse position
    System.Windows.Point mousePos = e.GetPosition(null);
    Vector diff = startPoint - mousePos;

    if (e.LeftButton == MouseButtonState.Pressed &&
     Math.Abs(diff.X) > SystemParameters.MinimumHorizontalDragDistance &&
     Math.Abs(diff.Y) > SystemParameters.MinimumVerticalDragDistance)
    {
        TreeView treeView = sender as TreeView;
        TreeViewItem treeViewItem = FindAnchestor<TreeViewItem>((DependencyObject)e.OriginalSource);

        if (treeViewItem != null)
        {
            Type type = this.GetType();
            Assembly assembly = type.Assembly;
            DataObject dragData = new DataObject("myFormat", _userControls[((System.Windows.Controls.HeaderedItemsControl)(treeViewItem)).Header.ToString()]);
            DragDrop.DoDragDrop(treeViewItem, dragData, DragDropEffects.Copy);
        }
    }
}

private static T FindAnchestor<T>(DependencyObject current) where T : DependencyObject
{
    do
    {
        if (current is T)
        {
            return (T)current;
        }
        current = VisualTreeHelper.GetParent(current);
    }
    while (current != null);
    return null;
}

private void MyDesignerCanvas_DragEnter(object sender, DragEventArgs e)
{
    if (!e.Data.GetDataPresent("myFormat") || sender == e.Source)
    {
        e.Effects = DragDropEffects.None;
    }
}

private void MyDesignerCanvas_Drop(object sender, DragEventArgs e)
{
    if (e.Data.GetDataPresent("myFormat"))
    {
        if (treeItem != null)
        {
            UserControl myCanvasItem = e.Data.GetData("myFormat") as UserControl;
            UserControl newCanvastItem = new UserControl
            {
                Content = _userControls[((System.Windows.Controls.HeaderedItemsControl)(treeItem)).Header.ToString()]
            };
            Point position = e.GetPosition(MyDesignerCanvas);
            DesignerCanvas.SetLeft(newCanvastItem, position.X);
            DesignerCanvas.SetTop(newCanvastItem, position.Y);
            DesignerCanvas.SetZIndex(newCanvastItem, 1);
            MyDesignerCanvas.Children.Add(newCanvastItem);
        }
    }
}

In XAML Code:

<TreeView x:Name="presetTreeView4" Grid.Row="1" >
    <TreeViewItem Header="testCtrl1" Selected="TreeViewItem_Selected" PreviewMouseLeftButtonDown="TreeViewItem_PreviewMouseLeftButtonDown" PreviewMouseMove="TreeViewItem_PreviewMouseMove"/>
    <TreeViewItem Header="testCtrl2" Selected="TreeViewItem_Selected" PreviewMouseLeftButtonDown="TreeViewItem_PreviewMouseLeftButtonDown" PreviewMouseMove="TreeViewItem_PreviewMouseMove"/>
</TreeView>
<s:DesignerCanvas x:Name="MyDesignerCanvas" AllowDrop="True" Drop="MyDesignerCanvas_Drop" DragEnter="MyDesignerCanvas_DragEnter"  Background="#A6B0D2F5" DockPanel.Dock="Bottom" Margin="0" >
</s:DesignerCanvas>

Upvotes: 0

Views: 1106

Answers (2)

Eli Arbel
Eli Arbel

Reputation: 22739

You cannot add the same control to different containers - a control can only appear once in the visual tree.

Instead of loading the user controls in advance, you should construct them at MyDesignerCanvas_Drop (i.e. use Activator the same way you're using it right now in LoadUsercontrols) and assign the resulting control to the UserControl.Content.

Upvotes: 1

Nikolay
Nikolay

Reputation: 3828

I think you have to clone control _userControls[((System.Windows.Controls.HeaderedItemsControl)(treeItem)).Header.ToString()] in MyDesignerCanvas_Drop

Upvotes: 0

Related Questions