wpfperformancememory-leaksrenderslowdown

WPF View leak - invisible, but still rendered in the background


There is a complex App, I try to simplify the scenario. There is a host .exe (.NET), which contains lots of controls (ActiveX, .NET, WPF).
One control is basically a grid with items (call it "list"), and when new selection happens, it sends a message to another WPF control (call it "DataView"). "DataView" will display details of the current selection of "list".
When DataView receives that message, it will re-create it's ViewModel, and assign to its DataContext, so re-create its View. Its View is very complex (XAML declared), full of controls, templates, and also contains several Images (type: NonDPIImage, derived from Image, with a few basic non-important change, just consider it as Image), and it's Source is a Converter, which creates the BitmapImages.

<Image.Source>
    <MultiBinding Converter="{StaticResource ImageConverter}">
        ...

It works fine, but I noticed that after selection changes the "DataView" update is getting slower and slower.
I debugged, and found that after several selection changes, all previous Views are still in the memory, and all are rendering its content, so the ImageConverter is called for all previous Views, thus it's getting slower and slower.

I tried to profile, this is what I see after 10+ selection.

enter image description here

You see the previous Views are still in the memory (lower prio problem), with the Images, and those are still being rendered (high prio problem), making the App slower and slower.

I am not really familiar with WPF, I read after leaks (mostly not DependencyProperty is used or similar), but this control is so difficult that first I wand to quickly workaround, so prevent rendering the leaked Views, and later on investigate the memory issue. (of course both would be the best...)

I tried that before DataContext assigned to new value, set the current View Image.Source to null, so at least the leaked Image will not render itself, but that caused the new View(!) also to lose its Image.Source, looks like WPF is caching or sharing like some static data?

As my first prio is to stop the "invisible render", after this I tried to set some of the model properties to null before creating the new one (so it would still leak, but at least no render anymore), so when Converter will receive properties to create the image, will see it's null, and skip the render.
But it behaves very strange!
For the leaked instances the breakpoint is not hit in the properties_get code, like as WPF cached the values or so? This prevented me this path to continue.

Any help / idea would be appreciated guys.


Solution

  • I think I found the issue: there was a message hook added to the View HwndSource (AddHook()) , but was not removed. This kept the whole View (see the red rectangled class) alive. Now if I call MyHwndSource.RemoveHook(WndProc) in the UserControl_Unloaded, the View will be also GCd.