0

I a beginner in C# and WPF. I'm programming plugin for a node based software called vvvv. I have implemented sliders, buttons and other simple ui elements. The following code shows how a sliders node look in c# :

using System.Windows;
using System.Windows.Controls;
using System.Windows.Markup;
using System.Xml;
using VVVV.PluginInterfaces.V2;

namespace VVVV.Packs.UI.Nodes.WPF
{
    [PluginInfo(Author = "lecloneur", Category = "WPF", Help = "WPF Slider", Name = "Slider", AutoEvaluate = false)]

    public class WPFSlider : GenericNode, IPluginEvaluate
    {
        [Input("SizeX", DefaultValue = 120, Order = 9, MinValue = 0)]
        public IDiffSpread<int> SizeX;

        [Input("SizeY", DefaultValue = 120, Order = 9, MinValue = 0)]
        public IDiffSpread<int> SizeY;

        [Input("Orientation", Order = 1, DefaultEnumEntry = "Horizontal")]
        public IDiffSpread<Orientation> OrientationIn;

        [Output("Value", Order = 2)]
        public ISpread<double> ValueOut;

        int elements_count = 0;

        public void Evaluate(int SpreadMax)
        {
            UIElementOut.SliceCount = SpreadMax;
            ValueOut.SliceCount = SpreadMax;
            for (int i = 0; i < SpreadMax; i++)
            {
                if (UIElementOut == null || !(UIElementOut[0] is Slider) || elements_count < SpreadMax || OrientationIn.IsChanged || SizeX.IsChanged || SizeY.IsChanged)
                {
                    CreateElement(i);
                }
                OutputData(i);
                Transformation(i, (Slider)UIElementOut[i]);
            }
            elements_count = SpreadMax;
        }

        private void CreateElement(int i)
        {
            UIElementOut[i] = new Slider();
            var uiElement = (Slider)UIElementOut[i];
            uiElement.Minimum = 0;
            uiElement.Maximum = 1;
            uiElement.Orientation = OrientationIn[i];
            uiElement.IsMoveToPointEnabled = true;
            uiElement.Width = SizeX[i]; ;
            uiElement.Height = SizeY[i];
            uiElement.VerticalAlignment = VerticalAlignment.Center;
            uiElement.HorizontalAlignment = HorizontalAlignment.Center;

            XmlReader XmlRead = XmlReader.Create("Styles/SliderStyle.xaml");
            ResourceDictionary myResourceDictionary = (ResourceDictionary)XamlReader.Load(XmlRead);
            XmlRead.Close();
            Style uiElementStyle = myResourceDictionary["SliderStyle"] as Style;
            uiElement.Style = uiElementStyle;
        }

        private void OutputData(int i)
        {
            var uiElement = (Slider)UIElementOut[i];
            ValueOut[i] = uiElement.Value;
        }
    } 
}

Now I'm trying to implement a tabcontrol where I could dynamically create tabitem and input UIElement into it. As far as I understand, I can only add one things to a tabitem. So I was thinking about creating a grid everytime I need to and fill it with all the incoming UIElement.

    public void Evaluate(int SpreadMax)
    {
        SpreadMax = 1;

        UIElementOut.SliceCount = 1;

        for (var i = 0; i < SpreadMax; i++)
        {
            if (UIElementOut == null || !(UIElementOut[i] is TabControl))
                UIElementOut[i] = new TabControl();
                var uiElement = (TabControl)UIElementOut[i];
                uiElement.Height = 200;
                uiElement.Width = 500;
        }



        Grid grid;

        int[] _elementCounts = new int[_elementInputs.SliceCount];

        for (var i = 0; i < _elementInputs.SliceCount; i++)
        {
            if (_elementInputs[i] == null || !(_elementInputs[i] is UIElement))
            {
                grid = new Grid();

                for (var j = 0; j < _elementInputs[i].IOObject.SliceCount; j++)
                {
                    if (_elementInputs[i].IOObject[j] != null)
                    {
                        UIElement test = new UIElement();
                        test = _elementInputs[i].IOObject[j];
                        grid.Children.Add(test);
                    }
                }
                _elementCounts[i] = _elementInputs[i].IOObject.SliceCount;
                ValueOut[i] = _elementCounts[i];


                if (((TabControl)UIElementOut[0]).Items.Count <= i)
                {
                    ((TabControl)UIElementOut[0]).Items.Add(new TabItem { Header = _nameInputs[i].IOObject[0], Content = grid });
                }

                if (_nameInputs[i].IOObject.IsChanged)
                {
                    ((TabItem)((TabControl)UIElementOut[0]).Items[i]).Header = _nameInputs[i].IOObject[0];
                }

                if (_elementInputs[i].IOObject.IsChanged)
                {
                    ((TabItem)((TabControl)UIElementOut[0]).Items[i]).Content = grid;
                }
            }
        }

        for (var i = ((TabControl)UIElementOut[0]).Items.Count - 1; ((TabControl)UIElementOut[0]).Items.Count > _elementInputs.SliceCount; i--)
        {
            ((TabControl)UIElementOut[0]).Items.RemoveAt(i);
        }
    }

