Problem

RibbonEditorStyleBehavior causes memory leak

Posted 4 years ago by Avatar Jon Cain - Software Architect, AutoMon Corporation
Using RibbonEditorsStyleBehavior.ApplyRibbonCompatibleStyles="True" causes a child window to remain in memory forever, i.e., it's never garbage collected.

Here is some sample code that demonstrates the problem:

Window1.xaml -> "Main Window"
<Window 
  x:Class="RibbonEditorMemoryLeak.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>
    <Grid.RowDefinitions>
      <RowDefinition Height="Auto" />
      <RowDefinition Height="Auto" />
      <RowDefinition Height="*" />
    </Grid.RowDefinitions>
    <Button Click="OpenChildWindow_Click">Open Child Window</Button>
    <Button Grid.Row="1" Click="GarbageCollect_Click">Garbage Collect</Button>
    <TextBlock Grid.Row="2" FontWeight="Bold">Watch Debug Output for messages.</TextBlock>
  </Grid>
</Window>
Window1.xaml.cs
using System;
using System.Diagnostics;
using System.Windows;

namespace RibbonEditorMemoryLeak
{
   public partial class Window1 : Window
   {
      public Window1()
      {
         InitializeComponent();
      }

      private void OpenChildWindow_Click(object sender, RoutedEventArgs e)
      {
         Debug.WriteLine("OpenChildWindow_Click() -> Opening Child Window");
         new RibbonWindow1().Show();
      }

      private void GarbageCollect_Click(object sender, RoutedEventArgs e)
      {
         Debug.WriteLine("GarbageCollect_Click()");
         GC.Collect();
         GC.WaitForPendingFinalizers();
         GC.Collect();
      }
   }
}
RibbonWindow1.xaml -> "Child Window"
<ribbon:RibbonWindow 
  x:Class="RibbonEditorMemoryLeak.RibbonWindow1"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:shared="http://schemas.actiprosoftware.com/winfx/xaml/shared" 
    xmlns:ribbon="http://schemas.actiprosoftware.com/winfx/xaml/ribbon" 
  xmlns:ribboneditors="http://schemas.actiprosoftware.com/winfx/xaml/ribboneditors"
  ApplicationName="RibbonWindow1" Width="620" Height="420"
  ribboneditors:RibbonEditorsStyleBehavior.ApplyRibbonCompatibleStyles="True">
    <Grid>
    <TextBlock TextWrapping="WrapWithOverflow">
        This window will not get Garbage Collected because RibbonEditorsStyleBehavior.ApplyRibbonCompatibleStyles="True"
    </TextBlock>
  </Grid>
</ribbon:RibbonWindow>
RibbonWindow1.xaml.cs
using System.Diagnostics;

namespace RibbonEditorMemoryLeak
{

   public partial class RibbonWindow1 : ActiproSoftware.Windows.Controls.Ribbon.RibbonWindow
   {
      public RibbonWindow1()
      {
         InitializeComponent();
         Debug.WriteLine("RibbonWindow1() -> constructed");
      }

      ~RibbonWindow1()
      {
         Debug.WriteLine("~RibbonWindow1() -> deconstructed");
      }
   }
}

Comments (6)

Posted 4 years ago by Actipro Software Support - Cleveland, OH, USA
Hi Jon,

It looks like the WPF ResourceDictionary is maintaining a reference to the first instance of your child window. Really, it could be any object but is the first element that the ApplyRibbonCompatibleStyles to set to true on. If you open and close several child windows, only the first one is retained.

You could manually clear the ApplyRibbonCompatibleStyles property on the Window, like so:
public Window2() {
    InitializeComponent();
    this.Unloaded += new RoutedEventHandler(Window2_Unloaded);
}

void Window2_Unloaded(object sender, System.Windows.RoutedEventArgs e) {
    RibbonEditorsStyleBehavior.SetApplyRibbonCompatibleStyles(this, false);
}
Unfortunately, there's a bug in WPF that was fixed in .NET 4.0, that prevents this from working in .NET 3.5 SP1 or earlier. You can find more information on this here.

To get around this completely, for all versions of .NET, you can manually merge a new instance of the associated ResourceDictionary. Something like this would work the same as setting ApplyRibbonCompatibleStyles (which is just there for convenience), but doesn't reuse the same instance.
<ribbon:RibbonWindow.Resources>
    <ResourceDictionary>
        <ResourceDictionary.MergedDictionaries>
            <ResourceDictionary Source="/ActiproSoftware.Editors.Interop.Ribbon.Wpf351;component/Windows.Controls.Editors.Interop.Ribbon/RibbonEditorsResourceDictionary.xaml" />
        </ResourceDictionary.MergedDictionaries>
    </ResourceDictionary>
</ribbon:RibbonWindow.Resources>

Actipro Software Support
Posted 4 years ago by Jon Cain - Software Architect, AutoMon Corporation
Do you know when there will be a 4.0 version of Actipro Studio?
Posted 4 years ago by Actipro Software Support - Cleveland, OH, USA
Hi Jon,

We target 3.5 sp1 and don't have any iummediate plans to target 4.0 because we need to support the older framework for a while for other customers.

Regardless, as long as your application is configured to target .NET 4.0, then our assemblies will run on that version also. And, the ResourceDictionary bug mentioned above with not be present in your application or our assemblies.

Actipro Software Support
Posted 4 years ago by Jon Cain - Software Architect, AutoMon Corporation
Either it's not fixed in .NET 4.0 or there's a different problem because I still experience the problem when targeting the 4.0 framework.
Posted 4 years ago by Actipro Software Support - Cleveland, OH, USA
Hi Jon,

Just to be clear, I meant that reseting ApplyRibbonCompatibleStyles to false would only work in .NET 4.0.

So to use the ApplyRibbonCompatibleStyles with secondary windows (i.e. not a window that is open as long as the application is running), you would need to add the Unloaded event handler, reset the ApplyRibbonCompatibleStyles property to false in the handler, and target .NET 4.0.

If this is what your sample is doing, please send along your sample project (sans any executable files) and we can see what's different from our sample.

Actipro Software Support
Posted 4 years ago by Jon Cain - Software Architect, AutoMon Corporation
I understand now. The workaround works. Thanks.
Information The latest build of this product (2014.1 build 0601) was released 28 days ago, which was after the last post in this thread.

Add a Comment

Please log in to a validated account to post comments.