2012-10-04 18 views
10

लीक करने पर वर्तमान में WPF TreeView के साथ एक अजीब स्मृति रिसाव है। जब मैं TreeView में कोई आइटम चुनता हूं, तो इसी बाउंड व्यू मॉडेल को दृढ़ता से TreeView EffectiveValueEntry [] संग्रह में पकड़ लिया जाता है। मुद्दा यह है कि जब इसे ViewModel को इसके मूल संग्रह से हटा दिया जाता है तो इसे जारी नहीं किया जाता है।WPF TreeView चयनित आइटम

यहाँ समस्या को ठीक करने के लिए एक सरल कोड है:

MainWindow.xaml

using System.Collections.ObjectModel; 
using System.Windows; 
using System.Windows.Controls.Primitives; 

namespace TreeViewMemoryLeak 
{ 
    public partial class MainWindow : Window 
    { 
     public MainWindow() 
     { 
      InitializeComponent(); 
      DataContext = this; 
     } 

     public ObservableCollection<Entry> Entries 
     { 
      get 
      { 
       if (entries == null) 
       { 
        entries = new ObservableCollection<Entry>() { new Entry() { DisplayName = "First Entry" } }; 
       } 
       return entries; 
      } 
     } 

     private void Button_Click(object sender, RoutedEventArgs e) { entries.Clear(); } 

     private ObservableCollection<Entry> entries; 

    } 

    public class Entry : DependencyObject 
    { 
     public string DisplayName { get; set; } 
    } 
} 

MainWindow.xaml.cs

<Window x:Class="TreeViewMemoryLeak.MainWindow" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    xmlns:local="clr-namespace:TreeViewMemoryLeak" 
    Title="MainWindow" Height="350" Width="250"> 

    <Window.Resources> 
     <DataTemplate DataType="{x:Type local:Entry}"> 
      <TextBlock Text="{Binding DisplayName}" /> 
     </DataTemplate> 
    </Window.Resources> 

    <StackPanel> 
     <Button Content="delete item" Click="Button_Click" Grid.Row="0" Margin="10"/> 
     <TreeView x:Name="treeView" ItemsSource="{Binding Entries}" Grid.Row="1" Margin="10" BorderBrush="Black" BorderThickness="1" /> 
    </StackPanel> 

</Window> 

समस्या को पुनः करने के लिए

आइटम का चयन करें, फिर ObservableCollection को साफ़ करने के लिए बटन क्लिक करें। अब TreeView नियंत्रण पर EffectiveValueEntry [] की जांच करें: ViewModel अभी भी वहां है और कचरा संग्रह के लिए फ़्लैग नहीं किया गया है।

+0

क्या नेट संस्करण आप उपयोग कर रहे: निम्नलिखित है? – JleruOHeP

+0

मुझे .NET 3.5 और 4.0 के साथ समस्या है। मैं पूरी तरह से इसका जिक्र करना भूल गया, क्षमा करें। मैं अभी 4.5 के साथ परीक्षण करूंगा। – Sisyphe

+1

समस्या अभी भी .NET 4.5 – Sisyphe

उत्तर

3

वैसे मैं अंत में एक हिंसक समाधान के साथ आया था। TreeView में अंतिम ऑब्जेक्ट को हटाते समय मैं प्रभावी Values ​​संग्रह से संदर्भ हटा देता हूं। यह अधिक हो सकता है लेकिन कम से कम, यह काम करता है।

public class MyTreeView : TreeView 
{ 
    protected override void OnSelectedItemChanged(RoutedPropertyChangedEventArgs<object> e) 
    { 
     base.OnSelectedItemChanged(e); 

     if (Items.Count == 0) 
     { 
      var lastObjectDeleted = e.OldValue; 
      if (lastObjectDeleted != null) 
      { 
       var effectiveValues = EffectiveValuesGetMethod.Invoke(this, null) as Array; 
       if (effectiveValues == null) 
        throw new InvalidOperationException(); 

       bool foundEntry = false; 
       int index = 0; 
       foreach (var effectiveValueEntry in effectiveValues) 
       { 
        var value = EffectiveValueEntryValueGetMethod.Invoke(effectiveValueEntry, null); 
        if (value == lastObjectDeleted) 
        { 
         foundEntry = true; 
         break; 
        } 
        index++; 
       } 

       if (foundEntry) 
       { 
        effectiveValues.SetValue(null, index); 
       } 
      } 
     } 
    } 

    protected MethodInfo EffectiveValueEntryValueGetMethod 
    { 
     get 
     { 
      if (effectiveValueEntryValueGetMethod == null) 
      { 
       var effectiveValueEntryType = AppDomain.CurrentDomain.GetAssemblies().SelectMany(a => a.GetTypes()).Where(t => t.Name == "EffectiveValueEntry").FirstOrDefault(); 
       if (effectiveValueEntryType == null) 
        throw new InvalidOperationException(); 

       var effectiveValueEntryValuePropertyInfo = effectiveValueEntryType.GetProperty("Value", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.DeclaredOnly | System.Reflection.BindingFlags.Instance); 
       if (effectiveValueEntryValuePropertyInfo == null) 
        throw new InvalidOperationException(); 

       effectiveValueEntryValueGetMethod = effectiveValueEntryValuePropertyInfo.GetGetMethod(nonPublic: true); 
       if (effectiveValueEntryValueGetMethod == null) 
        throw new InvalidOperationException(); 

      } 
      return effectiveValueEntryValueGetMethod; 
     } 
    } 

