There’s no substitute for customer feedback! [Improving Windows Phone 7 application performance now a bit easier with LowProfileImageLoader and DeferredLoadListBox updates]


This blog has moved to a new location and comments have been disabled.

All old posts, new posts, and comments can be found on The blog of dlaa.me.

See you there!

Comments (26)
  1. David Burela says:

    Thanks, looking forward to checking the latest version when I get home tonight.

    The OnApplyTemplate fix is something that I also hacked in myself. I didn't report it as I thought that I was just doing something wrong in the order I was loading things.

  2. Zeus says:

    Hi,

    Does DeferredLoadListBox  work in pivot control? I am noticing some odd behavior with it. It seems to hang and make things load show when scrolling from one pivot item to the next.

  3. Delay says:

    Zeus,

    I haven't specifically tried that scenario, but this is also the first I've heard of it being a problem, so maybe it's unique to your scenario. What you might do is create a new project and try to reproduce the problem there – reducing everything to its simplest form can often help identify the source of a problem like this. If you're still seeing this behavior in a simplified project, please get in touch with me via the "Email Blog Author" link on this page and I can have a look.

    Thanks!

  4. info815 says:

    For several time in my test app, I encountered the same problem of

    System.Exception was unhandled

    in the file

    LowProfileImageLoader.cs

    at

    Ln 155

    var response = responseState.WebRequest.EndGetResponse(pendingResponse);

    Any suggestions to resolve or work around it.

    Thanks very much.

  5. Delay says:

    info815,

    As you can tell from the comment in the catch clause of that block, instances of WebException are expected when the file couldn't be downloaded, etc.. If you're running with first chance exceptions enabled, you may simply be hitting an instance of that. But if you're really seeing instances of Exception itself, it might be interesting to see the message and stack trace contained within to get an idea of what might be causing this. If you're able to share those, that could be quite helpful.

    Thanks!

  6. Michael James says:

    Hi, first of all thanks for releasing this. It does make a noticeable difference in performance. I am encountering a weird issue. I'm using the listbox in a user control that is within a pivot. However intermittently the application crashed throwing the "All containers must have a Height set (ex: via ItemContainerStyle), though the heights need not all need to be the same." exception. However this is said, and when you inspect the height property it has a value but the actual height doesn't.

    Do you have any ideas on what would be causing this? I have read that "ActualWidth and ActualHeight are calculated based on the Width/Height property values and the layout system.  There is no guarantee as to when these values will be "calculated"." Which is why I'm guessing this error doesn't always occur for me.

    I was wondering whether the UnmaskItemContent needs to be called as an action using the Dispatcher?

  7. Delay says:

    Michael James,

    I'm glad to hear this has been helpful for you!

    Regarding ActualWidth/ActualHeight, they will typically be NaN set until at least one layout pass has happened in order to compute and set them. I know the Pivot control plays some tricks that could be leading to exactly what you're suggesting: that no layout pass has happened for some new containers when DeferredLoadListBox looks at them. You're welcome to try calling UnmaskItemContent from a Dispatcher – that seems like exactly the right kind of thing to try. It might also be interesting to consider whether it's possible to ignore things once or twice if the value is NaN because a subsequent update will happen when it's not and things will get set correctly at that time. (I know I used a trick like that in another Windows Phone 7 project somewhat recently.)

    I hope this helps – and I'd love to hear the results of your experimentation! :)

  8. Michael James says:

    I implemented a retry counter like you suggested, after many attempts at trying to reproduce the issue it appears having a retry counter is enough to resolve the issue. In my testing it appears just trying it 1 to 2 more times is enough.

    I now only throw an exception if the ActualHeight is 0 and the retry counter is greater than 2. You do notice a very slight pause in the UI rendering however as this error only occurs occasionally it seems a plausible and passable workaround.

    It's worth noting that I have a global retry counter, I considered one per item you try to unmask however it appears that a global one is more than enough as updates don't occur that frequently.

  9. Delay says:

    Michael James,

    Great information – thanks a lot for the update!

  10. Martin says:

    "So in lieu of a reliable way to detect the cache state of individual images (I'm open to suggestions, by the way!)"

    IIRC WebClient has a download can be forced to only use cached stuff. But of course WebClient is async as well.. But maybe this can be used as a starting point…

  11. Delay says:

    Martin,

    That's a really interesting idea! What I'd really like is a way to tell if *each image* is in the cache or not – and I'm concerned the technique you point out might force the code to make a cache-only web request which is unnecessary/wasteful when it succeeds and delay-inducing when it fails. Ick. :( You may be onto something here, but it's not quite the clean WebClient.IsImageCached(…) method I might prefer… :)

  12. Arne Schmid says:

    David,

    I just implemented the DeferredLoadListBox and it gives an amazing performance compared to a ListBox. However, my layout depends on Items in the ListBox with different Heights (Text-Blocks with varying length and wordwrapping). I'd even go as far and calculate the Height on my own but couldn't find an example on how to do. The Items in the DeferredLoadListBox are not controlls and therefore don't have an height?

    I also couldn't find any examples out there…

    Thanks in advance for some tips where to look.

  13. Delay says:

    Arne Schmid,

    One way of doing this is to subclass and override the GetContainerForItemOverride method on (DeferredLoad)ListBox – that's your chance to customize the ListBoxItem container that gets automatically generated and you can assign a Height there (with full knowledge of the corresponding model object which will be its DataContext). For some background on how containers work with ItemsControl subclasses, here's a good post to read: drwpf.com/…/itemscontrol-g-is-for-generator

    Hope this helps!

  14. Nadeem says:

    Hi David,

    I played around with the DefferedLoadListBox and the performance is really great. However, I am trying to use it for items with varying heights. I looked at the link you posted in the previous comment and I tried to override GetContainerForItemOverride but according to the article, a link between the container and the item it contains isn't established at this stage. I am still new to wpf/silverlight and I can't get my head around this problem yet. I would appreciate if you could give us more hints on how to achieve this.

    Thanks a lot for the time and effort.

    Much appreciated,

    Nadeem

  15. Delay says:

    Nadeem,

    Sorry for the confusion! GetContainerForItemOverride is the place to provide a custom container type (or just customize the default one), but PrepareContainerForItemOverride is the method I was thinking of when I talked about access to the container *and* the object for relevant customizations: msdn.microsoft.com/…/system.windows.controls.itemscontrol.preparecontainerforitemoverride(v=VS.95).aspx

    With that mistake of mine cleared up, I think you'll have an easier time of this. :)

  16. Nadeem says:

    Thanks a lot David. I have a good idea of how to do it now.

    …I should have spent more time digging for the right method but sometimes a person get's overwhelmed with the sheer amount of new info to digest :-)

    Regards,

    Nadeem

  17. Veri says:

    Hi David,

    I have tried to use the LowProfileImageLoader in a ListBox and WrapPanel as the ListBox's ItemsPanel but my image was not loaded at all :(

    Do you have any idea why?

    Regards,

    Veri

  18. Delay says:

    Veri,

    Nothing about switching to a different ItemsPanel should affect how LowProfileImageLoader works. You might try switching back to the default ItemsPanel to see if the problem remains – if so, then it's probably something else (like a broken Binding?). Alternatively maybe try WrapPanel+LowProfileImageLoader outside a ListBox to verify that works okay.

  19. Veri says:

    Hi David,

    I have tried your suggestion but it still didn't work :(

    I have a few questions though:

    1. Do I have to use the DeferredListBox or can I use the default ListBox?

    2. Can I change the LowProfileImageLoader.UriSource to catch string instead of url?

    Regards,

    Veri

  20. Delay says:

    Veri,

    1. You can definitely use LowProfileImageLoader with the default ListBox. The two classes work together, but are not *tied* together. :)

    2. I don't quite understand the question, but if you're asking if you could make UriSource a string-typed property instead of a Uri-typed property, that should work just fine (once you update the parts of the code that assume Uri).

    Hope this helps!

  21. Geobert says:

    Thank you for this lib!

    Is there a way to fade in the downloaded image? I tried from ImageOpened but I discover here that it is not triggered :(

    Thanks for any help!

  22. Delay says:

    Geobert,

    Please see the comments in this post for some information about ImageOpened: blogs.msdn.com/…/keep-a-low-profile-lowprofileimageloader-helps-the-windows-phone-7-ui-thread-stay-responsive-by-loading-images-in-the-background.aspx

    But you may be able to modify the code for LowProfileImageLoader itself to get the effect you want by adding in an Animation of the Image's Opacity property from 0->1.

    Hope this helps!

  23. Hello!

    Although I found issues, listbox perfomance with your LowProfileImageLoader class is very high (I use observable collection).

    I try to use your LowProfileImageLoader class, but there is a issue.  All is fine when I scroll listbox(or your DeferredLoadListBox) slow, but when I scroll very fast, the emulator crashes(but when I remove images from items all is good), there is no problem with your twitter example, but I can't understand what I'am doing wrong. I tried many times and different ways, but not result :( I download listbox items only per 20 and there is another small issue. Images sometimes are not refreshing immediately. Can you help me?

  24. Delay says:

    iglaweb,

    If there's a crash, you can usually find out what the exception was in the debugger and use that (plus the call stack) to figure out what went wrong. Sometimes it can help to turn on first chance exception handling in the debugg.r as well (a web search will say how to do that). Because my sample application works properly, I'm not sure offhand what would cause this – another thing to try would be incrementally commenting stuff out of your application until the problem goes away and using that to see what the specific cause might be.

    Hope this helps!

  25. Pete says:

    Hi David,

    great stuff! Wouldn't it be a good idea to have an option to store the downloaded file in isolated storage for later use? And then automatically check whether the icon is already available in isolated storage and load it from there instead of the internet?

  26. Delay says:

    Pete,

    Sure, that's a neat idea. Shawn Burke actually wrote a framework for that called AgFx: github.com/…/AgFx. You may want to check that out; my understanding is that it supports exactly the scenario you're interested in.

    Good luck!

Comments are closed.