【问题标题】:Bindable richTextBox still hanging in memory {WPF, Caliburn.Micro}可绑定的richTextBox 仍然挂在内存中 {WPF, Caliburn.Micro}
【发布时间】:2011-06-05 17:23:00
【问题描述】:

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

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

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

如果我使用 .NET Framework 中的 richTextBox 或 Extended WPF Toolkit 中的richTextBox,则不会导致此内存泄漏问题。

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

这是可绑定的richTextBox的一个类:

基类可以来自 .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); }
        }

    }

感谢您的帮助和建议。

快速示例:

带有 .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对象。

【问题讨论】:

    标签: wpf binding memory-leaks richtextbox flowdocument


    【解决方案1】:

    我怀疑由于 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);
    }
    

    【讨论】:

    • 阿门多拉先生您好。首先感谢您的回答和建议。我是 WPF 世界的新手,这是我仍在学习的第二个 WPF 应用程序;)。我有点困惑。因为不知道 BindableRichTextBox 类在哪里使用 DependencyPropertyDescriptor.RemoveValueChanged 的​​好地方。
    • 这就是重点:(据我所知)确保始终调用 RemoveValueChanged 并没有什么好处;出于这个原因,我更喜欢使用 PropertyMetadata 来指定 PropertyChangedCallback,这是处理依赖属性的预期方式。我会在之前的回复中添加一个小sn-p。
    • 谢谢阿门多拉先生,您只有一个错误,我更正了。 base.Document = (FlowDocument)GetValue(DocumentProperty);
    【解决方案2】:

    也许用richTextBox 创建用户控件会更好。 Richtextbox wpf binding

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-02-04
      • 2010-09-25
      • 2013-07-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多