    protected MethodInfo EffectiveValuesGetMethod 
    { 
     get 
     { 
      if (effectiveValuesGetMethod == null) 
      { 
       var dependencyObjectType = typeof(DependencyObject); 
       var effectiveValuesPropertyInfo = dependencyObjectType.GetProperty("EffectiveValues", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.DeclaredOnly | System.Reflection.BindingFlags.Instance); 
       if (effectiveValuesPropertyInfo == null) 
        throw new InvalidOperationException(); 

       effectiveValuesGetMethod = effectiveValuesPropertyInfo.GetGetMethod(nonPublic: true); 
       if (effectiveValuesGetMethod == null) 
        throw new InvalidOperationException(); 
      } 
      return effectiveValuesGetMethod; 
     } 
    } 

    #region Private fields 
    private MethodInfo effectiveValueEntryValueGetMethod; 
    private MethodInfo effectiveValuesGetMethod; 
    #endregion 
} 
+0

हमने पाया कि 'प्रभावी वैल्यूज [इंडेक्स + 1]' पर 'बाध्यकारी एक्सप्रेशन' था, जिसे 'lastObjectDeleted' (i.e '((बाइंडिंग एक्सपेरेशन) मान भी संदर्भित किया गया था। .ParentBinding.Source == lastObjectDeleted'), इसलिए हमने उसे भी हटा दिया। –

1

ऐसा इसलिए है क्योंकि आपने OneTime मोड के साथ अपने वृक्षदृश्य को बांध दिया है, इसलिए आपका संग्रह 'स्नैपशॉट' था।

EffectiveValueEntry के बारे में है कैसे DependencyObjects दुकान उनके DependencyProperties के मूल्यों: कहा गया है:

अपडेट किया गया। यह संग्रह तब तक ऑब्जेक्ट रखेगा जब तक TreeView ने आइटम का चयन किया हो। जैसे ही आप कुछ और संग्रह चुनते हैं, अपडेट हो जाएगा।

+0

दरअसल, " वनटाइम "बाध्यकारी एक और धागे पर मिले मुद्दे को हल करने में एक असफल प्रयास था। इसे हटाने से कुछ भी नहीं बदलेगा और यह समस्या का कारण नहीं है। संपादित करें: यह देखने के लिए कि क्या समस्या हल हो गई है, मैं वनवे बाध्यकारी कोशिश करूंगा। EDIT2: काम नहीं करता है, प्रविष्टि अभी भी जिंदा है। – Sisyphe

+0

मैंने वनटाइम बाध्यकारी को हटाने के लिए मूल पोस्ट को अपडेट किया। – Sisyphe

+0

हां यह निर्भरता संपत्ति भंडारण के बारे में है। यह सिर्फ कष्टप्रद है कि अगर अंतिम ऑब्जेक्ट को चुना गया तो स्मृति को तब जारी नहीं किया जाता है जब इसे चुना गया हो। इस मामले में, मेरे पास TreeView में कोई और ऑब्जेक्ट नहीं है, और ऑब्जेक्ट तब तक होगा जब तक कि एप्लिकेशन बंद न हो जाए। वैसे भी आपके उत्तर के लिए धन्यवाद, मैं अपनी समस्या का समाधान नहीं कर सकता, लेकिन कम से कम, आपने पुष्टि की कि मैं क्या सोच रहा था। – Sisyphe

1

मैं एक ही मुद्दा था और यह link (टॉम गौफ द्वारा पोस्ट की गई) पर समाधानों में से एक का उपयोग कर इसे हल।

ClearSelection(this.treeView); 
this.treeView.SelectedValuePath = "."; 
this.treeView.ClearValue(TreeView.SelectedValuePathProperty); 
this.treeView.ItemsSource = null; 

...

public static void ClearSelection(TreeView treeView) 
{ 
    if (treeView != null) 
     ClearSelection(treeView.Items, treeView.ItemContainerGenerator); 
} 

private static void ClearSelection(ItemCollection collection, ItemContainerGenerator generator) 
{ 
    if ((collection != null) && (generator != null)) 
    { 
     for (int i = 0; i < collection.Count; i++) 
     { 
      TreeViewItem treeViewItem = generator.ContainerFromIndex(i) as TreeViewItem; 
      if (treeViewItem != null) 
      { 
       ClearSelection(treeViewItem.Items, treeViewItem.ItemContainerGenerator); 
       treeViewItem.IsSelected = false; 
      } 
     } 
    } 
} 
संबंधित मुद्दे