2014-03-06 60 views
-1

我已经构建了自己的控件来模仿Windows 8中的VariableSizedGridView。由于该控件不支持UI虚拟化,我使用虚拟化来重建它。控制基本上测量所有元素并计算它们的位置,但只绘制那些在视图中的元素。这使得Surface RT具有巨大的性能提升。Windows 8.1中的内存泄漏

虽然有一个问题,控件泄漏内存。我确信它是控件(或与之相关的东西),因为我已经能够在孤立的应用程序中重现泄漏。控件的源代码分为两类。 VirtualizedList类是控件的基础,并定义了几个DependencyProperties。 VirtualizedVariableSizedWrapGrid类是魔法发生的地方,以及屏幕上绘制物品的位置。

我不知道我该如何解决这个问题,因为它已经发生了好几个月了,而且我没有帮上什么忙。我检查了我的事件,试图处理UI元素(这看起来不可能)。我猜发生了泄漏,因为DataTemplate的一些子元素仍然绑定到数据对象,因此仍然引用它们,这就是UI对象仍然留在内存中的原因。但我可能完全错了。正如你在代码中看到的那样,当我更新视图时,我注释掉了RemoveChild(old);。这是因为当我重新使用旧的FrameworkElement(从DataTemplate创建)时,泄漏速度会变慢。当我一直重新创建新项目时,每个滚动操作的泄漏大约为5MB。

对此的任何帮助将非常多赞赏!谢谢。

PS:好像我无法附上所有源代码。 VirtualizedList类只定义了一些DependencyProperties,所以我会留下一个。

来源 VirtualizedVariableSizedWrapGrid

[TemplatePart(Name="Root", Type=typeof(Grid))] 
[TemplatePart(Name="Scroll", Type=typeof(ScrollViewer))] 
[TemplatePart(Name="LayoutArea", Type=typeof(Grid))] 
[TemplatePart(Name="SnappedView", Type=typeof(ListView))] 
public class VirtualizedVariableSizedWrapGrid : VirtualizedList 
{ 
    public static readonly DependencyProperty SnappedItemContainerStyleProperty = DependencyProperty.Register("SnappedItemContainerStyle", typeof(Style), typeof(VirtualizedVariableSizedWrapGrid), new PropertyMetadata(null, SnappedItemContainerStyleChanged)); 
    public static readonly DependencyProperty ListStateProperty = DependencyProperty.Register("ListState", typeof(ViewState), typeof(VirtualizedVariableSizedWrapGrid), new PropertyMetadata(ViewState.Full, ListStateChanged)); 

    private readonly List<object> _currentDataView; 
    private Grid _root; 
    private Panel _panel; 
    private ScrollViewer _scrollViewer; 
    private ListView _snappedView; 
    private const double COLUMNS_PRELOADED = 2; 
    private const double CARD_MARGIN = 6.0; 
    private int _maxRows = 0; 
    private object _selectedItem; 
    private bool _moreDataRequested = false; 

    public delegate void CalculatingItemSizeEventHandler(ItemContainer item); 
    public event CalculatingItemSizeEventHandler OnCalculatingItemSize; 
    public event SelectionChangedEventHandler SelectionChanged; 
    public event EventHandler DataRequested; 

    public enum ViewState 
    { 
     Full, 
     Snapped 
    } 