I searched a lot but can't find any idea how to solve the error. Apparently adding elements to a the grid throw "specified element is already the logical child of another element";

Ilan
  • 2,762
  • 1
  • 13
  • 24
lecloneur
  • 424
  • 5
  • 20
  • 1
    maybe this is what you're looking for http://stackoverflow.com/questions/5954948/navigating-up-the-visual-tree-in-wpf and this VisualTreeHelper Class might be helpful as well https://msdn.microsoft.com/en-us/library/system.windows.media.visualtreehelper(v=VS.100).aspx – kenny Jan 31 '16 at 10:19
  • sorry I might have written my question in the wrong way. My code throw this error : "specified element is already the logical child of another element" – lecloneur Jan 31 '16 at 14:14
  • `if(VisualTreeHelper.GetParent(some_elem) == null)` – AnjumSKhan Jan 31 '16 at 14:50
  • I updated my question with the full code as I'm still lost in how to use VisualTreeHelper. – lecloneur Feb 01 '16 at 03:47

1 Answers1

1

Hi please try the next method on a several visual objects (child) and check if the resulted object is the same reference. Here is a usefull link with explanations and more...

Extension class code:

public static class VisualTreeHelperExtensions
{
    public static T FindParent<T>(this DependencyObject child) where T : DependencyObject
    {
        while (true)
        {
            //get parent item
            DependencyObject parentObject = VisualTreeHelper.GetParent(child);

            //we've reached the end of the tree
            if (parentObject == null) return null;

            //check if the parent matches the type we're looking for
            T parent = parentObject as T;
            if (parent != null)
                return parent;
            child = parentObject;
        }
    }
}

Example:

var dataGrid1 = dependencyObject1.FindParent<DataGrid>();
var dataGrid2 = dependencyObject2.FindParent<DataGrid>();
var isSameObject = dataGrid1 == dataGrid2;

Updates

  1. The grid can contains a large number of elements but the only last will be visible to user.
  2. The error is coming from the elemnt you want to add itself, that elemnt is belong to another control (some another control has the element as a child).
  3. Find the parent element of the element you want to add, remove the element from the current parent's children collection, and add this element as the new child of your grid.
  4. Try to use snoop to figure out who is the parent of your element (containing in _elementInputs).
  5. Here are some useful links (first, second).

Update 2

  1. As I can understand you have a third party infrastructure in your project because I can't resolve the type of _elementInputs and UIElementOut collections.
  2. Since the _elementInputs is a field, I still can't understand where is _elementInputs came from (can't see that in code).
  3. The code to add a completely new element is wrong:

Correct code(in my opinion)

                //here is in my opinion the correct algorithm in this case
                var elementFromInputs = _elementInputs[i] as UIElement;
                if (elementFromInputs == null) continue;
                //try to find parent of type Panel
                var parentPanel = elementFromInputs.FindParent<Panel>();
                //try to find parent of type ContentControl
                var parentContentControl = elementFromInputs.FindParent<ContentControl>();
                if (parentPanel == null && parentContentControl == null)
                {
                    //add if there is no any parents
                    grid.Children.Add(elementFromInputs);
                }
                if (parentPanel != null)
                {
                    //remove element from parent's collection
                    parentPanel.Children.Remove(elementFromInputs);
                }
                if(parentContentControl != null)
                {
                    //reset parent content to release element
                    parentContentControl.Content = null;
                }
                grid.Children.Add(elementFromInputs);

Update 3

