Reputation: 2251
I have a wrappanel which is beeing used to apply tags to an image.
Tags can be added and removed:
The problem is the following: Upon removing an element, the memory is not released and also never garbage collected.
this is how I add the Control:
foreach (MetadataAttribute attribute in metadata.attributes)
{
this.Attributes_StackPanel.Children.Add(new Attribute(attribute));
}
this is how i removed the elements initially:
this.Attributes_StackPanel.Children.Clear();
I found multiple resources and questions about the topic, but none of them seemed to work for me:
I wandered through posts until I ended with an abomination like this:
parent:
/// <summary>
/// clears the attribute panel to prepare for new metadata to be loaded
/// </summary>
private void ClearAttributesPanel()
{
for(int i = this.Attributes_StackPanel.Children.Count-1; i > 0; i--)
{
Attribute attr = (Attribute)this.Attributes_StackPanel.Children[i];
attr.Delete();
attr = null;
}
}
xaml:
<UserControl x:Class="Minter_UI.Attribute"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Minter_UI"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800" Padding="0.1cm, 0.1cm, 0, 0">
<Grid x:Name="Main_Grid">
<Grid.ColumnDefinitions>
<ColumnDefinition MinWidth="1cm"></ColumnDefinition>
<ColumnDefinition Width="0.5cm"></ColumnDefinition>
</Grid.ColumnDefinitions>
<StackPanel x:Name="Stack_Panel" Orientation="Vertical" Grid.Row="0">
<ComboBox x:Name="TraitType_ComboBox" IsEditable="True" Text="TraitType" SelectionChanged="TraitType_ComboBox_SelectionChanged"></ComboBox>
<ComboBox x:Name="Value_ComboBox" IsEditable="True" Text="Value"></ComboBox>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<Label Content="MinValue" Grid.Column="0"></Label>
<Label Content="MaxValue" Grid.Column="1"></Label>
<TextBox x:Name="MinValue_TextBox" Grid.Row="1" Grid.Column="0"></TextBox>
<TextBox x:Name="MaxValue_TextBox" Grid.Row="1" Grid.Column="1"></TextBox>
</Grid>
</StackPanel>
<Button x:Name="Delete_Button" Content="X" Background="LightCoral" Grid.Column="1" Click="Delete_Button_Click"></Button>
</Grid>
</UserControl>
Attribute delete:
public void Delete()
{
// unregister events
this.TraitType_ComboBox.SelectionChanged -= this.TraitType_ComboBox_SelectionChanged;
this.Delete_Button.Click -= this.Delete_Button_Click;
// main grid
this.Main_Grid.Children.Clear();
this.Main_Grid = null;
// stack panel
this.Stack_Panel.Children.Clear();
this.Stack_Panel = null;
// textboxes
this.MinValue_TextBox.Text = null;
this.MinValue_TextBox = null;
this.MaxValue_TextBox.Text= null;
this.MaxValue_TextBox = null;
//comboboxes
this.TraitType_ComboBox.ItemsSource= null;
this.TraitType_ComboBox.SelectedItem= null;
this.TraitType_ComboBox.Text= null;
this.TraitType_ComboBox = null;
this.Value_ComboBox.ItemsSource = null;
this.Value_ComboBox.SelectedItem = null;
this.Value_ComboBox.Text = null;
this.Value_ComboBox = null;
// delete button
this.Delete_Button.Content = null;
this.Delete_Button = null;
// clear all x:name properties
this.UnregisterName("Stack_Panel");
this.UnregisterName("MinValue_TextBox");
this.UnregisterName("MaxValue_TextBox");
this.UnregisterName("TraitType_ComboBox");
this.UnregisterName("Value_ComboBox");
this.UnregisterName("Main_Grid");
this.UnregisterName("Delete_Button");
// remove self from parent
((Panel)this.Parent).Children.Remove(this);
}
Still, this doesnt work. I can find thousands of references to the comboboxes and textpanels as well as hashtables in heap after enough reloads. After a while, the garbage collector goes crazy but never actually clears any elements.
Upvotes: 1
Views: 209
Reputation: 2251
I dont kow if this is the way to do it but it works:
public void SetAttribute(MetadataAttribute attribute)
{
// you need to extend the function, its just an example
this.TraitType_ComboBox.Text = attribute.trait_type.ToString();
this.Value_ComboBox.Text = [email protected]();
this.MinValue_TextBox.Text = attribute.min_value.ToString();
this.MaxValue_TextBox.Text = attribute.max_value.ToString();
}
Queue<Attribute> AttributeReuseElements = new Queue<Attribute>();
private void ClearAttributesPanel()
{
for(int i = this.Attributes_StackPanel.Children.Count-1; i > 0; i--)
{
AttributeReuseElements.Enqueue((Attribute)this.Attributes_StackPanel.Children[i]);
this.Attributes_StackPanel.Children.RemoveAt(i);
}
}
foreach (MetadataAttribute attribute in CollectionInformation.Information.LikelyAttributes)
{
if (AttributeReuseElements.Count > 0)
{
Attribute attr = AttributeReuseElements.Dequeue();
attr.SetAttribute(attribute);
this.Attributes_StackPanel.Children.Add(attr);
}
else
{
this.Attributes_StackPanel.Children.Add(new Attribute(attribute));
}
}
This will not neglect the memory leak from manually pressing the delete button of the control but this will likely be neglible. Whenever the information is reloaded however, elements get recycled instead of destroyed and created freshly.
This might have some implications if you do not make sure all fields and values are properly cleared. For example you might find values from the previus property.
Upvotes: 0