gorkir
gorkir

Reputation: 23

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

I have this UserControl:

[ContentProperty("Items")]
[DefaultProperty("Items")]
public partial class PanelControl : UserControl
{
    public static readonly DependencyProperty OrientationProperty = DependencyProperty.Register("Orientation", typeof(Orientation), typeof(PanelControl), new FrameworkPropertyMetadata(Orientation.Horizontal, new PropertyChangedCallback(OnOrientationChanged)));
    public static readonly DependencyProperty ItemsProperty = DependencyProperty.Register("Items", typeof(ObservableCollection<UIElement>), typeof(PanelControl), new FrameworkPropertyMetadata(new ObservableCollection<UIElement>(), new PropertyChangedCallback(OnItemsChanged)));
    public static readonly DependencyProperty SizeProperty = DependencyProperty.RegisterAttached("Size", typeof(double), typeof(PanelControl), new FrameworkPropertyMetadata(1.0, new PropertyChangedCallback(OnSizeChanged)), new ValidateValueCallback(IsSizeValid));

    public Orientation Orientation
    {
        get
        {
            return (Orientation)GetValue(OrientationProperty);
        }
        set
        {
            SetValue(OrientationProperty, value);
        }
    }

    public ObservableCollection<UIElement> Items
    {
        get
        {
            return (ObservableCollection<UIElement>)GetValue(ItemsProperty);
        }
        set
        {
            SetValue(ItemsProperty, value);
        }
    }

    public static void SetSize(UIElement element, double size)
    {
        element.SetValue(SizeProperty, size);
    }
    public static double GetSize(UIElement element)
    {
        return (double)element.GetValue(SizeProperty);
    }

    private static void OnOrientationChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs args)
    {
        /*MessageBox.Show("orientation");*/
        ((PanelControl)dependencyObject).ClearAndBuildGrid();
    }

    private static void OnItemsChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs args)
    {
        /*MessageBox.Show("items");*/
        ((PanelControl)dependencyObject).ClearAndBuildGrid();
        if(args.OldValue != null)
            ((ObservableCollection<UIElement>)args.OldValue).CollectionChanged -= ((PanelControl)dependencyObject).OnItemsCollectionChanged;
        if (args.NewValue != null)
            ((ObservableCollection<UIElement>)args.NewValue).CollectionChanged += ((PanelControl)dependencyObject).OnItemsCollectionChanged;

    }

    private void OnItemsCollectionChanged(object sender, NotifyCollectionChangedEventArgs args)
    {
        /*MessageBox.Show("collection");*/
        ClearAndBuildGrid();
    }

    private static void OnSizeChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs args)
    {
        ((PanelControl)dependencyObject).ClearAndBuildGrid();
        /*MessageBox.Show("size");*/
    }
    private static bool IsSizeValid(object value)
    {
        return (double)value < 0 ? false : true;
    }

    private void ClearAndBuildGrid()
    {
        MainGrid.Children.Clear();
        MainGrid.RowDefinitions.Clear();
        MainGrid.ColumnDefinitions.Clear();
        /*MessageBox.Show(MainGrid.Children.Count.ToString());*/
        for (int i = 0; i < Items.Count; i++)
        {
            if (Orientation == Orientation.Horizontal)
            {
                MainGrid.ColumnDefinitions.Add(new ColumnDefinition() { Width = SizeToGridLength(GetSize(Items[i])) });
                Grid.SetColumn(Items[i], i * 2);
                if (i != Items.Count - 1)
                {
                    MainGrid.ColumnDefinitions.Add(new ColumnDefinition() { Width = new GridLength(5) });
                    GridSplitter splitter = new GridSplitter() { ResizeDirection = GridResizeDirection.Columns, HorizontalAlignment = HorizontalAlignment.Stretch };
                    Grid.SetColumn(splitter, i * 2 + 1);
                    MainGrid.Children.Add(splitter);
                }
            }
            else
            {
                MainGrid.RowDefinitions.Add(new RowDefinition() { Height = SizeToGridLength(GetSize(Items[i])) });
                Grid.SetRow(Items[i], i * 2);
                if (i != Items.Count - 1)
                {
                    MainGrid.RowDefinitions.Add(new RowDefinition() { Height = new GridLength(5) });
                    GridSplitter splitter = new GridSplitter() { ResizeDirection = GridResizeDirection.Rows, VerticalAlignment = VerticalAlignment.Stretch };
                    Grid.SetRow(splitter, i * 2 + 1);
                    MainGrid.Children.Add(splitter);
                }
            }
            MainGrid.Children.Add(Items[i]);
        }
    }

    private GridLength SizeToGridLength(double size)
    {
        return new GridLength(size, GridUnitType.Star);
    }

    public PanelControl()
    {
        InitializeComponent();
        Items.CollectionChanged += OnItemsCollectionChanged;
    }
}

And I use it here:

<p:PanelControl>
    <Button />
    <Button />
    <Button />
</p:PanelControl>

When i start application it works good, but in designer I have underlined first button in xaml and error "Specified element is already the logical child of another element. Disconnect it first." Thanks for help, sorry for my bad English.

Upvotes: 2

Views: 2762

Answers (2)

Kory Gill
Kory Gill

Reputation: 7163

Using the code from J.H., I would move it up to the top of your function, so that the state of your grid when you clear it, is "cleared", and all the children have been disconnected from the Visual Tree.

private void ClearAndBuildGrid()
{
    foreach (var item in Items)
    {
        var parent = System.Windows.Media.VisualTreeHelper.GetParent(item) as Grid;
        if (parent != null)
            parent.Children.Remove(item);
    }
    MainGrid.Children.Clear();

It would be a style/intention/preference thing, and to be clear, the answer J.H. gives is totally valid.

Consider using foreach instead of for and not having to deal with array subscripts.

Upvotes: 0

J.H.
J.H.

Reputation: 4322

Not sure what is going on with the designer but this will 'fix' it.

Change the line:

MainGrid.Children.Add(Items[i]);

To:

var parent = VisualTreeHelper.GetParent(Items[i]) as Grid;
if (parent != null)
    parent.Children.Remove(Items[i]);
MainGrid.Children.Add(Items[i]);

Upvotes: 2

Related Questions