You've pasted the code (from the correct code section) inside the if which condition is the _elementInputs[i] == null || !(_elementInputs[i] is UIElement) that means filtering all your UIElements out of the scope. Since I'm not familiar with the vvvv concepts I don't know what do you have inside the _elementInputs array, but if you have the UIElements there you need past the code I gave you before the if with condition _elementInputs[i] == null || !(_elementInputs[i] is UIElement).

Please update your question with the next clarifications: 1. What is inside the _elementInputs[i]? 2. What is inside the _elementInputs[i].IOObject? 3. What are UIElements you want to add to the grid? 4. Please run the next method and write me the comment what do you have in your grid and TabControl controls.

Test code

public void Evaluate(int SpreadMax)
{
    SpreadMax = 1;

    UIElementOut.SliceCount = 1;

    for (var i = 0; i < SpreadMax; i++)
    {
        if (UIElementOut == null || !(UIElementOut[i] is TabControl))
            UIElementOut[i] = new TabControl();
            var uiElement = (TabControl)UIElementOut[i];
            uiElement.Height = 200;
            uiElement.Width = 500;
    }

    Grid grid = new Grid();
    var listOfElements = new List<UIElements>
    {
            new Button {Background = Brushes.Tomato, Content = "Click Me"},
            new Button {Background = Brushes.Yellow, Content = "Click Me"},
            new Button {Background = Brushes.Green, Content = "Click Me"},
            new Button {Background = Brushes.Blue, Content = "Click Me"}
    };
    listOfElements.ForEach(button => grid.Children.Add(button));
    ((TabControl)UIElementOut[0]).Items.Add(new TabItem { Header = "Objects", Content = grid });
}

I'll be glad to help if you will have problems with the code. Regards.

Community
  • 1
  • 1
Ilan
  • 2,762
  • 1
  • 13
  • 24
  • thank you, sorry but I may have written my question in a wrong way. See edit. – lecloneur Jan 31 '16 at 14:15
  • @lecloneur do you want to add element to grid in code? what is the problem you want to solve? why do you want to use a grid and not a stack panel or items control? – Ilan Jan 31 '16 at 14:37
  • yes I want to add element with code behind and use a grid. Problem is the code I posted is not working, as apparently I'm addind the same visual element to the grid so an error is thrown. – lecloneur Jan 31 '16 at 14:42
  • @lecloneur there is no any problem with the grid please see the update I've added. – Ilan Jan 31 '16 at 14:55
  • but what I don't understand is, which control this element belongs to ? I'm only adding it through this loop. It is not added anywhere else. – lecloneur Jan 31 '16 at 15:19
  • @lecloneur your controls are came from _elementInputs what is that array and where are it's elements came from? you can use the provided function (FindParent) to find the current parent of that element, type of the parent you are looking for is a Panel I think (FindParent), the method will find the first parent of type Panel, and then parent.Children.Remove(elementYouWandToPutIntoYourGrid). – Ilan Jan 31 '16 at 15:37
  • sorry I'm lost so I posted the full code in case it helps. _elementInputs is an array of UIElement (slider, button, etc..., can be anything). The grid parent is one tabcontrol, which is created only once. Then I create several grid and for each grid I fill them with the UIElement contained in the array. So I create a new var every time so I don't put the same instance each time. Sorry but I'm a real beginner... – lecloneur Feb 01 '16 at 03:35
  • hi,thank you for your patience. I was at first trying to make my question as precise as possible but where I thought there was an error was the wrong place I guess. _elementsInputs comes from an other class, I'm using vvvv, node based software with C# plugin. I input UIElement (slider or button or anything) to this tab node. I tried your code, error is coming from this : DependencyObject parentObject = VisualTreeHelper.GetParent(child); it can't be null. I don't know why. – lecloneur Feb 01 '16 at 08:40
  • @lecloneur it means that the child is null, try to prevent the child to be null – Ilan Feb 01 '16 at 08:58
  • yes if I prevent it to be null with the line you wrote : if (elementFromInputs == null) continue; no error any longer but no elements are added into the TabItem. – lecloneur Feb 01 '16 at 09:19
  • I should see a sliders, as _elementInputs[i] is an array of UIElement (Sliders) but could be button too, or toggle it doesn't matter, there are created the same way. And _elementInputs[0].IOObject[0] should be one of those sliders in this case. – lecloneur Feb 01 '16 at 09:28
  • Here you can find the whole thing I have right now [link]http://pastebin.com/i5tB9bgg elementFromInputs is always null apparently so nothing happen. – lecloneur Feb 01 '16 at 09:44