Disconnecting an element from any/unspecified parent container in WPF
Asked Answered
K

4

24

I have a control that is a child of another control (as all non-root controls/elemts are in WPF). If I want to move the control to another container I have to disconnect it from its current container first (otherwise an exception is thrown).

If I know what the parent is then I can just remove it from its Children collection, or Content or whatever. But what if I don't know what the parent container's type is - how do I remove the child control then?

In the code sample below: How would I be able to move "sp1" to another container without knowing the type of the parent (Panel, GroupBox...)?

// Add the child object "sp1" to a container (of any type).
StackPanel sp1 = new StackPanel();
SomeParentControl.Children.Add(sp1);

// Somewhere else in the code. I still have a reference to "sp1" but now I don't know what container it is in. I just want to move the "sp1" object to another parent container.
AnotherParentControl.Content = sp1; // Generates exception: "Must disconnect specified child from current parent Visual before attaching to new parent Visual."

Ideally I would just like to write something like:

sp1.Parent.RemoveChild(sp1);

But I haven't found anything like that.

Klepac answered 11/10, 2013 at 11:29 Comment(0)
W
27

You may write a helper class with an extension method:

public static class RemoveChildHelper
{
    public static void RemoveChild(this DependencyObject parent, UIElement child)
    {
        var panel = parent as Panel;
        if (panel != null)
        {
            panel.Children.Remove(child);
            return;
        }

        var decorator = parent as Decorator;
        if (decorator != null)
        {
            if (decorator.Child == child)
            {
                decorator.Child = null;
            }
            return;
        }

        var contentPresenter = parent as ContentPresenter;
        if (contentPresenter != null)
        {
            if (contentPresenter.Content == child)
            {
                contentPresenter.Content = null;
            }
            return;
        }

        var contentControl = parent as ContentControl;
        if (contentControl != null)
        {
            if (contentControl.Content == child)
            {
                contentControl.Content = null;
            }
            return;
        }

        // maybe more
    }
}
Worthen answered 11/10, 2013 at 12:40 Comment(3)
Accepted! This answer also has some interesting information: https://mcmap.net/q/576920/-remove-control-from-window-in-wpfCob
I added ItemsControl as well: var itemsControl = parent as ItemsControl; if (itemsControl != null) { itemsControl.Items.Remove(child); return; }Cob
Ok, but be aware that the items in an ItemsControl are typically no UI elements, but data items which get their UI by a data template.Worthen
A
4

NEW:

I propose to use base classes instead of all other listed. Try this code, this 3 classes are the most use cases for your needs. As I understand, it's almost the same as previos ^)

  var parent = VisualTreeHelper.GetParent(child);
  var parentAsPanel = parent as Panel;
  if (parentAsPanel != null)
  {
      parentAsPanel.Children.Remove(child);
  }
  var parentAsContentControl = parent as ContentControl;
  if (parentAsContentControl != null)
  {
      parentAsContentControl.Content = null;
  }
  var parentAsDecorator = parent as Decorator;
  if (parentAsDecorator != null)
  {
      parentAsDecorator.Child = null;
  }

OLD: As far as I remember, you can use Visual type as parent type and try to call RemoveVisualChild method.

Absentminded answered 11/10, 2013 at 11:33 Comment(1)
RemoveVisualChild on Visual is protected. So unfortunately I can't call it - otherwise it would have been just what I was looking for.Cob
G
1

For completeness, I've added in the ItemsControl check, and an Add method that will put the child back. The child or parent may not yet be in the visual tree, so you have to check both the visual and logical trees:

    /// <summary>
    /// Adds or inserts a child back into its parent
    /// </summary>
    /// <param name="child"></param>
    /// <param name="index"></param>
    public static void AddToParent(this UIElement child, DependencyObject parent, int? index = null)
    {
        if (parent == null)
            return;

        if (parent is ItemsControl itemsControl)
            if (index == null)
                itemsControl.Items.Add(child);
            else
                itemsControl.Items.Insert(index.Value, child);
        else if (parent is Panel panel)
            if (index == null)
                panel.Children.Add(child);
            else
                panel.Children.Insert(index.Value, child);
        else if (parent is Decorator decorator)
            decorator.Child = child;
        else if (parent is ContentPresenter contentPresenter)
            contentPresenter.Content = child;
        else if (parent is ContentControl contentControl)
            contentControl.Content = child;
    }

    /// <summary>
    /// Removes the child from its parent collection or its content.
    /// </summary>
    /// <param name="child"></param>
    /// <param name="parent"></param>
    /// <returns></returns>
    public static bool RemoveFromParent(this UIElement child, out DependencyObject parent, out int? index)
    {
        parent = child.GetParent(true);
        if (parent == null)
            parent = child.GetParent(false);

        index = null;

        if (parent == null)
            return false;

        if (parent is ItemsControl itemsControl)
        {
            if (itemsControl.Items.Contains(child))
            {
                index = itemsControl.Items.IndexOf(child);
                itemsControl.Items.Remove(child);
                return true;
            }
        }
        else if (parent is Panel panel)
        {
            if (panel.Children.Contains(child))
            {
                index = panel.Children.IndexOf(child);
                panel.Children.Remove(child);
                return true;
            }
        }
        else if (parent is Decorator decorator)
        {
            if (decorator.Child == child)
            {
                decorator.Child = null;
                return true;
            }
        }
        else if (parent is ContentPresenter contentPresenter)
        {
            if (contentPresenter.Content == child)
            {
                contentPresenter.Content = null;
                return true;
            }
        }
        else if (parent is ContentControl contentControl)
        {
            if (contentControl.Content == child)
            {
                contentControl.Content = null;
                return true;
            }
        }

        return false;
    }

    public static DependencyObject GetParent(this DependencyObject depObj, bool isVisualTree)
    {
        if (isVisualTree)
        {
            if(depObj is Visual || depObj is Visual3D)
                return VisualTreeHelper.GetParent(depObj);
            return null;
        }
        else
            return LogicalTreeHelper.GetParent(depObj);
    }
Graciagracie answered 25/9, 2019 at 13:47 Comment(0)
C
-1

My version for @Clemens solution:

    /// <summary>
    /// Disconnects <paramref name="child"/> from it's parent if any.
    /// </summary>
    /// <param name="child"></param>
    public static void DisconnectIt(this FrameworkElement child)
    {
        var parent = child.Parent;
        if (parent == null)
            return;

        if (parent is Panel panel)
        {
            panel.Children.Remove(child);
            return;
        }

        if (parent is Decorator decorator)
        {
            if (decorator.Child == child)
                decorator.Child = null;

            return;
        }

        if (parent is ContentPresenter contentPresenter)
        {
            if (contentPresenter.Content == child)
                contentPresenter.Content = null;
            return;
        }

        if (parent is ContentControl contentControl)
        {
            if (contentControl.Content == child)
                contentControl.Content = null;
            return;
        }

        //if (parent is ItemsControl itemsControl)
        //{
        //  itemsControl.Items.Remove(child);
        //  return;
        //}
    }
Cholula answered 10/5, 2019 at 7:47 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.