ElementHost + FlowDocument = GC does not work, memory continues to increase

[Updated, see below!]

In our WinForms application, where there is WPF FlowDocumentReader, there is a memory leak, in ElementHost. I recreated this problem in a simple project and added the code below.

What does the application do?

When I click button1:

  • A UserControl1, which contains only FlowDocumentReader, is created and set asElementHost Child
  • A FlowDocumentis created from a text file (it contains only FlowDocumentwith StackPanela few thousand lines <TextBox/>)
  • Property FlowDocumentReader Documentset toFlowDocument

At this point, the page displays correctly FlowDocument. A lot of memory is used, as expected.

Problem

  • button1, , ! GC , , ! , , :

  • button2, elementHost1.Child null GC (. ), - ​​ , , !

, . , ElementHost Controls, Disposing , null, GC, .

  • button1 , .
  • ( "" , , )

, , GC . .

VS, : http://speedy.sh/8T5P2/WindowsFormsApplication7.zip

, . 2 . Form1.cs:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Windows.Documents;
using System.IO;
using System.Xml;
using System.Windows.Markup;
using System.Windows.Forms.Integration;


namespace WindowsFormsApplication7
{
    public partial class Form1 : Form
    {
        private ElementHost elementHost;

        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            string rawXamlText = File.ReadAllText("in.txt");
            using (var flowDocumentStringReader = new StringReader(rawXamlText))
            using (var flowDocumentTextReader = new XmlTextReader(flowDocumentStringReader))
            {
                if (elementHost != null)
                {
                    Controls.Remove(elementHost);
                    elementHost.Child = null;
                    elementHost.Dispose();
                }

                var uc1 = new UserControl1();
                object document = XamlReader.Load(flowDocumentTextReader);
                var fd = document as FlowDocument;
                uc1.docReader.Document = fd;

                elementHost = new ElementHost();
                elementHost.Dock = DockStyle.Fill;
                elementHost.Anchor = AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right;
                Controls.Add(elementHost);
                elementHost.Child = uc1;
            }
        }

        private void button2_Click(object sender, EventArgs e)
        {
            if (elementHost != null)
                elementHost.Child = null;

            GC.Collect();
            GC.WaitForPendingFinalizers();
            GC.Collect();
        }
    }
}

UserControl1.xaml

<UserControl x:Class="WindowsFormsApplication7.UserControl1"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300">
    <FlowDocumentReader x:Name="docReader"></FlowDocumentReader>
</UserControl>

:

, . ElementHost, , . , , , click1 , , - , , . , .

, , , :

1)

2) "",

3) "GO" -

4a) - .

4b) "CLEAN", , , , GC/

, 3) 4). "CLEAN" , , .

CLR "GO" ( ~ 350 ). , 16125 (5x ) Controls.TextBox 16125 Controls.TextBoxView, 16125 Documents.TextEditor, - . :

http://i.imgur.com/m28Aiux.png blah

.

- ()

, WPF-, ElementHost FlowDocument, . , WPF TextBox, TextEditor.

, , , .

, TextBoxes, ( ):

        var textBoxes = FindVisualChildren<TextBox>(this).ToList();
        foreach (var textBox in textBoxes)
        {
            var type = textBox.GetType();
            object textEditor = textBox.GetType().GetProperty("TextEditor", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(textBox, null);
            var onDetach = textEditor.GetType().GetMethod("OnDetach", BindingFlags.NonPublic | BindingFlags.Instance);
            onDetach.Invoke(textEditor, null);
        }

FindVisualChildren:

    public static IEnumerable<T> FindVisualChildren<T>(DependencyObject depObj) where T : DependencyObject
    {
        if (depObj != null)
        {
            for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
            {
                DependencyObject child = VisualTreeHelper.GetChild(depObj, i);
                if (child != null && child is T)
                {
                    yield return (T)child;
                }

                foreach (T childOfChild in FindVisualChildren<T>(child))
                {
                    yield return childOfChild;
                }
            }
        }
    }

, , TextBox. GC.Collect() ( , ). , , , . TextEditors .

+5
2

, PresentationFramework.dll!System.Windows.Documents.TextEditor , ( , ), . PresentationFramework.dll, , , , TextBox es, TextEditor s. TextBox.OnDetach TextBoxBase.InitializeTextContainer(). , a TextBox TextEditor, . , TextEditor, - WPF . , WPF. WPF , MarshalByRefObject, Windows Forms. ElementHost ( ), MAF WPF ; . SO.

+1

: ElementHost WPF Windows Forms

, click Button2:

if (elementHost1 != null)
{
    elementHost1.Child = null;
    elementHost1.Dispose();
    elementHost1.Parent = null;
    elementHost1 = null;
}

, GC.Collect() , . , Form1. 20 , Button1, Button2, , .

: , , , , GC.Collect(). , ElementHost.

Edit2, my Form1:

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();

        m_uc1 = new UserControl1();
        elementHost1.Child = m_uc1;
    }

    private UserControl1 m_uc1;

    private void button1_Click(object sender, EventArgs e)
    {
        string rawXamlText = File.ReadAllText(@"in.txt");
        var flowDocumentStringReader = new StringReader(rawXamlText);            
        var flowDocumentTextReader = new XmlTextReader(flowDocumentStringReader);           
        object document = XamlReader.Load(flowDocumentTextReader);
        var fd = document as FlowDocument;

        m_uc1.docReader.Document = fd;

        flowDocumentTextReader.Close();
        flowDocumentStringReader.Close();
        flowDocumentStringReader.Dispose();

    }        

    private void Form1_FormClosing(object sender, FormClosingEventArgs e)
    {
        if (elementHost1 != null)
        {
            elementHost1.Child = null;
            elementHost1.Dispose();
            elementHost1.Parent = null;
            elementHost1 = null;
        }
    }

GC.Collect() . , .

+2

All Articles