In the constructor of XamPropertyGrid, it looks like there is a call to add an event handler to a static event (TypeDescriptor.Refreshed).
IE. I can put a breakpoint in the debugger and I see this method call:
System.ComponentModel.TypeDescriptor.add_Refreshed
There is never an equivalent call to release/unsubscribe from that event handler.
The result is that my app has a very rapid leak. Below is an image of the path to the rooted reference. I suppose I may have to do manual cleanup to remove the XamPropertyGrid control from my ProductDetailsControl as a workaround. But even then it will still have a leak of the XamPropertyGrid instances themselves...
Can anyone suggest any easy workarounds?
Hello David,
Thank you for your update. I have recently answered the internal support case that we have together that is linked to development issue 262191. That response refers to this forum thread, and I will post a transcript of that response below:
"I am a little confused in this case, as on the forum thread that we have together, you mentioned you were able to come up with a workaround that disconnects the XamPropertyGrid from the parent ProductDetailsControl and now are seeing issues with a ScrollBarInfoMediator and a ScrollBarAutomationPeer. We have no control over the creation of the ScrollBarAutomationPeer, as this is likely being done by the vertical scrollbar, which is a WPF-built in ScrollBar element and it is creating its own automation peer.
I am currently working toward trying to get to the point that you are seeing, as I have tried to disconnect the XamPropertyGrid from the parent ProductDetailsControl, but I am still seeing the same root in memory – that is, the RefreshEventHandler for TypeDescriptor.Refreshed.
That said, while I have not been able to test things out on my end to see if this would work, I did come up with a way that you can get to the ScrollBarInfoMediator and set it to null. It is hacky and I was only able to test this such that it doesn’t throw an exception when run, but it may help you to disconnect the ScrollBarInfoMediator, where “propGrid” is your local XamPropertyGrid:
FieldInfo info = typeof(XamPropertyGrid).GetField("_scrollBarInfoMediator", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
var x = info.GetValue(propGrid); info.SetValue(propGrid, null);"
In the private support case, I attached a modified version of the sample project that you had provided. I won’t be attaching it here (yet) in the case that you do not want it publicly visible.
Please let me know if you have any other questions or concerns on this matter.
Sincerely, Andrew Goldenbaum Software Developer
Hi Andrew, I had started down the path you described. Now my path to root is still related to automation, but it appears to be slightly different in that it is related to the scrollbar.
See screenshot. Note that the "XamFastPropertyGrid" is basically a XamPropertyGrid whose OnCreateAutomationPeer returns null.
This UI automation stuff is very troublesome. I wish you could tell me how to override the behavior so that all controls in my application would ignore it altogether.
Any ideas how I can break apart these new paths to root that pass thru the "ScrollBarInfoMediator" and the "ScrollBarAutomationPeer"?
Thank you for your update on this matter. It makes sense that you want to make mention of this and have it resolved out in the open.
Regarding actually accessing the XamPropertyGridAutomationPeer, I’m unsure how you can actually access these, as there isn’t a collection that I can see, and the XamPropertyGridAutomationPeer is effectively just a FrameworkElementAutomationPeer. As such, if there is a way to get the automation peers from a simple FrameworkElement, I would expect you could get it from the XamPropertyGrid the same way.
I believe the best thing I could recommend in this case if you don’t actually need the automation and want to shut it off, you could derive a custom XamPropertyGrid and override OnCreateAutomationPeer and return null from this method. This should remove it from memory completely, as they simply won’t be created, and it would resolve this issue you are seeing.
I tried to mitigate the leak by changing the custom code to remove the XamPropertyGrid from the parent control (ProductDetailsControl). That would happen whenever the ProductDetailsControl was being destroyed. That went to production this weekend. But it only releases the rooted path in certain scenarios. I'm not sure what the pattern is between the scenarios where my ProductDetailsControls are GC'ed and the scenarios where they are not. Out of 800 rooted XamPropertyGrids, I can get 600 related PropertyDetailsControls to be GC'ed by removing the XamPropertyGrid from the ProductDetailsControl. But in the case of the remaining 200, there is no GC and I still have a rooted reference.
In the case of the 600, the system allows the GC to collect the ProductDetailsControls because the paths to root always/only look like the one I had shown earlier.
But in the case of the 200 remainint, the GC is still not able to collect the ProductDetailsControls. For these ones, the paths to root are formatted a bit differently. Please see the following image. It has a different path to root whereby the ProductDetailsControl is referenced via a XamPropertyGridAutomationPeer (whatever that is).
Can someone tell me how I can access the XamPropertyGridAutomationPeer and cause it to release the references to the ProductDetailsControl?
Here is the code I have created so far (ProductDetailsControl code-behind) that was intended to detach from XamPropertyGrid and cause the ProductDetailsControl to be GC'ed (even if the XamPropertyGrid cannot).
public void OnNavigatedFrom(NavigationContext p_NavigationContext) { if(!(m_PropertyGrid.Parent is Grid)) { return; } if(!object.ReferenceEquals(m_PropertyGrid.Parent, m_ThisControlGrid)) { return; } // Clear out the properties; // Make life easy for GC. m_PropertyGrid.SelectedObject = null; m_PropertyGrid.RegenerateProperties(); m_PropertyGrid.UpdateLayout(); // Remove the XamPropertyGrid from it's parent. m_ThisControlGrid.Children.Remove(m_PropertyGrid); // Null things out; and make life easy for GC. m_PropertyGrid.SelectedObject = null; m_PropertyGrid.Resources = null; m_PropertyGrid = null; }
Please let me know how to remove the XamPropertyGridAutomationPeer from by path to root. Any help would be greatly appreciated. Note that this is all being done as a workaround and I expect that the "right" fix will be done within the XamPropertyGrid so that it doesn't permanently get rooted by subscribing to the "TypeDescriptor.Refreshed" event.
Yes, thanks Andrew. I wanted to also post the problem (and eventual resolution) in the open forum. I didn't find any mention of it yet, and this issue would presumably affect every WPF customer using the XamPropertyGrid.
I always like to see problems being solved out in the open. If a google search can give a quick answer to a problem like this one, then it is easy to save some other developer a lot of time (days/weeks) that they might otherwise be interacting with tech support.
... I know there have been many times that I've been able to benefit from public information about well-known bugs (whether in third-party forums or KB's or blogs or whatever).