Bindable richTextBox still hanging in memory {WPF, Caliburn.Micro}
I use in WFP Caliburn.Micro Framework. I need bindable richTextbox for Document property. I found many ways how do it bindable richTextBox.
But I have one problem. From parent window I open child window. Child window consist bindable richTextBox user control.
After I close child window and use memory profiler view class with bindabelrichTextBox control and view model class is still hanging in memory. -> this cause memory leaks.
If I use richTextBox from .NET Framework or richTextBox from Extended WPF Toolkit it doesn’t cause this memory leak problem.
I can’t identified problem in bindable richTextBox class.
Here is ist class for bindable richTextBox:
Base class can be from .NET or Extended toolkit.
/// <summary>
/// Represents a bindable rich editing control which operates on System.Windows.Documents.FlowDocument
/// objects.
/// </summary>
public class BindableRichTextBox : RichTextBox
{
/// <summary>
/// Identifies the <see cref="Document"/> dependency property.
/// </summary>
public static readonly DependencyProperty DocumentProperty = DependencyProperty.Register("Document",
typeof(FlowDocument), typeof(BindableRichTextBox));
/// <summary>
/// Initializes a new instance of the <see cref="BindableRichTextBox"/> class.
/// </summary>
public BindableRichTextBox()
: base()
{
}
/// <summary>
/// Initializes a new instance of the <see cref="BindableRichTextBox"/> class.
/// </summary>
/// <param title="document">A <see cref="T:System.Windows.Documents.FlowDocument"></see> to be added as the initial contents of the new <see cref="T:System.Windows.Controls.BindableRichTextBox"></see>.</param>
public BindableRichTextBox(FlowDocument document)
: base(document)
{
}
/// <summary>
/// Raises the <see cref="E:System.Windows.FrameworkElement.Initialized"></see> event. This method is invoked whenever <see cref="P:System.Windows.FrameworkElement.IsInitialized"></see> is set to true internally.
/// </summary>
/// <param title="e">The <see cref="T:System.Windows.RoutedEventArgs"></see> that contains the event data.</param>
protected override void OnInitialized(EventArgs e)
{
// Hook up to get notified when DocumentProperty changes.
DependencyPropertyDescriptor descriptor = DependencyPropertyDescriptor.FromProperty(DocumentProperty, typeof(BindableRichTextBox));
descriptor.AddValueChanged(this, delegate
{
// If the underlying value of the dependency property changes,
// update the underlying document, also.
base.Document = (FlowDocument)GetValue(DocumentProperty);
});
// By default, we support updates to the source when focus is lost (or, if the LostFocus
// trigger is specified explicity. We don't support the PropertyChanged trigger right now.
this.LostFocus += new RoutedEventHandler(BindableRichTextBox_LostFocus);
base.OnInitialized(e);
}
/// <summary>
/// Handles the LostFocus event of the BindableRichTextBox control.
/// </summary>
/// <param title="sender">The source of the event.</param>
/// <param title="e">The <see cref="System.Windows.RoutedEventArgs"/> instance containing the event data.</param>
void BindableRichTextBox_LostFocus(object sender, RoutedEventArgs e)
{
// If we have a binding that is set for LostFocus or Default (which we are specifying as default)
// then update the source.
Binding binding = BindingOperations.GetBinding(this, DocumentProperty);
if (binding.UpdateSourceTrigger == UpdateSourceTrigger.Default ||
binding.UpdateSourceTrigger == UpdateSourceTrigger.LostFocus)
{
BindingOperations.GetBindingExpression(this, DocumentProperty).UpdateSource();
}
}
/// <summary>
/// Gets or sets the <see cref="T:System.Windows.Documents.FlowDocument"></see> that represents the contents of the <see cref="T:System.Windows.Controls.BindableRichTextBox"></see>.
/// </summary>
/// <value></value>
/// <returns>A <see cref="T:System.Windows.Documents.FlowDocument"></see> object that represents the contents of the <see cref="T:System.Windows.Controls.BindableRichTextBox"></see>.By default, this property is set to an empty <see cref="T:System.Windows.Documents.FlowDocument"></see>. Specifically, the empty <see cref="T:System.Windows.Documents.FlowDocument"></see> contains a single <see cref="T:System.Windows.Documents.Paragraph"></see>, which contains a single <see cref="T:System.Windows.Documents.Run"></see> which contains no text.</returns>
/// <exception cref="T:System.ArgumentException">Raised if an attempt is made to set this property to a <see cref="T:System.Windows.Documents.FlowDocument"></see> that represents the contents of ano开发者_JS百科ther <see cref="T:System.Windows.Controls.RichTextBox"></see>.</exception>
/// <exception cref="T:System.ArgumentNullException">Raised if an attempt is made to set this property to null.</exception>
/// <exception cref="T:System.InvalidOperationException">Raised if this property is set while a change block has been activated.</exception>
public new FlowDocument Document
{
get { return (FlowDocument)GetValue(DocumentProperty); }
set { SetValue(DocumentProperty, value); }
}
}
Thank fro help and advice.
Qucik example:
Child window with .NET richTextBox
<Window x:Class="WpfApplication2.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300">
<Grid>
<RichTextBox Background="Green"
VerticalScrollBarVisibility="Auto"
HorizontalScrollBarVisibility="Auto"
FontSize="13"
Margin="4,4,4,4"
Grid.Row="0"/>
</Grid>
</Window>
This window I open from parent window:
var w = new Window1();
w.Show();
Then close this window, check with memory profiler and it memory doesn’t exist any object of window1 - richTextBox. It’s Ok.
But then I try bindable richTextBox:
Child window 2:
<Window x:Class="WpfApplication2.Window2"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:Controls="clr-namespace:WpfApplication2.Controls"
Title="Window2" Height="300" Width="300">
<Grid>
<Controls:BindableRichTextBox Background="Red"
VerticalScrollBarVisibility="Auto"
HorizontalScrollBarVisibility="Auto"
FontSize="13"
Margin="4,4,4,4"
Grid.Row="0" />
</Grid>
</Window>
Open child window 2, close this child window and in memory are still alive object of this child window also bindable richTextBox object.
I suspect that since instances of DependencyPropertyDescriptor
are likely to be cached at application level, the references to ValueChanged delegate (the anonymous delegate in OnInitialized
method) might leak instances of the BindableRichTextBox
.
In the code shown there isn't, indeed, any call to DependencyPropertyDescriptor.RemoveValueChanged
to remove the handler.
You might consider using the DependencyProperty.Register
overload, which supports a PropertyMetadata
parameter; this allows you to specify a proper PropertyChangedCallback for the property (see http://msdn.microsoft.com/en-us/library/cc903933(v=VS.95).aspx#metadata):
public static readonly DependencyProperty DocumentProperty =
DependencyProperty.Register("Document",
typeof(FlowDocument), typeof(BindableRichTextBox),
new PropertyMetadata(null,
new PropertyChangedCallback(OnDocumentChanged)
)
);
public static void OnDocumentChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
((BindableRichTextBox)d).SetBaseDocument((FlowDocument)e.NewValue);
}
public new FlowDocument Document
{
get { return (FlowDocument)GetValue(DocumentProperty); }
set { SetValue(DocumentProperty, value); }
}
private SetBaseDocument(FlowDocument document) {
base.Document = (FlowDocument)GetValue(DocumentProperty);
}
Maybe is better to create user control with richTextBox. Richtextbox wpf binding
精彩评论