Утечка памяти на CollectionView.View.Refresh - PullRequest
3 голосов
/ 12 марта 2010

Я определил свою привязку следующим образом:

<TreeView
        ItemsSource="{Binding UsersView.View}"
        ItemTemplate="{StaticResource MyDataTemplate}"
/>

CollectionViewSource определяется следующим образом:

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);

Пока все хорошо, все работает, как и ожидалось: представление показывает сначала пользователей, которые вошли в алфавитном порядке, а затем тех, кто не является.

Однако свойство IsLoggedIn в UserData обновляется каждые несколько секунд потоком фонового работника, а затем код вызывает:

UsersView.View.Refresh();

в потоке пользовательского интерфейса.

Опять же, это работает как ожидалось: пользователи, которые входят в систему, перемещаются из нижней части представления в верхнюю и наоборот. Однако: Каждый раз, когда я вызываю метод Refresh в представлении, приложение накапливает 3,5 МБ дополнительной памяти, которая освобождается только после завершения работы приложения (или после исключения OutOfMemoryException ...)

Я провел некоторое исследование, и ниже приведен список исправлений, которые НЕ работали:

  • Класс UserData реализует INotifyPropertyChanged
  • Изменение базовой коллекции пользователей не имеет никакого значения: любая IENumerable <UserData> как источник для CollectionViewSource вызывает проблему.
    -Изменение ColletionViewSource в List <UserData> (и обновление привязки) или наследование от ObservableCollection для получения доступа к базовой коллекции Items для сортировки на месте не работает.

У меня нет идей! Помощь

EDIT: Я нашел это: Resource MyDataTemplate содержит метку, которая привязана к объекту UserData для отображения одного из его свойств, объекты UserData передаются из TreeS в ItemsSource. Метка имеет ContextMenu, определенное таким образом:

 <ContextMenu Background="Transparent" Width="325" Opacity=".8" HasDropShadow="True">

      <PrivateMessengerUI:MyUserData IsReadOnly="True" >

          <PrivateMessengerUI:MyUserData.DataContext>

              <Binding Path="."/>

          </PrivateMessengerUI:MyUserData.DataContext>

     </PrivateMessengerUI:MyUserData>

</ContextMenu>

Объект MyUserData - это UserControl, который показывает все свойства объекта UserData. Таким образом, пользователь сначала видит только один фрагмент данных пользователя, а при щелчке правой кнопкой мыши видит их все.

Когда я удаляю MyCserData UserControl из DataTemplate, утечка памяти исчезает! Как я все еще могу реализовать поведение, как указано выше?

Ответы [ 2 ]

2 голосов
/ 18 марта 2010

Первый шаг в устранении утечек памяти - найти точный источник. Это не всегда очевидно и может иногда бросать вызов вашей интуиции. Исходя из вашего объяснения, если вы удалите свой пользовательский элемент управления, проблема исчезнет, ​​но когда вы вернете ее обратно, вы начнете течь снова. Это очень вероятно указывает на утечку памяти в этом элементе управления (хотя и не обязательно). Возможно, ваш элемент управления вписывается в один из многих типов утечек памяти WPF , или у вас есть более классическая проблема подписки на событие, но не правильного подключения к ней, когда она больше не нужна ( шаблон слабого события полезно здесь).

Лучше всего взять такой инструмент, как .NET Memory Profiler или ANTS Memory Profiler (оба превосходны и имеют бесплатные пробные версии). Используйте один из этих инструментов, чтобы найти объекты, которые висят вокруг после того, как они должны исчезнуть. Эти инструменты помогают отслеживать цепочку объектов, висящих на вашем объекте. Есть много хороших статей о профилировании памяти на их сайтах , здесь на SO и в широко открытой сети.

1 голос
/ 19 марта 2010

Вы можете попробовать 2 вещи:

Прежде всего, у DropShadow есть некоторые проблемы, так как он содержит некоторые ссылки, которые должны быть собраны garbadge. См. Эту статью для получения дополнительной информации: http://blog.ramondeklein.nl/index.php/2009/02/20/memory-leak-with-wpf-resources-in-rare-cases/

Вы можете попытаться установить для th HasDropShadow значение false и посмотреть, что происходит с вашей памятью.

Во-вторых, если у нас есть несколько объектов ContextMenu одного и того же меню, вы можете создать ресурс ContextMenu в своих ресурсах и ссылаться на него с расширением StaticResource следующим образом:

<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>

И используйте его там, где вы определили свое контекстное меню:

ContextMenu="{StaticResource MyAwesomeContextMenu}"

Надеюсь, это немного поможет!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...