Memory leak on CollectionView.View.Refresh
I have defined my binding thus:
<TreeView
ItemsSource="{Binding UsersView.View}"
ItemTemplate="{StaticResource MyDataTemplate}"
/>
The CollectionViewSource is defined thus:
private ObservableCollection<UserData> users;
public CollectionViewSource UsersView{get;set;}
UsersView=new CollectionViewSource{Source=users};
UsersView.SortDescriptions.Add(
new SortDescription("IsLoggedOn",ListSortDirection.Descending);
UsersView.SortDescriptions.Add(
new SortDescription("Username",ListSortDirection.Ascending);
So far, so good, this works as expected: The view shows first the users that are logged on in alphabetical order, then the ones that are not.
However, the IsLoggedIn property of the UserData is updated every few seconds by a backgroundworker thread and then the code calls:
UsersView.View.Refresh();
on the UI thread.
Again this works as expected: users that log on are moved from the bottom of the view to the top and vice versa. However: Every time I call the Refresh method on the view the application hoards 3,5MB of extra memory, which is only released after application shutdown (or after an OutOfMemoryException...)
I did some research and below is a list of fixes that did NOT work:
- The UserData class implements INotifyPropertyChanged
- Changing the underlying users collection does not make any difference at all: any IENumerable
<UserData
> as a source for the CollectionViewSource causes the problem. -Changing the ColletionViewSource to a List<UserData
> (and refreshing the binding) or inheriting from ObservableCollection to get access to the underlying Items collection to sort that in place does not work.
I am out of ideas! Help?
EDIT: I found it: The Resource MyDataTemplate contains a Label that is bound to a UserData object to show one of开发者_C百科 its properties, the UserData objects being handed down by the TreeView's ItemsSource. The Label has a ContextMenu defined thus:
<ContextMenu Background="Transparent" Width="325" Opacity=".8" HasDropShadow="True">
<PrivateMessengerUI:MyUserData IsReadOnly="True" >
<PrivateMessengerUI:MyUserData.DataContext>
<Binding Path="."/>
</PrivateMessengerUI:MyUserData.DataContext>
</PrivateMessengerUI:MyUserData>
</ContextMenu>
The MyUserData object is a UserControl that shows All properties of the UserData object. In this way the user first only sees one piece of data of a user and on a right click sees all of it.
When I remove the MyUserData UserControl from the DataTemplate the memory leak disappears! How can I still implement the behaviour as specified above?
Step one in troubleshooting memory leaks is to find the definitive source. This is not always obvious and can occasionally defy your intuition. Based on your explanation, if you remove your user control, the issue disappears, but when you put it back, you start leaking again. This very likely points to a memory leak within that control (though not necessarily). Perhaps your control fits into one of the many types of WPF memory leaks, or you have a more classic problem of subscribing to an event but not properly unwiring it when no longer needed (the weak event pattern is useful here).
Your best bet is to grab a tool like .NET Memory Profiler or ANTS Memory Profiler (both are excellent and have free trials). Use one of these tools find the objects that are hanging around after they should be gone. These tools provide help in tracing the chain of objects that are hanging onto your object. There are many good articles on memory profiling on their sites, here on SO and on the wide open web.
You could try 2 things:
First of all the DropShadow has some problems, as it hold some references that should be garbadge collected.. see this article for more information: http://blog.ramondeklein.nl/index.php/2009/02/20/memory-leak-with-wpf-resources-in-rare-cases/
You can try to set th HasDropShadow to false, and see what happens with your memory.
Secondly, if we have multiple ContextMenu objects of the same menu, you might want to create a ContextMenu resource in your Resources, and reference it with the StaticResource extension like so:
<ContextMenu Background="Transparent" Width="325" Opacity=".8" x:Key="MyAwesomeContextMenu">
<PrivateMessengerUI:MyUserData IsReadOnly="True" >
<PrivateMessengerUI:MyUserData.DataContext>
<Binding Path="."/>
</PrivateMessengerUI:MyUserData.DataContext>
</PrivateMessengerUI:MyUserData>
</ContextMenu>
And use it where you defined your context menu:
ContextMenu="{StaticResource MyAwesomeContextMenu}"
Hope this helps a bit!
精彩评论