    public VirtualizedVariableSizedWrapGrid() 
    { 
     _currentDataView = new List<object>(); 
     DefaultStyleKey = typeof(VirtualizedVariableSizedWrapGrid); 
     this.Style = (Style) XamlReader.Load(
      @"<Style xmlns=""http://schemas.microsoft.com/winfx/2006/xaml/presentation"" xmlns:x=""http://schemas.microsoft.com/winfx/2006/xaml"" xmlns:controls=""using:MeTweets.Controls"" TargetType=""controls:VirtualizedVariableSizedWrapGrid""> 
       <Setter Property=""Template""> 
        <Setter.Value> 
         <ControlTemplate TargetType=""controls:VirtualizedVariableSizedWrapGrid""> 
          <Grid x:Name=""Root"" VerticalAlignment=""Stretch"" HorizontalAlignment=""Stretch""> 
           <ScrollViewer x:Name=""Scroll"" ZoomMode=""Disabled"" HorizontalScrollMode=""Auto"" VerticalScrollMode=""Disabled"" HorizontalScrollBarVisibility=""Hidden"" VerticalScrollBarVisibility=""Hidden"" HorizontalAlignment=""Stretch"" VerticalAlignment=""Stretch""> 
            <Grid x:Name=""LayoutArea"" /> 
           </ScrollViewer> 
           <ListView x:Name=""SnappedView"" HorizontalContentAlignment=""Stretch"" HorizontalAlignment=""Stretch"" /> 
          </Grid> 
         </ControlTemplate> 
        </Setter.Value> 
       </Setter> 
      </Style>"); 

     this.Loaded += VirtualizedVariableSizedWrapGrid_Loaded; 
    } 

    ~VirtualizedVariableSizedWrapGrid() 
    { 
     this.Loaded -= VirtualizedVariableSizedWrapGrid_Loaded; 
    } 

    #region Dependency Property Changed 
    private static void SnappedItemContainerStyleChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e) 
    { 
     VirtualizedVariableSizedWrapGrid varSizeGrid = sender as VirtualizedVariableSizedWrapGrid; 
     if (varSizeGrid == null || !(e.NewValue is Style)) 
      return; 

     varSizeGrid.SetupSnappedView(); 
    } 

