Question

When the ItemsSource property of an ItemsControl is bound to an ObservableCollection, what determines the lifetime of the user control created through the ItemTemplate of the ItemsControl?

The specific ItemsControl I am testing this on is a ScatterView which is a Surface SDK component:

<s:ScatterView ItemsSource="{Binding Images}">
    <s:ScatterView.ItemTemplate>
        <DataTemplate>
            <Image Source="{Binding} />
        </DataTemplate>
    </s:ScatterView.ItemTemplate>
</s:ScatterView>

It simply binds to an ObservableCollection<string> in code-behind:

public ObservableCollection<string> Images { get; private set; }

As expected, when file system paths pointing to images are added to this collection, these images show up in the ScatterView. When they are removed, they are no longer visible.

However, when trying to rename/move/delete a file which was previously shown on the ScatterView but now removed from the ObservableCollection, a message shows up this file is still in use. This is because the Image component seems to lock the file while it is being displayed. However, the Image is no longer being displayed, but it doesn't seem to be disposed.

Although another post discusses how to manually remove these user controls, this is not what I am interested in. Does any element created through an ItemTemplate stay in memory indefinitely by default? What is the 'proper' way to handle this?

Était-ce utile?

La solution

The problem here is not the Image control destruction. By default WPF is caching all image files and locks them. In this way when an image is displayed in multiple controls it is loaded only once in memory.

To avoid cashing you can use this kind of string to BitmapImage converter:

public class PathToBitmapConverter : IValueConverter
{

    public object Convert( object value, Type targetType, object parameter, System.Globalization.CultureInfo culture )
    {
        string filePath = (string)value;

        if ( String.IsNullOrEmpty( filePath ) )
        {
            return null;
        }
        if ( !File.Exists( filePath ) )
        {
            return null;
        }

        BitmapImage bitmapImage;
        try
        {
            using ( var fileStream = new FileStream( filePath, FileMode.Open, FileAccess.Read ) )
            {
                bitmapImage = new BitmapImage();
                bitmapImage.BeginInit();
                bitmapImage.StreamSource = fileStream;
                bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
                bitmapImage.EndInit();
                bitmapImage.Freeze();

                fileStream.Close();
            }
        }
        catch
        {
            bitmapImage = null;
        }

        return bitmapImage;
    }

    public object ConvertBack( object value, Type targetType, object parameter, System.Globalization.CultureInfo culture )
    {
        throw new NotImplementedException();
    }
}

In this way the image is loaded at the same time when binding is resolved and the file is no longer locked.

In other words, it seems like the user controls created by the data templates are garbage collected. The underlying problem is the way WPF handles image sources by default. By taking care of how the image is loaded yourself (which is what happens in the converter) the default behavior can be overridden. By setting CacheOption to BitmapCacheOption.OnLoad, "the entire image is loaded into memory at load time. All requests for image data are filled from the memory store."

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top