在WPF中断开元素与任何/未指定的父容器的连接

时间:2013-10-11 11:29:29

标签: c# wpf logical-tree

我有一个控件是另一个控件的子控件(因为所有非root控件/元素都在WPF中)。 如果我想将控件移动到另一个容器,我必须先将它与当前容器断开连接(否则抛出异常)。

如果我知道父母是什么,那么我可以将其从儿童收藏品,内容或其他内容中删除。但是,如果我不知道父容器的类型是什么 - 如何删除子控件呢?

在下面的代码示例中:如何在不知道父类型(Panel,GroupBox ...)的情况下将“sp1”移动到另一个容器?<​​/ p>

// 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."

理想情况下,我只想写下这样的内容:

sp1.Parent.RemoveChild(sp1);

但我没有找到类似的东西。

4 个答案:

答案 0 :(得分:20)

您可以使用扩展方法编写辅助类:

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
    }
}

答案 1 :(得分:4)

NEW:

我建议使用基类而不是列出的所有其他类。试试这个代码,这3个类是最符合您需求的用例。据我了解,它与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: 据我所知,您可以使用Visual类型作为父类型,并尝试调用RemoveVisualChild方法。

答案 2 :(得分:0)

我的@Clemens解决方案版本:

    /// <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;
        //}
    }

答案 3 :(得分:0)

为了完整起见,我在ItemsControl检查中添加了一个Add方法,该方法会将子级放回原处。子级或父级可能尚未出现在可视树中,因此您必须同时检查可视树和逻辑树:

ContextAwarePoolExecutor