    private static void ListStateChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e) 
    { 
     VirtualizedVariableSizedWrapGrid varSizeGrid = sender as VirtualizedVariableSizedWrapGrid; 
     if (varSizeGrid == null || e.NewValue.Equals(e.OldValue)) 
      return; 

     if (varSizeGrid.ListState == ViewState.Full) 
     { 
      varSizeGrid._scrollViewer.Visibility = Visibility.Visible; 
      varSizeGrid._snappedView.Visibility = Visibility.Collapsed; 
     } 
     else if (varSizeGrid.ListState == ViewState.Snapped) 
     { 
      varSizeGrid._scrollViewer.Visibility = Visibility.Collapsed; 
      varSizeGrid._snappedView.Visibility = Visibility.Visible; 
     } 

     if (varSizeGrid.ListState == ViewState.Full) 
     { 
      var snapScroll = varSizeGrid._snappedView.FindFirstChildOfType<ScrollViewer>(); 
      if (snapScroll != null) 
      { 
       int index = (int)snapScroll.VerticalOffset; 
       if (index >= 0 && index < varSizeGrid.ItemsSource.Count) 
        varSizeGrid.BringIntoView(varSizeGrid.ItemsSource[index]); 
      } 
     } 
     else if (varSizeGrid.ListState == ViewState.Snapped) 
     { 
      if (varSizeGrid._itemContainer == null) 
       return; 

      var item = (from a in varSizeGrid._itemContainer 
         where (a.Column * varSizeGrid.ItemWidth) + varSizeGrid.ItemWidth > varSizeGrid._scrollViewer.HorizontalOffset + varSizeGrid.Padding.Left 
         select a).FirstOrDefault(); 
      if (item == null) 
       return; 

      varSizeGrid.BringIntoView(varSizeGrid.ItemsSource[item.Index]); 
     } 
    } 
    #endregion 

    #region Virtual Voids 
    protected override void OnItemTemplateChanged(DependencyPropertyChangedEventArgs e) 
    { 
     Debug.WriteLine("Item template changed"); 
     SetupSnappedView(); 
     GenerateItemContainers(); 
    } 

    protected override void OnItemsSourceChanged(DependencyPropertyChangedEventArgs e) 
    { 
     Debug.WriteLine("Items source changed"); 
     SetupSnappedView(); 

     if (e.OldValue != null) 
     { 
      if (e.OldValue is INotifyCollectionChanged) 
      { 
       INotifyCollectionChanged collection = (INotifyCollectionChanged)e.OldValue; 
       collection.CollectionChanged -= collection_CollectionChanged; 
      } 

      ClearItems(); 
     } 
     if (e.NewValue != null) 
     { 
      GenerateItemContainers(); 
      UpdateView(); 

      if (e.NewValue is INotifyCollectionChanged) 
      { 
       INotifyCollectionChanged collection = (INotifyCollectionChanged)e.NewValue; 
       collection.CollectionChanged += collection_CollectionChanged; 
      } 
     } 
    } 

    protected virtual DataTemplate GetDataTemplateForItem(object item) 
    { 
     return ItemTemplate; 
    } 
    #endregion 

    #region Properties 
    public Style SnappedItemContainerStyle 
    { 
     get { return (Style)GetValue(SnappedItemContainerStyleProperty); } 
     set { SetValue(SnappedItemContainerStyleProperty, value); } 
    } 

    public ViewState ListState 
    { 
     get { return (ViewState)GetValue(ListStateProperty); } 
     set { SetValue(ListStateProperty, value); } 
    } 

    public object SelectedValue 
    { 
     get { return _selectedItem; } 
     set 
     { 
      if (value == _selectedItem) 
       return; 

      if (value == null) 
      { 
       _selectedItem = null; 
       return; 
      } 

      var oldValue = _selectedItem; 

      if (BringIntoView(value)) 
      { 
       _selectedItem = value; 
       if (SelectionChanged != null) 
        SelectionChanged(this, new SelectionChangedEventArgs(new List<object>() { oldValue }, new List<object>() { value })); 
      } 
     } 
    } 
    #endregion 

    #region Overrides 
    protected override void OnApplyTemplate() 
    { 
     base.OnApplyTemplate(); 
     Debug.WriteLine("OnApplyTemplate"); 
     _root = this.GetTemplateChild("Root") as Grid; 
     _panel = this.GetTemplateChild("LayoutArea") as Panel; 
     _scrollViewer = this.GetTemplateChild("Scroll") as ScrollViewer; 
     _snappedView = this.GetTemplateChild("SnappedView") as ListView; 
     _snappedView.Loaded += _snappedView_Loaded; 
     _scrollViewer.ViewChanging += _scrollViewer_ViewChanging; 
     _scrollViewer.ViewChanged += _scrollViewer_ViewChanged; 
     _panel.SizeChanged += _panel_SizeChanged; 
     _snappedView.SelectionChanged += _snappedView_SelectionChanged; 
     App.RootFrame.SizeChanged += RootFrame_SizeChanged; 

     if (ItemsSource != null) 
     { 
      GenerateItemContainers(); 
      UpdateView(); 
     } 
     SetupSnappedView(); 
    } 
    #endregion 

    #region Private voids 
    protected override void ClearItems() 
    { 
     if (_panel == null) 
      return; 

     foreach (var item in _panel.Children.Cast<FrameworkElement>()) 
     { 
      RemoveChild(item); 
     } 

     _currentDataView.Clear(); 
     _panel.Children.Clear(); 
    } 

    protected override void GenerateItemContainers() 
    { 
     if (_panel == null || _panel.Visibility == Visibility.Collapsed || _panel.ActualHeight == 0 || _panel.ActualWidth == 0) 
      return; 

     _itemContainer.Clear(); 

     if (ItemsSource == null) 
      return; 

     int _currentRow = 0, _currentColumn = 0; 
     int _currentColWidth = 1; 
     _maxRows = (int)((_panel.ActualHeight-this.Padding.Bottom-this.Padding.Top)/this.ItemHeight); 
     for (int ix = 0; ix < ItemsSource.Count; ix++) 
     { 
      var item = ItemsSource[ix]; 
      var container = new ItemContainer { Index = ix }; 
      if (_currentDataView.Contains(item)) 
       container.IsRealized = true; 

      if (OnCalculatingItemSize != null) 
       OnCalculatingItemSize(container); 

      if (container.RowSpan < 0) 
       container.RowSpan = 0; 
      if (container.ColumnSpan < 0) 
       container.ColumnSpan = 0; 

      container.Column = _currentColumn; 
      container.Row = _currentRow; 

      if (container.RowSpan + _currentRow > _maxRows) //Not enough rows --> new column 
      { 
       if (_itemContainer.Count > 0) 
       { 
        // Equally split the rest of the space in this column over the elements that are currently in it 
        var additionalrows = _maxRows - _currentRow; 
        var c = _itemContainer.Where(x => x.Column == _currentColumn).ToList(); 
        var ccount = c.Count(); 
        var i = 0; 
        foreach (var t in c) 
        { 
         t.Row += (additionalrows/ccount)*i; 
         t.RowSpan += additionalrows/ccount; 
         i++; 
        } 

        // Couldn't equally divide, give the rest of the space to the last element 
        if (additionalrows%ccount > 0) 
        { 
         c.Last().RowSpan += additionalrows%ccount; 
        } 
       } 

       _currentColumn++; 
       container.Column = _currentColumn; 
       _currentRow = 0; 
       container.Row = _currentRow; 
       _currentColWidth = container.ColumnSpan; 
       _itemContainer.Add(container); 
       _currentRow += container.RowSpan; 
       continue; 
      } 

      //Make column as wide as widest element 
      if (container.ColumnSpan > _currentColWidth) 
       _currentColWidth = container.ColumnSpan; 
      if (_currentColWidth > container.ColumnSpan) 
       container.ColumnSpan = _currentColWidth; 

      _currentRow += container.RowSpan; 

      if (_currentRow >= _maxRows) 
      { 
       //Make sure all elements in column are as wide as widest element 
       foreach (var wider in 
          _itemContainer.Where(x => x.ColumnSpan < _currentColWidth && x.Column == _currentColumn)) 
       { 
        wider.ColumnSpan = _currentColWidth; 
       } 

       _currentRow = 0; 
       _currentColumn += _currentColWidth; 
       _currentColWidth = 1; 
      } 

      _itemContainer.Add(container); 
     } 

    } 

    protected override void UpdateView() 
    { 
     if (_panel == null || _scrollViewer == null || _itemContainer == null || _itemContainer.Count == 0) 
      return; 

     _panel.Width = ((_itemContainer.Last().Column + 1) * this.ItemWidth) + this.Padding.Left + this.Padding.Right; 

     var margin = this.ItemWidth * COLUMNS_PRELOADED; 
     double minLeft = (_scrollViewer.HorizontalOffset - margin); 
     double maxLeft = (_scrollViewer.HorizontalOffset + _scrollViewer.ActualWidth + margin); 
     int minColumn = (int)(minLeft/this.ItemWidth)-1; 
     int maxColumn = (int)(maxLeft/this.ItemWidth)+1; 
     minLeft = (minColumn * this.ItemWidth) + this.Padding.Left; 
     maxLeft = (maxColumn * this.ItemWidth) + this.Padding.Left; 

     _currentDataView.Clear(); 
     var shown = _itemContainer.Where(x => x.Column >= minColumn && x.Column <= maxColumn); 
     _currentDataView.AddRange(shown.Select(x => ItemsSource[x.Index])); 

     List<FrameworkElement> recycle = new List<FrameworkElement>(); 
     foreach (var old in _panel.Children.Cast<FrameworkElement>().Where(x => x.Margin.Left < minLeft || x.Margin.Left > maxLeft || 
      !(_currentDataView.Contains(x.DataContext))).ToList()) 
     { 
      foreach (var recycled in _itemContainer.Where(x => ItemsSource[x.Index] == old.DataContext)) 
       recycled.IsRealized = false; 
      //RemoveChild(old); 
      recycle.Add(old); 
     } 

     foreach (var item in shown) 
     { 
      FrameworkElement obj = recycle.FirstOrDefault(); 
      double left, top; 
      if (item.IsRealized) 
      { 
       //Rearrange item if needed 
       obj = _panel.Children.FirstOrDefault(x => x is FrameworkElement && ((FrameworkElement)x).DataContext == ItemsSource[item.Index]) as FrameworkElement; 
       if (obj == null) 
        continue; 
       obj.Height = (this.ItemHeight * item.RowSpan) - (2 * CARD_MARGIN); 
       obj.Width = (this.ItemWidth * item.ColumnSpan) - (2 * CARD_MARGIN); 
       left = (item.Column * this.ItemWidth) + this.Padding.Left; 
       top = (item.Row * this.ItemHeight) + this.Padding.Top; 
       obj.Margin = new Thickness(left, top, 0, 0); 
       continue; 
      } 

      item.IsRealized = true; 

      if (obj == null) 
      { 
       obj = this.GetDataTemplateForItem(item).LoadContent() as FrameworkElement; 
       obj.Tapped += Item_Tapped; 
       obj.PointerReleased += Item_PointerReleased; 
       obj.PointerEntered += Item_PointerEntered; 
       obj.PointerExited += Item_PointerExited; 
       _panel.Children.Add(obj); 
      } 
      else 
       recycle.Remove(obj); 

      obj.DataContext = null; 
      obj.DataContext = ItemsSource[item.Index]; 
      obj.Height = (this.ItemHeight * item.RowSpan) - (2 * CARD_MARGIN); 
      obj.Width = (this.ItemWidth * item.ColumnSpan) - (2 * CARD_MARGIN); 
      obj.VerticalAlignment = VerticalAlignment.Top; 
      obj.HorizontalAlignment = HorizontalAlignment.Left; 
      obj.Visibility = Visibility.Visible; 
      left = (item.Column * this.ItemWidth) + this.Padding.Left; 
      top = (item.Row * this.ItemHeight) + this.Padding.Top; 
      obj.Margin = new Thickness(left, top, 0, 0); 
     } 

     foreach (var notRecycled in recycle) //Remove not recycled items 
      RemoveChild(notRecycled); 

     Debug.WriteLine("Children count "+_panel.Children.Count); 
    } 

    private void RemoveChild(FrameworkElement item) 
    { 
     item.Tapped -= Item_Tapped; 
     item.PointerReleased -= Item_PointerReleased; 
     item.PointerEntered -= Item_PointerEntered; 
     item.PointerExited -= Item_PointerExited; 
     _panel.Children.Remove(item); 
     item.DataContext = null; 
     item.ClearValue(DataContextProperty); 
    } 

    public static IEnumerable<PropertyInfo> GetAllProperties(TypeInfo type) 
    { 
     var list = type.DeclaredProperties.ToList(); 

     var subtype = type.BaseType; 
     if (subtype != null) 
      list.AddRange(GetAllProperties(subtype.GetTypeInfo())); 

     return list.ToArray(); 
    } 

    public List<DependencyObject> AllChildren(DependencyObject parent) 
    { 
     var list = new List<DependencyObject>(); 
     for (int i = 0; i < VisualTreeHelper.GetChildrenCount(parent); i++) 
     { 
      var child = VisualTreeHelper.GetChild(parent, i); 
      if (child != null) 
       list.Add(child); 
      list.AddRange(AllChildren(child)); 
     } 
     return list; 
    } 

    private void SetupSnappedView() 
    { 
     if (_snappedView == null) 
      return; 

     _snappedView.ItemTemplate = this.ItemTemplate; 
     _snappedView.ItemContainerStyle = this.SnappedItemContainerStyle; 
     _snappedView.ItemsSource = this.ItemsSource; 

     var snapScroll = this._snappedView.FindFirstChildOfType<ScrollViewer>(); 
     if (snapScroll != null) 
     { 
      snapScroll.ViewChanged -= snapped_ViewChanged; 
      snapScroll.ViewChanged += snapped_ViewChanged; 
     } 
    } 

    private void Item_PointerExited(object sender, Windows.UI.Xaml.Input.PointerRoutedEventArgs e) 
    { 

    } 

    private void Item_PointerEntered(object sender, Windows.UI.Xaml.Input.PointerRoutedEventArgs e) 
    { 

    } 

    private bool _pointerPressed = false; 
    private object _pointerOriginalSource = null; 
    private void Item_PointerReleased(object sender, Windows.UI.Xaml.Input.PointerRoutedEventArgs e) 
    { 
     if (e.Handled) 
      return; 

     _pointerPressed = true; 
     _pointerOriginalSource = e.OriginalSource; 
    } 

    private void Item_Tapped(object sender, Windows.UI.Xaml.Input.TappedRoutedEventArgs e) 
    { 
     if (!_pointerPressed || e.OriginalSource != _pointerOriginalSource) 
     { 
      _pointerPressed = false; 
      return; 
     } 

     object previousObject = _selectedItem; 
     object dataObject = ((FrameworkElement)sender).DataContext; 

     if (dataObject != _selectedItem) 
     { 
      _selectedItem = dataObject; 
      if (SelectionChanged != null) 
       SelectionChanged(this, new SelectionChangedEventArgs(new List<object>() { previousObject }, new List<object>() { dataObject })); 
     } 

     _pointerPressed = false; 
    } 

    void collection_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e) 
    { 
     object item = null; 
     if (_itemContainer != null && _itemContainer.Count > 0 && ListState == ViewState.Snapped) 
     { 
      var snapScroll = _snappedView.FindFirstChildOfType<ScrollViewer>(); 
      if (snapScroll != null) 
      { 
       int index = (int)snapScroll.VerticalOffset; 
       if (index >= 0 && index < ItemsSource.Count) 
        item = ItemsSource[index]; 
      } 
     } 
     else if (_itemContainer != null && _itemContainer.Count > 0 && ListState == ViewState.Full) 
     { 
      var i = (from a in _itemContainer 
         where (a.Column * ItemWidth) + ItemWidth > _scrollViewer.HorizontalOffset + Padding.Left 
         select a).FirstOrDefault(); 
      if (i == null) 
       return; 

      item = ItemsSource[i.Index]; 
     } 

     if (e.Action == NotifyCollectionChangedAction.Add || e.Action == NotifyCollectionChangedAction.Reset) 
      _moreDataRequested = false; 

     GenerateItemContainers(); 
     UpdateView(); 

     //if (item != null) 
     // BringIntoView(item); 
     //else if (this.ItemsSource is IList && (this.ItemsSource as IList).Count > 0) 
     // BringIntoView((this.ItemsSource as IList)[0]); 
    } 

    void VirtualizedVariableSizedWrapGrid_Loaded(object sender, RoutedEventArgs e) 
    { 
     if (this.ListState == ViewState.Full) 
     { 
      this._scrollViewer.Visibility = Visibility.Visible; 
      this._snappedView.Visibility = Visibility.Collapsed; 
     } 
     else if (this.ListState == ViewState.Snapped) 
     { 
      this._scrollViewer.Visibility = Visibility.Collapsed; 
      this._snappedView.Visibility = Visibility.Visible; 
     } 

     Debug.WriteLine("VirtualizedVariableSizedWrapGrid Loaded"); 
     if (this.ItemsSource != null && (_itemContainer == null || _itemContainer.Count == 0)) 
     { 
      GenerateItemContainers(); 
      UpdateView(); 
     } 
    } 

    void snapped_ViewChanged(object sender, ScrollViewerViewChangedEventArgs e) 
    { 
     if (!(sender is ScrollViewer)) 
      return; 

     var scroll = (sender as ScrollViewer); 
     var maxOffset = scroll.ExtentHeight - scroll.ViewportHeight; 
     if (!_moreDataRequested && (maxOffset <= 0 || scroll.VerticalOffset >= maxOffset)) 
     { 
      _moreDataRequested = true; 
      if (DataRequested != null) 
       DataRequested(this, EventArgs.Empty); 
     } 
    } 

    void _snappedView_Loaded(object sender, RoutedEventArgs e) 
    { 
     var snapScroll = this._snappedView.FindFirstChildOfType<ScrollViewer>(); 
     if (snapScroll != null) 
     { 
      snapScroll.ViewChanged += snapped_ViewChanged; 
     } 
    } 

    private double _lastOffsetUpdate = 0; 
    void _scrollViewer_ViewChanging(object sender, ScrollViewerViewChangingEventArgs e) 
    { 
     if (e.NextView.HorizontalOffset == e.FinalView.HorizontalOffset) 
     { 
      UpdateView(); 
      return; 
     } 

     if (Math.Abs(e.NextView.HorizontalOffset - _lastOffsetUpdate) > 100) 
     { 
      _lastOffsetUpdate = e.NextView.HorizontalOffset; 
      UpdateView(); 
     } 
    } 

    void _scrollViewer_ViewChanged(object sender, ScrollViewerViewChangedEventArgs e) 
    { 
     if (!_moreDataRequested && this._scrollViewer.HorizontalOffset + this._scrollViewer.ActualWidth >= (this._panel.ActualWidth)) 
     { 
      _moreDataRequested = true; 
      if (DataRequested != null) 
       DataRequested(this, EventArgs.Empty); 
     } 
    } 

    void _panel_SizeChanged(object sender, SizeChangedEventArgs e) 
    { 
     int currentMaxRows = _maxRows; 
     _maxRows = (int)((_panel.ActualHeight - this.Padding.Bottom - this.Padding.Top)/this.ItemHeight); 
     if (_maxRows != currentMaxRows) //Orientation probably changed, control got higher 
     { 
      Debug.WriteLine("Panel size changed"); 
      ClearItems(); 
      GenerateItemContainers(); 
      UpdateView(); 
     } 
    } 

    void _snappedView_SelectionChanged(object sender, SelectionChangedEventArgs e) 
    { 
     if (e.AddedItems.Count > 0) 
      this.SelectedValue = e.AddedItems[0]; 

     _snappedView.SelectedItem = null; 
    } 

    void RootFrame_SizeChanged(object sender, SizeChangedEventArgs e) 
    { 
     if (_itemContainer != null && _itemContainer.Count > 0 && _panel.ActualWidth > 0) 
      UpdateView(); 
    } 
    #endregion 

    #region Public voids 
    public override bool BringIntoView(object obj, bool animate = true) 
    { 
     try 
     { 
      if (_itemContainer == null || _itemContainer.Count == 0) 
       return false; 

      var item = (from a in _itemContainer 
         where ItemsSource[a.Index] == obj 
         select a).FirstOrDefault(); 
      if (item == null) 
       return false; 

      //Calculate position 
      double offset = (item.Column * this.ItemWidth) + CARD_MARGIN; 
      this._scrollViewer.ChangeView(offset, null, null, !animate); 
      _snappedView.ScrollIntoView(obj, ScrollIntoViewAlignment.Leading); 

      UpdateView(); 

      return true; 
     } 
     catch { return false; } 
    } 
    #endregion 
} 
+1

