简体   繁体   English

从包装面板中清除子控件不会清除 memory 和参考

[英]clearing child controls from wrappanel does not clear memory and references

Issue问题

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.问题如下:删除元素后,memory 不会被释放,也不会被垃圾回收。

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

What I tried我试过的

I found multiple resources and questions about the topic, but none of them seemed to work for me:我找到了有关该主题的多个资源和问题,但似乎没有一个对我有用:

  • name properties have to be set to null before clearing清除前必须将名称属性设置为 null
  • grids and stackpanels have to be cleared必须清除网格和堆栈面板
  • all references sould be cleared所有引用都应该被清除
  • the element itself should be cleared元素本身应该被清除
  • name property of the parent should be cleared应清除父项的名称属性

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: 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.一段时间后,垃圾收集器变得疯狂但从未真正清除任何元素。

I dont kow if this is the way to do it but it works:我不知道这是不是这样做的方法,但它有效:

  1. add a function to repopulate the properties to the child control xaml.cs:添加一个 function 以重新填充子控件 xaml.cs 的属性:
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 = attribute.@value.ToString();
    this.MinValue_TextBox.Text = attribute.min_value.ToString();
    this.MaxValue_TextBox.Text = attribute.max_value.ToString();
}
  1. to the parent which holds the collection, add a queue and adjust the remove function to add the elements to the queue instead of destroying them:对于保存集合的父级,添加一个队列并调整 remove function 以将元素添加到队列而不是销毁它们:
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);
    }
}
  1. upon loading of the elements, recycle the previously removed elements from the queue:加载元素后,回收先前从队列中删除的元素:
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.这不会忽略手动按下控件的删除按钮导致的 memory 泄漏,但这可能可以忽略不计。 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.例如,您可能会从 previus 属性中找到值。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM