I have an WPF form that I myself did not create, so I am not very good at WPF. It is leaking badly though, up to 400 MB and closing the form does not help.
The problem lies in my application loading all the pictures at once. I would like to only load the ones visible at the moment. It is about 300 pictures and they are a bit large so my WPF-form suffers from loading them all.
I have a DataTemplate
with my own type that has a property Thumbnail
. The code in the template is like this:
<Image Source="{Binding Path=Thumbnail}" Stretch="Fill"/>
And then I have a grid with a control that has the above template as source. The code for this control is the below. Please provide me with hints on how to optimize the code and perhaps get the only ones that are visible and only have that many controls loaded at the same time?
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Controls:ElementFlow">
<Grid Background="{TemplateBinding Background}">
<Canvas x:Name="PART_HiddenPanel"
IsItemsHost="True"
Visibility="Hidden" />
<Viewport3D x:Name="PART_Viewport">
<!-- Camera -->
<Viewport3D.Camera>
<PerspectiveCamera FieldOfView="60"
Position="0,1,4"
LookDirection="0,-1,-4"
UpDirection="0,1,0" />
</Viewport3D.Camera>
<ContainerUIElement3D x:Name="PART_ModelContainer" />
<ModelVisual3D>
<ModelVisual3D.Content>
<AmbientLight Color="White" />
</ModelVisual3D.Content>
</ModelVisual3D>
<Viewport2DVisual3D
RenderOptions.CachingHint="Cache"
RenderOptions.CacheInvalidationThresholdMaximum="2"
RenderOptions.CacheInvalidationThresholdMinimum="0.5"/>
</Viewport3D>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
It is hard to narrow down the problem with just your code snippet. You might want to Visual Profiler if you haven't already.
The first place to look at when you're trying to find memory leaks in a .NET application, WPF or not, is objects that subscribe to events.
If object X is listening to an event raised by object Y, then Y holds a reference to X. Whatever virtualization (or disposal) method you implement, if X doesn't unsubscribe from Y's event, X will stay in the object graph as long as Y does, and will never get finalized and garbage-collected. (Even if it implements
IDisposable
and you explicitly callDispose
on it.)When you say "closing the form does not help," that makes me doubly suspicious: I'd expect that someone's implemented an object property on the
Window
object, and that object has subscribed to an event of some kind. So you close the window, but it still exists in the object graph because one of its properties is being referenced.(To give you an idea of how insidious this can be: WinForms
ToolStrip
controls subscribe to Windows theme-change events when they become visible. This is great in that changing your computer's theme is automagically reflected in the UI of your running application. It is not so great in that if you dereference aToolStrip
without first settingVisible
to false, it will continue receiving theme-change events until the application terminates.)A memory profiler can help with this - that's how I discovered that my application had thousands of
ToolStrip
objects in memory even though I thought all of them had been destroyed.Is the 'ElementFlow' control the same one described here? It appears that control is already using virtualization, so I wouldn't expect it to access the Thumbnail property of a non-visible item.
How are you modelling the data structure that exposes the 'Thumbnail' property? Can you set it up such that the property demand-loads the thumbnail on first access? Perhaps implementing this with a backing cache (that keeps the thumbnails loaded for a period of time) would address the issue.
EDIT
I may have assumed something I should not have. In reading the comments to the second post I linked, I now think it may be that the publicly available version of the ElementFlow control does not, in fact, implement virtualization. Perhaps you could log access to the 'Thumbnail' property and determine if the property is accessed for non-visible elements.