
I’m having a problem with memory cleanup when using some Actipro editor controls and Actipro ribbon split buttons.
Background:
I have a WPF application that uses projects, similar to Visual Studio. Each project is represented by a ProjectViewModel object, which holds collections of other ViewModels, which in turn hold collections to other ViewModels, etc…
When a user opens a project, they can double click on items to open editors, which allow the user to edit information in the items. Each editor inherits from WPF Page and a NavigatorFrame is used to display a Page and navigate to other pages.
Scenario:
The trouble is that when a user closes a project, we expect the ProjectViewModel, and the ViewModels referencing it, to be freed by the garbage collector. However, there are many controls that use WPF dependency properties which hold references to our ViewModels, and don’t release those references when expected. Note that we expect to be able to leave our editors in memory (since databinding should allow us to reuse the same editor instance if the user opens another project). Thus, on project close, we’ve decided to call cleanup functions on our editors in order to clear DependencyProperties that are holding references.
When doing this, we’re seeing that some controls still don’t clear their internal DependencyProperties, which has forced us to use unpleasant reflection code to clear those values. Along with being annoying to maintain, it’s difficult to know for certain if we’ve found everything. Our hope is that you can show us a better way to solve this, or to integrate our fixes into your controls so other users can benefit and so we don’t need to maintain these.
The Specifics:
1. Editors
We use several editor controls in our editors, including:
• FontFamilyComboBox
• FontSizeComboBox
• ForeColorEditBox
• BackColorEditBox
When we close our project and look at what’s keeping our ProjectViewModel in memory (using the ANTS memory profiler), we see that the above controls continue to hold references to a child ViewModel of our project.
For the FontFamilyComboBox, the reference chain is as follows:
1. FontFamilyComboBox has a visual child that’s a Popup.
2. Within Popup, there’s a _popupRoot.Value expression. (note that _popupRoot is private)
3. The Value property is a DependencyObject and its DataContext property holds the ViewModel reference.
The other controls listed above have the same issue. Is there a more direct way to break this reference or a function we can call that will handle this for us?
2. Ribbon SplitButtons
For ribbon split buttons, the problem is nastier. It seems that every SplitButton we use has the same Popup problem as the aforementioned Editors. However, cleanup is nastier since the reference is held after switching tabs. Thus, we have to iterate over all the SplitButtons in all our tab groups to ensure no references are accidently held.
As a reference, I’ve included our cleanup logic below to help make our approach clear. Again, we’d prefer to not have to rely on reflection and repeated cleanup checks to free these unexpected references. Is there anything we can do?
Thanks,
-Craig
Background:
I have a WPF application that uses projects, similar to Visual Studio. Each project is represented by a ProjectViewModel object, which holds collections of other ViewModels, which in turn hold collections to other ViewModels, etc…
When a user opens a project, they can double click on items to open editors, which allow the user to edit information in the items. Each editor inherits from WPF Page and a NavigatorFrame is used to display a Page and navigate to other pages.
Scenario:
The trouble is that when a user closes a project, we expect the ProjectViewModel, and the ViewModels referencing it, to be freed by the garbage collector. However, there are many controls that use WPF dependency properties which hold references to our ViewModels, and don’t release those references when expected. Note that we expect to be able to leave our editors in memory (since databinding should allow us to reuse the same editor instance if the user opens another project). Thus, on project close, we’ve decided to call cleanup functions on our editors in order to clear DependencyProperties that are holding references.
When doing this, we’re seeing that some controls still don’t clear their internal DependencyProperties, which has forced us to use unpleasant reflection code to clear those values. Along with being annoying to maintain, it’s difficult to know for certain if we’ve found everything. Our hope is that you can show us a better way to solve this, or to integrate our fixes into your controls so other users can benefit and so we don’t need to maintain these.
The Specifics:
1. Editors
We use several editor controls in our editors, including:
• FontFamilyComboBox
• FontSizeComboBox
• ForeColorEditBox
• BackColorEditBox
When we close our project and look at what’s keeping our ProjectViewModel in memory (using the ANTS memory profiler), we see that the above controls continue to hold references to a child ViewModel of our project.
For the FontFamilyComboBox, the reference chain is as follows:
1. FontFamilyComboBox has a visual child that’s a Popup.
2. Within Popup, there’s a _popupRoot.Value expression. (note that _popupRoot is private)
3. The Value property is a DependencyObject and its DataContext property holds the ViewModel reference.
The other controls listed above have the same issue. Is there a more direct way to break this reference or a function we can call that will handle this for us?
2. Ribbon SplitButtons
For ribbon split buttons, the problem is nastier. It seems that every SplitButton we use has the same Popup problem as the aforementioned Editors. However, cleanup is nastier since the reference is held after switching tabs. Thus, we have to iterate over all the SplitButtons in all our tab groups to ensure no references are accidently held.
As a reference, I’ve included our cleanup logic below to help make our approach clear. Again, we’d prefer to not have to rely on reflection and repeated cleanup checks to free these unexpected references. Is there anything we can do?
Thanks,
-Craig
public static void CleanupPopup(Visual popupParent)
{
var popup = GetVisualChild<Popup>(popupParent);
CleanupPopup(popup);
}
public static void CleanupPopup(Popup popup)
{
if (popup != null)
{
FieldInfo popupRootFieldInfo = typeof(Popup).GetField("_popupRoot", BindingFlags.Instance | BindingFlags.NonPublic);
Debug.Assert(popupRootFieldInfo != null, "The Popup's popupRoot field is missing.");
if (popupRootFieldInfo != null)
{
var popupRoot = popupRootFieldInfo.GetValue(popup);
if (popupRoot != null)
{
PropertyInfo valuePropertyInfo = popupRoot.GetType().GetProperty("Value", BindingFlags.Instance | BindingFlags.NonPublic);
Debug.Assert(valuePropertyInfo != null, "The PopupRoot's Value property is missing.");
if (valuePropertyInfo != null)
{
var value = valuePropertyInfo.GetValue(popupRoot, null) as DependencyObject;
if (value != null)
{
value.SetValue(FrameworkElement.DataContextProperty, FrameworkElement.DataContextProperty.DefaultMetadata.DefaultValue);
}
}
}
}
}
}