Page 1 of 1

Unity remove / destroy control question

Posted: 15 Jul 2014, 17:59
by rcalt2vt
Hey all, I'm slowly figuring out all the little details about the Unity / NoesisGUI integration, but one thing I'm a bit stuck on is how a control gets removed and destroyed at runtime if so desired and the lifetime of UserControls. I've got a basic setup to test this, however I a few odd things as I run this code.

First I notice when I run the scene. I get 2 calls to the default constructor followed by a single call to the destructor. Secondly, when I remove the UserControl from the list of children, the test window is no longer visible as expected, however I do not receive a destructor call. So I assume there are still references somewhere, but given this simple test case, I'm drawing a blank. Any insight would be greatly appreciated.

MainWindow.xaml
<Page
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
  xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
  xmlns:noesis="clr-namespace:NoesisGUIExtensions" xmlns:test="clr-namespace:Test"
  RenderTransformOrigin="0.5,0.5">
	<Grid x:Name="LayoutRoot">
		<five:TestWindow/>
	</Grid>
</Page>
TestWindow.xaml
<UserControl
	xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
	xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
	xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
	xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
	xmlns:noesis="clr-namespace:NoesisGUIExtensions"
	mc:Ignorable="d" x:Class="Test.TestWindow" x:Name="TestWindowControl">

	<Grid x:Name="WindowRoot" Width="400" Height="200" Background="White">
		<Button x:Name="CloseButton" Content="Button"/>
	</Grid>
</UserControl>
TestWindow.cs
using Noesis;
using System;

namespace Test
{
    [Noesis.Extended]
    [Noesis.UserControlSource("Assets/GUI/TestWindow.xaml")]
    public class TestWindow: Noesis.UserControl
    {
        public TestWindow()
        {
            UnityEngine.Debug.Log("Default Contstructor");
        }

        ~TestWindow()
        {
            UnityEngine.Debug.Log("Default Destructor");
        }

        public void OnPostInit()
        {
            Button button = FindName<Button>("CloseButton");
            button.Click += OnButtonClick;
        }

        public void OnButtonClick(BaseComponent sender, RoutedEventArgs e)
        {
            FrameworkElement framElem = GetParent();
            Grid grid = framElem.As<Grid>();
            if (grid != null)
            {
                UnityEngine.Debug.Log("Grid Element Name: " + grid.GetName());
                UIElementCollection collection = grid.GetChildren();
                collection.Remove(this);
            }
        }
    }
}

Re: Unity remove / destroy control question

Posted: 15 Jul 2014, 23:43
by wckdspn
What happens when you remove "button.Click += OnButtonClick;" and instead just remove it after some time? I'm curious, since callbacks attached to native objects seem to one of the more common ways for memory leaks to show up.

Re: Unity remove / destroy control question

Posted: 15 Jul 2014, 23:59
by sfernandez
Loading of a xaml file takes two steps:
- All objects in the tree are created, thus, its constructors are called.
- All the objects in the tree are initialized. During initialization the UserControl loads its contents from the xaml specified in the UserControlSource attribute. This is why your UserControl constructos is called again, although that instance will be discarded and its content will be connected to the first UserControl instance.

About the destructor, it gets called when all strong references from C# are freed, and object is disconnected from the tree. In your test, when you attach a handler to the Click event, the delegate stored keeps a strong reference to your UserControl. And this strong reference is the one that prevents the object from being destroyed.

I changed your test to the following:
namespace Test
{
    [Noesis.Extended]
    [Noesis.UserControlSource("Assets/Test/TestWindow.xaml")]
    public class TestWindow : Noesis.UserControl
    {
        // ...

        public void OnButtonClick(BaseComponent sender, RoutedEventArgs e)
        {
            Button button = FindName<Button>("CloseButton");
            button.Click -= OnButtonClick;

            FrameworkElement framElem = GetParent();
            Grid grid = framElem.As<Grid>();
            if (grid != null)
            {
                UnityEngine.Debug.Log("Grid Element Name: " + grid.GetName());
                UIElementCollection collection = grid.GetChildren();
                collection.Remove(this);
            }
        }
    }
}
And the destructor is called just after you click the button and the GC collects the object.

Re: Unity remove / destroy control question

Posted: 16 Jul 2014, 00:54
by rcalt2vt
Doh, I knew it had to be something stupid like that. Thanks for the answer.