可绑定的 richTextBox 仍挂在内存中 {WPF, Caliburn.Micro}

发布于 2024-10-11 10:26:44 字数 7708 浏览 7 评论 0原文

我在 WFP Caliburn.Micro 框架中使用。 我需要可绑定的 richTextbox 作为 Document 属性。我发现了很多方法如何绑定 richTextBox。

但我有一个问题。从父窗口我打开子窗口。子窗口包含可绑定的 richTextBox 用户控件。

在我关闭子窗口并使用内存探查器视图类和 bindabelrichTextBox 控件之后,视图模型类仍然挂在内存中。 ->这会导致内存泄漏。

如果我使用 .NET Framework 中的 richTextBox 或扩展 WPF 工具包中的 richTextBox,则不会导致此内存泄漏问题。

我无法识别可绑定 richTextBox 类中的问题。

以下是可绑定 richTextBox 的 ist 类:

基类可以来自 .NET 或扩展工具包。

  /// <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 another <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); }
        }

    }

感谢您的帮助和建议。

Qucik 示例:

带有 .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>

我从父窗口打开此窗口:

        var w = new Window1();
        w.Show();

然后关闭此窗口,使用内存分析器检查,内存中不存在 window1 - richTextBox 的任何对象。没关系。

但后来我尝试可绑定的richTextBox:

子窗口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>

打开子窗口2,关闭该子窗口,并且在内存中该子窗口的对象仍然存在,也是可绑定的richTextBox对象。

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 another <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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

追风人 2024-10-18 10:26:44

我怀疑,由于 DependencyPropertyDescriptor 的实例可能会在应用程序级别缓存,因此对 ValueChanged 委托(OnInitialized 方法中的匿名委托)的引用可能会泄漏 >BindableRichTextBox

在显示的代码中,确实没有调用 DependencyPropertyDescriptor.RemoveValueChanged 来删除处理程序。

您可以考虑使用DependencyProperty.Register重载,它支持PropertyMetadata参数;这允许您为属性指定正确的 PropertyChangedCallback (请参阅 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);
}

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);
}
野侃 2024-10-18 10:26:44

也许使用 richTextBox 创建用户控件更好。
Richtextbox wpf 绑定

Maybe is better to create user control with richTextBox.
Richtextbox wpf binding

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文