欢迎来到Stack Overflow!如果你将代码编写成[最小,完整,测试和可读](http://stackoverflow.com/help/mcve),我们很容易找到问题。在很多情况下,锻炼甚至可以帮助你自己找到答案! – mhlester

回答

-2

你设置容器的地方ContentTemplate和/或Style?或者可能是另一个属性,如BorderBrush等?也许你在xaml中这样做?

如果这样做,请尝试在释放容器时将所有这些属性设置为null。它帮助我解决了一些内存泄漏问题。

+1

我downvoted,因为这是没有答案。即使它导致他摆脱泄漏,这只是一堆猜测,甚至不会导致适当的解决方案。我同意他的代码太长了,但是这与泄漏有什么关系?长码使得难以检测到问题,但这应该是“长码”第一次导致泄漏。 –

+0

如你所愿。要获得实际泄漏,您需要完整的解决方案从他的代码中你只能看到他退出了他所能做的一切。但泄漏可能不在那里。你认为这里有真正的魔术师从帽子里得到兔子吗? 如果你能阅读,我的答案不是长码 – notacat

+0

但他肯定泄漏是在这个控制。我理解你的观点,但它不是一个答案(这是一堆尝试或尝试)。这些信息可能很有用,但使用评论系统imo。不要误会我的意思,我也不会因为我喜欢downvote而失望。 Idd你的实际“答案”并不是太长的代码,而是你从它开始(至少提到过长代码与他的问题有关的原因:例如,长代码使得找到泄漏更难)。但是,这不是答案。如果我和OP有同样的问题,看看你的答案对我来说无济于事。 –