web-dev-qa-db-fra.com

Déconnexion d'un élément d'un conteneur parent quelconque / non spécifié dans WPF

J'ai un contrôle qui est un enfant d'un autre contrôle (car tous les contrôles/elemts non root sont dans WPF). Si je veux déplacer le contrôle vers un autre conteneur, je dois d'abord le déconnecter de son conteneur actuel (sinon une exception est levée).

Si je sais ce qu'est le parent, je peux simplement le supprimer de sa collection Enfants, ou Contenu ou autre. Mais que faire si je ne sais pas quel est le type du conteneur parent - comment puis-je supprimer le contrôle enfant?

Dans l'exemple de code ci-dessous: Comment pourrais-je déplacer "sp1" vers un autre conteneur sans connaître le type du 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."

Idéalement, je voudrais simplement écrire quelque chose comme:

sp1.Parent.RemoveChild(sp1);

Mais je n'ai rien trouvé de tel.

22
Björn

Vous pouvez écrire une classe d'assistance avec une méthode d'extension:

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

NOUVEAU:

Je propose d'utiliser des classes de base au lieu de toutes les autres répertoriées. Essayez ce code, ces 3 classes sont les cas d'utilisation les plus adaptés à vos besoins. Si je comprends bien, c'est presque la même chose que les 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: Autant que je me souvienne, vous pouvez utiliser le type Visual comme type parent et essayer d'appeler la méthode RemoveVisualChild.

4
Lonli-Lokli

Ma version pour la 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;
        //}
    }
0
google dev

Pour être complet, j'ai ajouté dans la vérification ItemsControl, et une méthode Add qui remettra l'enfant. L'enfant ou le parent n'est peut-être pas encore dans l'arborescence visuelle, vous devez donc vérifier à la fois les arborescences visuelles et logiques:

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