Tag Archives: Bug

Memory leak with focusable UIElement in Silverlight?

Suppose we have a StackPanel with following controls inside.

        <StackPanel x:Name="stackpanel" Width="200">
            <TextBox />
            <CheckBox />
            <ComboBox />
            <controls:DatePicker />
            <Button />
            <local:MyTextBox />
        </StackPanel>

 

All of them are focusable controls. MyTextBox is a subclass of TextBox as following:

    public class MyTextBox : TextBox
    {
        ~MyTextBox()
        {
            Debug.WriteLine("MyTextBox.Finalize");
        }
    }

 

Now, if I remove the Children in the stackpanel…

this.stackpanel.Children.Clear();


…all Children gets garbage collected. Everything correct until here.

 

Restart the application. Now set the focus to any element in the stackpanel and stepwise to all others too.

If I now remove all children from the stackpanel none of them gets garbage collected.

this.stackpanel.Children.Clear();


Unfortunately, this problem has several negative side effects. We are using the MVVM pattern and when we databind the controls to a ViewModel then the ViewModel is also part of the memory leak, because the binding contains a reference to the ViewModel.

It also doesn’t help to remove the whole page. I’ve tested the code within a navigation application with the same result.

 

Demo Project

Donwload the demo project here.

Please note: perhaps you have to run the GC n-times to collect all object, but at the end all elements which had the focus at any time will never be collected neither when you run the GC 1000 times or neither when you release the whole user control.

 

XAML snapshot

    <Grid x:Name="LayoutRoot" Background="White">
        
        <StackPanel >
            <Button x:Name="btnRemove" Content="Remove all" Click="btnRemove_Click" />
            <Button x:Name="btnCollect" Content="Run Garbage Collector" Click="btnCollect_Click"  />
        </StackPanel>

        <StackPanel x:Name="stackpanel" Width="200">
            <TextBox />
            <CheckBox />
            <ComboBox />
            <controls:DatePicker />
            <Button />
            <local:MyTextBox />
        </StackPanel>
        
    </Grid>

 

C# snapshot

    public partial class MainPage : UserControl
    {
        private List<WeakReference> weakRefs = new List<WeakReference>();

        public MainPage()
        {
            InitializeComponent();

            // collect all children to get a weakreference to it
            foreach (UIElement element in this.stackpanel.Children)
                weakRefs.Add(new WeakReference(element));

            // add the view model to the list too
            weakRefs.Add(new WeakReference(this.DataContext));
        }

        private void btnRemove_Click(object sender, RoutedEventArgs e)
        {
            // removing the DataContext forces a rebind of all databound controls. 
            // This releases the binding-reference from the control to the ViewModel.
            this.DataContext = null;

            // important: first remove the datacontext and then clear the children 
            //            otherwise the ViewModel is part of the memory leak.
            this.stackpanel.Children.Clear();
        }

        private void btnCollect_Click(object sender, RoutedEventArgs e)
        {
            GC.Collect();
            GC.WaitForPendingFinalizers();


            // count how many reference are not collected
            var objectsAlive = (from wr in weakRefs
                                where wr.IsAlive
                                select wr.Target).ToList();

            Debug.WriteLine("Total instances not collected: " + objectsAlive.Count);
            objectsAlive.ForEach(obj => Debug.WriteLine("     " + obj.GetType().Name));
        }

    }


    // class to output the finalizing
    public class MyTextBox : TextBox
    {
        ~MyTextBox()
        {
            Debug.WriteLine("MyTextBox.Finalize");
        }
    }

 

Feedback

I’ve posted this problem on the silverlight.net forum as a possible bug. Check it out here

Advertisements
Tagged ,