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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/19317064/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-10 14:43:54  来源:igfitidea点击:

Disconnecting an element from any/unspecified parent container in WPF

c#wpflogical-tree

提问by Bj?rn

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).

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

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...)?

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

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

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

采纳答案by Clemens

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

回答by Lonli-Lokli

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 ^)

我建议使用基类而不是所有其他列出的类。试试这个代码,这 3 个类是最能满足您需求的用例。据我了解,它和以前的几乎一样^)

  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 Visualtype as parent type and try to call RemoveVisualChild method.

OLD:据我所知,您可以使用Visual类型作为父类型并尝试调用 RemoveVisualChild 方法。

回答by google dev

My version for @Clemens solution:

我的@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;
        //}
    }

回答by outbred

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:

为了完整起见,我添加了 ItemsControl 检查和一个 Add 方法,它将把孩子放回去。子级或父级可能尚未在可视化树中,因此您必须检查可视化树和逻辑树:

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