Problem

Cleanup() doesn't dispose accessors if property grid never loaded

Posted 5 months ago by Avatar Phil Devaney

Our app has a property grid in a tab control, with other controls in other tabs. Recently we added code to remember which tab was selected when the user closes the window containing the tab control, and restore selection next time it is opened. This seems to be causing some memory leaks because PropertyDescriptor.RemoveValueChanged is not being called.

I think the root cause of this is PropertyGrid.Cleanup is not calling Dispose on the custom property accessors returned by our custom factory. This only happens if the tab containing the property grid is never shown - i.e. it is not the initially selected tab, and the user doesn't switch to the tab before closing the window. The factory's GetProperties method is still called even if the property grid isn't shown. I've confirmed Cleanup is definitely being called, it looks like it iterates around this.Items to dispose them, but the collection is empty.

We have a workaround for this by only adding the SelectedObject binding to the property grid when its tab is first shown.

I've sent a sample to the support address. This has two buttons that show a second window with the grid in a tab that is selected or not, then does a GC.Collect when the window is closed. There is an Assert(disposing) in the accessor's Dispose override. If the window is shown with the grid unselected and closed without selected it, the assert fires.


Comments (5)

Posted 5 months ago by Actipro Software Support - Cleveland, OH, USA

Hi Phil,

Thank you for the sample.  The problem seems to be that the PropertyGrid.ItemContainerGenerator hasn't run yet since the control wasn't displayed.  That means that the PropertyGridDataAccessorItem that it would generate and that would walk the data accessors to dispose them when the root Cleanup() method was called hasn't been created.  There's some complex code in PropertyGridDataAccessorItem and its DisposableListCollectionView that knows how to do this.  Without those PropertyGridDataAccessorItem containers being generated though, there's not a good way to walk the data accessors (particularly in nested property scenarios) to properly dispose the data accessors.

For the current time I might suggest that you continue doing your workaround of only setting the SelectedObject binding to the property grid when its tab is first shown.

We are currently rewriting PropertyGrid from scratch to make it much faster and simpler internally.  This is something we will look into as we develop that out.  We're actually getting into the attaching value changed handlers, etc. in the next day or two, so this is good timing.


Actipro Software Support
Posted 5 months ago by Phil Devaney

Thanks, we can live with the workaround as it doesn't cause any other problems.

Interestingly, this only happens on one of our two editors that use the property grid. For the other, to improve performance when changing selection we implemented a proxy/wrapper object that uses ICustomTypeDescriptor to forward the properties of the real target object. When the selection changes to an object of the same type, instead of changing SelectedObject and causing the visual tree to be rebuilt, we changing the target of the proxy and just raise PropertyChanged on all its properties. A SelectedObjectProxy attached property handles this update - for some reason binding to this instead of SelectedObject means the Items property gets populated even if the grid isn't shown. The proxy isn't suitable for the other editor as the objects shown have dynamic properties, hence the workaround

Posted 5 months ago by Actipro Software Support - Cleveland, OH, USA

Hi Phil,

The good news is that in the new PropertyGrid we're building, the control loads almost instantly so far when selecting objects.  Thus you shouldn't have to do anything special like the proxy to work around possible slowness in selecting an object.

In this new PropertyGrid, we worked yesterday on making all of the models IDisposable.  Any time there is a change in the root object being displayed by the PropertyGrid, it will run through the tree of loaded models and dispose them as appropriate.  This is regardless of whether an ItemsContainerGenerator has run yet or not.


Actipro Software Support
Posted 5 months ago by Phil Devaney

We've found another scenario where the accessors don't get disposed properly. This is partially due to a bug in our code, but I thought you'd want to know so you can make sure its not a problem in the new property grid.

The scenario is when we execute a command that causes SelectedObject to change twice to two different objects without returning to the Dispatcher loop in between. This means the Action in UpdateItemsSourceWorker doesn't get chance to run for the first selection, but it doesn't Dispose the accessors that were created.

We have a few potential workarounds

  • Fix our code so it doesn't change selection twice
  • Do the selection change in a Dispatcher.BeginInvoke with lower priority than DataBind, so UpdateItemsSourceWorker has chance to run
  • Set IsAsynchronous="False" on the property grid

Let me know if you want a sample for this.

I know you just did a mainteance release yesterday, are you planning another before 2017.1?

Posted 5 months ago by Actipro Software Support - Cleveland, OH, USA

Hi Phil,

The setup in the new PropertyGrid is completely different (much simpler and faster) and doesn't do any of the dispatcher sort of work the old version did.  Based on the design of the new PropertyGrid thus far, I don't think you'd have this sort of issue there.

Yes we will likely have other maintenance releases before the end of the year, but they may or may not have an alpha of the new PropertyGrid in them.  We can get you private builds of the new control via Slack though once we're a bit further along.


Actipro Software Support
Information The latest build of this product (2016.1 build 0635) was released 1 month ago, which was after the last post in this thread.

Add a Comment

Please log in to a validated account to post comments.