2

Rather new to UWP and XAML and I'm not sure how to debug this.

I have a UWP app based on the Template10 Hamburger template and the Template10 incremental loading sample, plus some bits from a sample photo viewer (Windows 8: Making a Simple Photo Viewer in C# and XAML).

I have modified the main page to display a Gridview of images from the Pictures folder, with the images being loaded incrementally. I also pulled some from a sample photo viewer (Windows 8: Making a Simple Photo Viewer in C# and XAML).

When the app starts, the images are displayed as expected, and as I scroll down, the images are loaded and displayed on demand. The problem is as I scroll back up the list, the images are not displayed any more. My gridview items are still there showing the file name and coloured item background but the image is no longer drawn.

To keep my memory footprint small, I am not storing the actual Bitmap image as part of my collection but a StorageItemThumbnail instead. I originally wanted to store just the image path but that doesn't work for anything in the Pictures library.

public class Picture
{
    public StorageItemThumbnail  ImageThumbNail {get; set;}
    public string Name {get; set;}
}

To display this, I use a converter class to create a stream to set the image source:

public object Convert(object value, Type targetType, object parameter, string language)
{

    BitmapImage image = null;

    if (value != null)
    {
        if (value.GetType() != typeof(StorageItemThumbnail))
        {
            throw new ArgumentException("Expected a StorageItemThumbnail as binding input.");
        }
        if (targetType != typeof(ImageSource))
        {
            throw new ArgumentException("Expected ImageSource as binding output.");
        }

        if (Windows.ApplicationModel.DesignMode.DesignModeEnabled)
        {
            image = new BitmapImage();
            image.UriSource = new Uri("ms-appx:///Assets/DesignModeThumbnailPlaceholder.png");
        }
        else
        {
            StorageItemThumbnail ThumbNailFile = (StorageItemThumbnail)value;

            if (ThumbNailFile == null)
                return image;

            image = new BitmapImage();
            IRandomAccessStream thumbnailStream = ThumbNailFile as IRandomAccessStream;
            image.SetSource(thumbnailStream);
        }

    }

    return (image);

}

And this is bound in my XAML as follows:

    <DataTemplate x:Name="PictureGridTemplate2">
    <Grid Height="150" Width="150">
        <Grid.RowDefinitions>
            <RowDefinition Height="*" />
            <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>
        <Border Grid.RowSpan="2" Background="Black" Opacity="0.8" />
        <Image Width ="130" HorizontalAlignment="Center"
               Stretch="Uniform" Source="{Binding ImageThumbNail, Converter={StaticResource ThumbnailFileToImageSourceConverter}}"/>
        <TextBlock Grid.Row="1" MaxHeight="30" Text="{Binding Name}"
                   Foreground="White" TextTrimming="CharacterEllipsis"/>
    </Grid>
</DataTemplate>

Can anyone point me in the direction where I've gone wrong with this?

Sheri

* RESOLVED *

I was finally able to figure out what was causing this issue.

It was a side effect of the gridview virtualization, my data model and how I was supplying the image through the converter.

As a test I removed the converter, changed my data model to store a BitmapImage instance of the thumbnail (smaller than storing the whole image) and to bind directly to that property. This worked, the images displayed on the screen as I scrolled up and down through my gridview.

I then changed my data model to have the BitmapImage property getter return the BitmapImage build on the fly from the StorageItemThumbnail property - same issue as when using the converter.

By adding some debugging statements in the getter I saw that the height and width of the BitmapImage on the second request was 0. Aha! So why 0?

Looking at the StorageItemThumbnail property on the second request I saw that the Stream position was at the EOF (not 0 like in the first request) - so this explained the 0 width and height, which explains the empty image control on the screen.

I changed my code to use StorageItemThumbnail.CloneStream and now all images are displayed.

Here is the converter method now:

    public object Convert(object value, Type targetType, object parameter, string language)
    {

        BitmapImage image = null;

        if (value != null)
        {
            if (value.GetType() != typeof(StorageItemThumbnail))
            {
                throw new ArgumentException("Expected a StorageItemThumbnail as binding input.");
            }
            if (targetType != typeof(ImageSource))
            {
                throw new ArgumentException("Expected ImageSource as binding output.");
            }

            if ((StorageItemThumbnail)value == null)
            {
                System.Diagnostics.Debug.WriteLine("Thumbnail is null.");
                return image;
            }

            image = new BitmapImage();

            using (var thumbNailClonedStream = ((StorageItemThumbnail)value).CloneStream())
            {
                System.Diagnostics.Debug.WriteLine("Setting image source from cloned stream.");
                image.SetSource(thumbNailClonedStream);
            } 
        }

        return (image);

    }

Thanks to everyone who took the time to answer and help point me in the right direction.

spazm
  • 4,399
  • 31
  • 30
SheriSteeves
  • 55
  • 1
  • 7
  • Converter is the reason. It's calling everytime when you scroll up creating images everytime. Rather store the BitMapImage itself. – Archana Apr 29 '16 at 17:32
  • I don't think so as if I put a breakpoint in the Convert method, it only gets hit once for each image as they are incrementally loaded. Once it's all loaded, I can scroll up and down as much as I want and the breakpoint is not triggered. The whole reason behind not storing the BitmapImage is to keep the memory footprint low. – SheriSteeves Apr 29 '16 at 17:49
  • Thank you for this, Sheri. I was having the same issue as you and I am getting to a point where I am sick of constantly deconstructing my code to learn this platform (WPF is SO much simpler!). Your fixed worked, so thanks a lot! – Moe45673 Oct 16 '18 at 21:39

1 Answers1

0

You can try use compiled binding x:Bind instead of Binding
In this case you application perfomance would be much more faster.

Optimize your datatemplate with phases to update GridView items progressively:

    <Grid Height="150" Width="150">
    <Grid.RowDefinitions>
        <RowDefinition Height="*" />
        <RowDefinition Height="Auto" />
    </Grid.RowDefinitions>
    <Border Grid.RowSpan="2" Background="Black" Opacity="0.8" />
    <Image Width ="130" HorizontalAlignment="Center"
           Stretch="Uniform" Source="{Binding ImageThumbNail, Converter={StaticResource ThumbnailFileToImageSourceConverter}}" x:Phase="2"/>
    <TextBlock Grid.Row="1" MaxHeight="30" Text="{Binding Name}" x:Phase="1"
               Foreground="White" TextTrimming="CharacterEllipsis"/>
   </Grid> 

Read this topics:
ListView and GridView UI optimization
ListView and GridView data virtualization

Alexej Sommer
  • 2,677
  • 1
  • 14
  • 25
  • Right now my issue is not about speed; the images, or actually thumbnails, load fairly quickly. I will look at x:Bind and using this in a resource dictionary date template later. The issue is that the Image control stops displaying them. When the app first opens, my images are displayed in the gridview items shown on the screen, and as I scroll down I see all my other images. BUT - if I scroll back up, at some point the images are no longer showing, just the black gridview item and the name as a textbox. – SheriSteeves Apr 29 '16 at 17:40
  • That's looks like data virtualization problem – Alexej Sommer Apr 30 '16 at 05:40
  • It is, as I was able to reproduce my apps bad behaviour in the Template10 incremental sample code by adding an image to it's list view template. It is like it doesn't know that it's been unloaded by the view, so it doesn't know when to reload it. I'm a complete newb with all this so I'm not sure what I missed yet but I'm looking at your suggestions and other samples. – SheriSteeves May 02 '16 at 13:58