简体   繁体   中英

Auto height images in WP8 LongListSelector

I'm making an infinite scrolling feed in XAML and C# on Windows Phone 8 using a LongListSelector. Items to be displayed are loaded from a web feed and displayed using data binding, with one of the fields for each item being an image which is also loaded from the web.

The images are all different sizes, but I haven't set the width or height on the Image contol, just Margin="10" so that it fills the width of the screen and is the correct height for the image.

The image control in the DataTemplate looks like this:

<Image Source="{Binding Source.ImageUrl}" Margin="10" Stretch="UniformToFill" />

However, I'm getting a System.Windows.LayoutCycleException which says: "Layout cycle detected. Layout could not complete.".

This seems to be due to the fact that the <Image> controls are placed into the LongListSelector before the images have downloaded. Then, once the images are downloaded, the <Image> controls resize, causing the error. If I set Height="300" on the <Image> controls, I don't get the exception.

I would still like the feed to be displayed before the images have downloaded, and potentially a lot of images could be in the feed, so it needs to be efficient with memory usage.

Are there any solutions to this problem?

Uniform should be used to make it fill the width instead of UniformToFill (because uniform to fill will try to fill the "height" to and since there is not height defined I can see it causing problem when item are recycled)

Maybe you can try to limit the number of image which are loaded at the same time, for example the following code will use a atached property to load one at a time (you can adapt to load more than one). For AsyncLock you can use the code from here (or search online for it)

public static readonly DependencyProperty LimitedImageProperty = DependencyProperty.RegisterAttached("LimitedImage", typeof (string), typeof (LimitImageDownloadConverter), new PropertyMetadata(default(string),LimitedImagedChanged));

    public static void SetLimitedImage(UIElement element, string value)
    {
        element.SetValue(LimitedImageProperty, value);
    }

    public static string GetLimitedImage(UIElement element)
    {
        return (string) element.GetValue(LimitedImageProperty);
    }


    AsyncLock imageLock = new AsyncLock();

    private static async void LimitedImagedChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        Image currentImage = d as Image;
        object dataContext = currentImage.DataContext;
        string uri = e.NewValue as string;
        if (string.IsNullOrEmpty(uri))
        {
            return;
        }

            using (await imageLock.AcquireLock())
            {
                if (currentImage.DataContext != dataContext)
                {
                    //Item jhave been recycled
                    return;
                }
                await LoadImage(currentImage, uri);
            }
    }




    private static Task<bool> LoadImage(Image image, string uri)
    {
        TaskCompletionSource<bool> tcs = new TaskCompletionSource<bool>();
        BitmapImage bitmap = new BitmapImage(new Uri(uri));
        bitmap.ImageOpened += (send, arg) =>
        {

            tcs.SetResult(true);
        };
        bitmap.ImageFailed += (send, arg) =>
        {

            tcs.SetResult(false);
        };
        return tcs.Task;
    }

Then just use local:MyAttatchedProperty.LimitedImage to set the Image source on your image instead of using ImageSource directly.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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