The blog of dlaa.me

Never do today what you can put off till tomorrow [DeferredLoadListBox (and StackPanel) help Windows Phone 7 lists scroll smoothly and consistently]

In my previous post about how LowProfileImageLoader helps the Windows Phone 7 UI thread stay responsive by loading images in the background, I began with the following introduction:

When writing applications for small, resource-constrained scenarios, it's not always easy to balance the demands of an attractive UI with the requirements of fast, functional design. Ideally, coding things the natural way will "just work" (the so-called pit of success) - and it usually does. But for those times when things need some extra "oomph", it's nice to have options. That's what my latest project, PhonePerformance is all about: giving developers performance-focused alternatives for common Windows Phone 7 scenarios.

 

In this second post, I'll be demonstrating the use of my DeferredLoadListBox class in conjunction with the Silverlight StackPanel in order to get good performance from a scenario that's pretty common for social media applications: a scrolling list of entries with a picture and a brief bit of text. (Ex: Twitter posts, Facebook updates, blog comments, and the like.) As usual, I've written a sample application to show what I'm talking about - it displays a simple list of about 200 image+name pairs from the web (the followers of my Twitter account [you know who you are! :) ]). You can see a screen shot of the "List Scrolling" sample below:

PhonePerformance List Scrolling sample
Note: Everything in this post pertains to the latest (internal) Windows Phone 7 builds, not the public Beta bits. Although I'd expect things to work the same on the Beta build, I haven't verified that because the final bits will be released to the public on September 16th.
Additional note: My discussion assumes all testing is done on actual phone hardware. Although running the emulator on a PC is a fantastic development experience, performance of applications in the emulator can vary significantly. The emulator is great for writing new code and fixing bugs, but performance work should be done on a real phone if at all possible.

 

Okay, go ahead and run the sample and wait a moment for it to load the user list from the web (and enable those two buttons). Then choose the "default" (left) or "performance" (right) scenario and watch the behavior of the little blue dots moving across the screen as the list content populates. (I discussed the motivation behind the blue dots in the previous post - for now just remember that when the dots are flowing smoothly, life is good - and when the dots get jumpy or stop animating completely, the application is unresponsive.)

Right now, we're interested in the different load times of the two scenarios. The "performance" side uses a StackPanel to get good scroll performance, but making that switch without doing anything else is likely to increase load times versus the default VirtualizingStackPanel used by the Windows Phone ListBox (because of the loss of virtualization; more on this later). That's why the sample also uses DeferredLoadListBox - to offset the performance loss by re-introducing enough pseudo-virtualization to bring performance back up to where it was. And as the sample application shows, the load times of the two lists are very similar. (Granted, neither is instantaneous - but it's also not the case that one is consistently faster than the other.)

Now that both lists are populated, the real experiment begins: go ahead and scroll the "default" (left) list up and down and watch the item content as you do so. When scrolling at slow or moderate speeds, the movement will be smooth and the experience will be great. But once you start scrolling quickly, things begin to break down - you may start to see brief glimpses of missing items, flickering, or even black-outs...

So with that baseline experience in mind, it's time to scroll the "performance" (right) list to see how it compares. The first thing you'll probably notice is that it's slower to load the images - that's due to the use of LowProfileImageLoader and was the topic of my previous post. The next thing you'll probably notice is that the scrolling is smooth for just about any speed - and especially for content that's already been on the screen at least once! I won't claim scrolling is perfect with StackPanel and DeferredLoadListBox, but it has been my experience (and that of others) that it can be notably better than the default behavior.

 

To understand why the DeferredLoadListBox+StackPanel combination is effective, it's necessary to understand a little about how VirtualizingStackPanel works - and why that ends up being a problem in this scenario...

Pretty much every Panel implementation for Silverlight and WPF works by measuring and arranging all its elements according to various layout guidelines (ex: stack, grid, etc.). This makes it straightforward to implement a custom Panel, but it also means that all the Children must be created and live in the visual tree from the beginning. Because in the vast majority of cases there are only a handful of children (and they're all on screen anyway), this isn't a problem. However, in the "really long list" scenario, there are often only 5-10 items on the screen at a time - and a few hundred other items that aren't. VirtualizingStackPanel works in conjunction with ItemsControl and ItemContainerGenerator to create only those elements that are actually on the screen. As the user scrolls the list, VirtualizingStackPanel automatically creates containers for any items about to come into view and recycles the containers for items that just scrolled out of view. (In reality, there's usually a screen's worth of buffer on either side.) Consequently, there are two big wins with VirtualizingStackPanel: load time (by virtue of creating only a fraction of the total elements) and memory consumption (by virtue of keeping only a fraction of the total elements around at any time).

But (as sometimes happens in life) VirtualizingStackPanel's strength is also its weakness. All that container recycling and item juggling takes time to execute - and that's precious time on the UI thread that can't be spent doing other things (like updating the UI). So what seems to happen when a list is scrolling quickly enough is that the VirtualizingStackPanel falls behind more and more - until some of those containers that are scrolling into view are blank because they haven't been created or populated yet! And that's when the user sees visual glitches and rendering issues...

Therefore, the fundamental approach I've taken to avoiding problems in scenarios like this is to replace the ListBox's VirtualizingStackPanel ItemsPanel with a StackPanel. This is quite easy to do - and the StackPanel's non-virtualizing nature means that scrolling long lists will be smooth as silk. However, there are two downsides to making this switch: load time is considerably longer and memory use will be significantly higher. The increased memory consumption probably won't be an issue with small- or medium-sized lists, but it could start to be a problem for large lists containing thousands of items. But while there are options for mitigating this, that's not my objective and I won't be going into them here. (Besides, I'm not sure how practical it is for people to scroll super-long lists, anyway!)

The way I avoid longer load times is by using the DeferredLoadListBox class I wrote for just this purpose - what it does is hold off on populating off-screen containers until they're about to show up on the screen. In this manner, it restores some of the benefits of VirtualizingStackPanel by making the relevant portion of the load time (roughly) independent of the total number of items in the list. But it's important to note that DeferredLoadListBox works in one direction only! Although there's no reason it couldn't "re-virtualize" items as they scroll out of view, it specifically doesn't do so because doing (and undoing) that would consume precious CPU cycles. DeferredLoadListBox is really only about softening the blow of switching from VirtualizingStackPanel to StackPanel - it's not about trying to re-implement VirtualizingStackPanel's fundamental behavior.

So with scrolling glitches avoided and load time back to where it started, there's just one other thing slowing things down and detracting from the user experience: the jumpiness that takes place during the first full scroll of the list. What's going on there is that the first full scroll causes all those "pseudo-virtualized" containers to be created - which causes the corresponding images to be downloaded from the web. We've established that downloading images on the UI thread is bad for performance, and this is exactly the scenario I created LowProfileImageLoader for! So by throwing LowProfileImageLoader into the mix, the first full scroll stays responsive, too.

And at this point, we've arrived at the smooth, pleasing, consistent scrolling experience you get from the "List Scrolling" sample's "performance" column! Yay us! :)

 

Of course, every application - and every scenario - is different, so there's no guarantee StackPanel, DeferredLoadListBox, or LowProfileImageLoader will help all the time (or even most of the time!). But what's nice is that it's extremely easy to try them out (alone or together), so there's nothing to lose by trying them out if your scenario seems likely to benefit!

 

[Click here to download the compiled PhonePerformance assembly, sample applications, and full source code for everything.]

 

To show what I mean, here's what the default scenario looks like:

<ListBox ItemsSource="{Binding Followers}">
    <ListBox.ItemTemplate>
        <DataTemplate>
            <Grid Height="50">
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="50"/>
                    <ColumnDefinition Width="10"/>
                    <ColumnDefinition/>
                </Grid.ColumnDefinitions>
                <Rectangle
                    Grid.Column="0"
                    Fill="{StaticResource PhoneChromeBrush}"/
                <Image
                    Grid.Column="0"
                    Source="{Binding ProfileImageUrl}"
                    Width="48"
                    Height="48"/>
                <TextBlock
                    Grid.Column="2"
                    Text="{Binding ScreenName}"
                    VerticalAlignment="Center"/>
            </Grid>
        </DataTemplate>
    </ListBox.ItemTemplate>
    <ListBox.ItemContainerStyle>
        <Style TargetType="ListBoxItem">
            <Setter Property="Height" Value="50"/>
        </Style>
    </ListBox.ItemContainerStyle>
</ListBox>

And here's all it takes to convert it to use StackPanel, DeferredLoadListBox, and LowProfileImageLoader:

<delay:DeferredLoadListBox ItemsSource="{Binding Followers}">
    <delay:DeferredLoadListBox.ItemTemplate>
        <DataTemplate>
            <Grid Height="50">
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="50"/>
                    <ColumnDefinition Width="10"/>
                    <ColumnDefinition/>
                </Grid.ColumnDefinitions>
                <Rectangle
                    Grid.Column="0"
                    Fill="{StaticResource PhoneChromeBrush}"/>
                <Image
                    Grid.Column="0"
                    delay:LowProfileImageLoader.UriSource="{Binding ProfileImageUrl}"
                    Width="48"
                    Height="48"/>
                <TextBlock
                    Grid.Column="2"
                    Text="{Binding ScreenName}"
                    VerticalAlignment="Center"/>
            </Grid>
        </DataTemplate>
    </delay:DeferredLoadListBox.ItemTemplate>
    <delay:DeferredLoadListBox.ItemContainerStyle>
        <Style TargetType="ListBoxItem">
            <Setter Property="Height" Value="50"/>
        </Style>
    </delay:DeferredLoadListBox.ItemContainerStyle>
    <delay:DeferredLoadListBox.ItemsPanel>
        <ItemsPanelTemplate>
            <StackPanel/>
        </ItemsPanelTemplate>
    </delay:DeferredLoadListBox.ItemsPanel>
</delay:DeferredLoadListBox>

(Don't forget to add the appropriate XMLNS to the top of the XAML file:)

xmlns:delay="clr-namespace:Delay;assembly=PhonePerformance"

 

The only thing I haven't talked about yet is the requirement that each ListBoxItem container needs to have a fixed height (via the ItemContainerStyle property; see above). This is currently necessary because it enables some optimizations in DeferredLoadListBox - however, it's important to note there's no need for all the containers to have the same fixed height - just that they all need to have a fixed height.

 

Aside: As it exists today, DeferredLoadListBox only works with vertical scrolling lists. Of course, the same concepts can be applied to horizontally scrolling lists, too, but because that's not consistent with the UI conventions of Windows Phone 7, I haven't tried to generalize the code to support both orientations. I optimized for the performance of the common scenario - but if you'd like to tweak things to support horizontal scrolling instead - or as well! - it should be fairly straightforward to do. :)

Keep a low profile [LowProfileImageLoader helps the Windows Phone 7 UI thread stay responsive by loading images in the background]

When writing applications for small, resource-constrained scenarios, it's not always easy to balance the demands of an attractive UI with the requirements of fast, functional design. Ideally, coding things the natural way will "just work" (the so-called pit of success) - and it usually does. But for those times when things need some extra "oomph", it's nice to have some options. That's what my latest project, PhonePerformance is all about: giving developers performance-focused alternatives for common Windows Phone 7 scenarios.

Note: Everything in this post pertains to the latest (internal) Windows Phone 7 builds, not the public Beta bits. Although I'd expect most everything to work on the Beta build, I haven't tested it. While that might seem a bit premature, the team has committed to releasing the final development tools on September 16th, so I'm only leading the curve a little bit here. :)

 

In this first post, I'll be demonstrating the use of the LowProfileImageLoader class. (A future post will explore the DeferredLoadListBox class which is also part of the sample download - but for now I don't want to get into what that's all about.) LowProfileImageLoader is meant to address a very specific scenario: loading lots of images from the web at the same time. This turns out to be more common than you might expect - just think about all the social media scenarios where every user has a little picture alongside their content. To help show what I'm talking about, I created the "Image Loading" sample application shown below - just run the sample, wait a little while for it to load the user list from the web (and enable the two buttons), then choose the "default" (left) or "performance" (right) scenario and watch the behavior of the little blue dots moving across the screen:

PhonePerformance Image Loading sample
Aside: My discussion assumes all testing is done on actual phone hardware. Although running the emulator on the desktop is a fantastic development experience, performance of applications in the emulator can vary significantly. The emulator is great for writing new code and fixing bugs, but performance work should be done on a real phone if at all possible.

The ProgressBar element just below the title text has its IsIndeterminate property set to true, so it's always animating. And as others have explained, ProgressBar animates entirely on the UI thread - so while it might not be a good choice for real applications, it makes a great test tool! In this case, I'm using it to provide an indication of how overwhelmed the UI thread is - and therefore how responsive the application will be to user input or updates. If the dots are flowing smoothly, then life is good; if the dots get jumpy or stop animating completely, then the application is unresponsive.

As you can see by trying the "default" side of the sample, populating an ItemsControl having a WrapPanel (actually, it's my BalancedWrapPanel!) ItemsPanel with a couple hundred small (48x48) images from the web (the followers of my Twitter account in case you're curious) hangs the application for a noticeable amount of time. The first part of the delay is simply the cost of applying the list to the container and the second part of the delay is the cost of downloading and processing those images (mostly on the UI thread).

In an attempt to avoid this extended period of unresponsiveness, the "performance" side of the sample uses LowProfileImageLoader. Although the initial cost of binding the list is the same as before, using LowProfileImageLoader allows the UI thread to become responsive much sooner. The obvious side effect is that the images load more slowly - but the fact that those ProgressBar dots stay active the entire time demonstrates that the UI thread isn't hung like it is for the default scenario. Think about it like this: LowProfileImageLoader trades off image load speed in favor of application responsiveness.

 

The way LowProfileImageLoader works is straightforward: it creates a worker Thread and performs as much work there as possible. As soon as the SourceUri attached property is set, that Uri is enqueued for the worker thread to process. Similarly, whenever an asynchronous response comes in, that's also queued for the worker thread to process. Meanwhile, the worker thread is looking for work on any of its (three) queues and processing it in batches for efficiency. At the same time, it's making regular calls to Thread.Sleep(1) which signals to the system that other threads with work to do should get priority. (True thread priorities would be ideal, but Thread.Priority doesn't exist in Silverlight.) Finally, when it has done as much as it can off the UI thread, the worker thread calls Dispatcher.BeginInvoke to perform the final BitmapImage operations on the UI thread where they need to take place (in batches, of course). The net effect of all of this is that all the image loading LowProfileImageLoader performs occurs on a single worker thread which does its best to be efficient and stay out of the UI thread's way as much as possible - which results in a happier UI thread and a less frustrated user. :)

 

Of course, every application - and every scenario - is different, so there's no guarantee LowProfileImageLoader will help all the time (or even most of the time!). But what's nice is that it's really easy to hook up, so there's nothing to lose by trying it if your scenario seems relevant!

 

[Click here to download the compiled PhonePerformance assembly, sample applications, and full source code for everything.]

 

To make things a tad more concrete, here's what a typical scenario looks like:

<Image
    Source="{Binding ProfileImageUrl}"
    Width="24"
    Height="24"/>

And here's all it takes to convert it to use LowProfileImageLoader:

<Image
    delay:LowProfileImageLoader.UriSource="{Binding ProfileImageUrl}"
    Width="24"
    Height="24"/>

(Remember to add the appropriate XMLNS to the top of the XAML file:)

xmlns:delay="clr-namespace:Delay;assembly=PhonePerformance"

 

PS - As an added bonus, the first 500 callers will receive a free (single-purpose, super bare-bones) Twitter API! (NIH disclaimer: I know there are Twitter libraries out there, but I generally avoid third party code because I don't want to deal with licensing issues. Yeah, it's a little paranoid, but it helps me sleep better at night - and besides, this was really simple to dash off.) Operators are standing by...

Your phone can turn into a robot [LayoutTransformer works great on the Windows Phone platform]

The WPF platform offers RenderTransform and LayoutTransform. Silverlight - being considerably smaller and a bit simpler - has only RenderTransform. Which is usually enough - except when it's not! :)

So I wrote LayoutTransformControl a while back in order to bring LayoutTransform to the Silverlight platform. It was so useful/popular that we included it in the Silverlight Toolkit with the name LayoutTransformer [insert robot sounds here]. That post includes links to a bunch of my posts about LayoutTransformControl/LayoutTransformer, but the most important one for newcomers is probably the original "motivational" post.

 

Here's the relevant portion:

People who want to rotate visual elements in Silverlight are likely to use RotateTransform within RenderTransform - but they may not always get the results they expect! For example, using RenderTransform to achieve the following effect:

Sweet

Actually renders like this:

Whoops

But the problem isn't with RenderTransform - it's with using the wrong tool for the job! By design, RenderTransform applies its transformations (a rotation in this case) after the layout system has performed its measure/arrange pass. So when the elements in the example are being measured and arranged, the text is still horizontal. It's only after everything has been positioned that the text is finally rotated - and ends up in the "wrong" place. While it's possible to correct for this discrepancy by hard-coding all the relevant offsets in the XAML (very brittle and error-prone) or by adjusting all the offsets in code (only slightly more flexible - and a lot more work), these aren't great alternatives.

The right tool for the job is LayoutTransform which applies its transformations before the layout pass. With LayoutTransform, the text in the example is already rotated by the time the elements are measured and arranged, and the desired effect can be achieved quite simply.

But there's a catch: LayoutTransform doesn't exist in Silverlight [and therefore Windows Phone]...

However, there's no reason to let that stop us. Rotation is rotation whenever it happens, so maybe there's a way to get the already-optimized RenderTransform implementation to do the real work earlier in the layout pass...

 

Lately, I've been contacted by a number of customers asking if LayoutTransform worked in Windows Phone applications or having trouble referencing it from the relevant Silverlight 3 Toolkit assembly. So I figured it would be good to verify this for myself and make it even easier for people to use!

There are lots of compelling scenarios for LayoutTransform, but the most common is definitely rotating text and images. So here's my simple "bookshelf" sample which highlights a few of my favorite programming books:

LayoutTransformer on Windows Phone

The tricky part of creating a layout like this without LayoutTransformer would have been getting those vertical book titles aligned properly on the spines of those books without hardcoding a bunch of positioning data (which would have broken as soon as anyone touched the XAML). But LayoutTransformer makes this a piece of cake - just wrap the content, rotate it, and everything automatically works just how you'd expect it to! You can change font sizes, margins, alignment - whatever - and never have to worry about your layout going funky.

 

[Click here to download the complete LayoutTransformerOnWindowsPhone sample and pre-compiled assembly]

 

When you're ready to add LayoutTransformer to your own application, just add a project reference to the LayoutTransformer.dll assembly (found in the root of the ZIP download) and add the appropriate XMLNS to the top of your XAML:

xmlns:toolkit="clr-namespace:System.Windows.Controls;assembly=LayoutTransformer"

After that, wrap anything you want in a LayoutTransformer and apply the relevant transforms. Here's what the sample uses:

<toolkit:LayoutTransformer>
    <toolkit:LayoutTransformer.LayoutTransform>
        <RotateTransform Angle="90"/>
    </toolkit:LayoutTransformer.LayoutTransform>
    <Border
        BorderBrush="{StaticResource PhoneForegroundBrush}"
        BorderThickness="1">
        <TextBlock
            Text="{Binding Title}"
            Margin="10"/>
    </Border>
</toolkit:LayoutTransformer>

 

Yep, it's really that easy!

 

PS - There's a bug in the default templates of the Windows Phone Developer Tools Beta that makes the books show up with different heights. That problem has been fixed in more recent Tools releases (as demonstrated by the screen capture above), so don't worry when you see that on the Beta - it has nothing to do with LayoutTransformer. :)

Fade Into You [Code to easily animate orientation changes for Windows Phone applications now supports fade, too!]

I've already written about my efforts to improve the orientation behavior of Windows Phone applications; while the default behavior of snapping to the new orientation is fast and easy, it lacks a certain amount of panache. So I wrote the AnimateOrientationChangesFrame class to animate changes between portrait and landscape layouts. It works well, and I know of a few upcoming Windows Phone applications that are using it.

 

Of course, the drawback to animating rotations is that the overhead for complex layouts could be enough to overwhelm the phone's resources. At which point, the animation would become choppy and the beauty of the transition would be lost...

I wanted to provide another option that would still make orientation changes interesting, but would do so more cheaply. And so I've created the FadeOrientationChangesFrame class which (surprise!) fades smoothly between the "before" and "after" orientations. Because of its simpler approach, FadeOrientationChangesFrame doesn't require any additional layout computation from the host application. What's more, its animation of the UIElement.Opacity property makes the animation eligible to run on the compositor thread (AKA the "render thread") where is the place to be when you want smooth, seamless animations!

 

Here's what the sample application looks like part of the way through a fade from portrait to landscape:

FadeOrientationChanges sample

 

If the sample application for FadeOrientationChangesFrame looks a lot like the one for AnimateOrientationChangesFrame, that's because they're practically the same. In fact, the two *OrientationChangesFrame classes have identical APIs and are wired up in exactly the same manner. If you're using one, you can switch to the other quite trivially; if you're not using either, it's easy to start! :)

 

[Click here to download the DynamicOrientationChanges sample for Windows Phone 7.]

 

Notes:

  • Not only do both classes work well with the Windows Phone Developer Tools Beta, they also run happily on the latest internal Tools builds and on real phone hardware.
  • Because I'm now offering two classes for dynamic orientation changes, I've created a dedicated DynamicOrientationChanges assembly for them both to live in. People who wish to use either class can opt to add the relevant .cs file to their project (as before), or they can reference the DynamicOrientationChanges.dll assembly directly. For everyone's convenience, I've put a pre-compiled copy of this assembly in the root of the sample ZIP.
  • Here are the complete directions for adding rotation/fade animation to a new application:
    1. Enable support for orientation changes by making the following change to the XAML for the relevant PhoneApplicationPages (note that rotation is not enabled by default for new pages):
      SupportedOrientations="Portrait"
      SupportedOrientations="PortraitOrLandscape"
    2. Verify (by running in the emulator) the pages you expect to support rotation really do - if things aren't rotating at this point, trying to animate the rotations won't help. :)
    3. Add the AnimateOrientationChangesFrame.cs or FadeOrientationChangesFrame.cs source code file from the sample to your project/solution.

      -OR-

      Add a reference to the pre-compiled DynamicOrientationChanges.dll assembly from the sample download to your project/solution.

    4. Open App.xaml.cs and change the RootFrame initialization in the InitializePhoneApplication method (you may need to expand the Phone application initialization region at the bottom):
      RootFrame = new PhoneApplicationFrame();
      RootFrame = new Delay.AnimateOrientationChangesFrame();

      -OR-

      RootFrame = new Delay.FadeOrientationChangesFrame();
    5. Done; rotations will now be animated! To customize the animation, use the Duration, EasingFunction, and IsAnimationEnabled properties which are all present and continue to work as outlined in the original post.
  • Though the *OrientationChangesFrame classes share a decent amount of code, I have deliberately kept them separate for now so each .cs file is completely self-contained. This makes it easy for anyone to incorporate either class into their project without worrying about pulling in a bunch of dependent files.
  • Fellow Microsoft employee (and former teammate from long, long ago) Mike Calligaro ran across a bug in AnimateOrientationChangesFrame where elements started getting clipped incorrectly after a couple of orientation changes. Looking into this, I discovered that my attempt to save a few cycles by calling InvalidateArrange instead of InvalidateMeasure was somewhat misguided. I changed the implementation to call InvalidateMeasure and that simple tweak fixed the bug. Aside from this, AnimateOrientationChangesFrame is exactly the same as last time.
  • The Beta phone emulator has a bug where it frequently fails to notify applications about an orientation change. This problem has nothing to do with my code (you can reproduce it by enabling rotation for any application), but shows up regularly when playing around with the sample app in the emulator. When the orientation gets "stuck" like this, just rotate a few more times and things sort themselves out. (Fortunately, this problem does not exist on actual phone hardware or on more recent builds of the emulator!)

 

In today's dynamic world, there's no need to settle for boring transitions - go ahead and spice up your Windows Phone application by making use of FadeOrientationChangesFrame or AnimateOrientationChangesFrame today!

My new home page, rejuvenated [Updated collection of great Silverlight/WPF/Windows Phone Data Visualization resources!]

It's been a few months since I posted my previous collection of Silverlight/WPF Charting links. In the meantime, the April 2010 release of the Silverlight Toolkit was published with support for stacked series and significant performance improvements! And Windows Phone 7 has been steadily building momentum - it's handy that the Data Visualization assembly also works on Windows Phone!

So there's lots of good stuff - here are all the links (FYI: previously published links are gray):

Overviews (100 level)

Scenarios (200 level)

Internals (300 level)

Team Member posts (Partner level)

My posts (Ego level)

Many thanks go out to everyone who has spent time helping people learn how to use Silverlight/WPF/Windows Phone Data Visualization!

PS - If I've missed something useful, please send me a link - I'm always happy to find more great content! :)

PPS - The most recent version of this collection will always be pointed to by http://cesso.org/r/DVLinks. If you're going to create a favorite or link to this post, please use that URL so your link will always be current.

Why didn't I think of that in the first place? [Windows Phone 7 Charting example updated to include reusable, platform-consistent Style and Templates]

I've previously blogged about how to get the Data Visualization assembly from the Silverlight Toolkit/WPF Toolkit working on Windows Phone. It's quite simple with my Data Visualization Development Release 4 and the Windows Phone Developer Tools Beta because it's now as easy as adding a reference to two assemblies and then creating some charts! However, those charts start out with visuals meant for use with Silverlight running in the web browser (white background, etc.), not Silverlight running on Windows Phone (black background, etc.). One of the things I did in a previous post was to customize the appearance of the sample charts so they fit in better with the phone conventions. Because Charting has come up quite a bit with customers lately, I wanted to do a quick follow-up to help make things even easier to use!

 

Sample in landscape orientation

 

Accordingly, I've made the following improvements to the DataVisualizationOnWindowsPhone sample:

  • Created the PhoneDataVisualizationResources.xaml file. It contains a ResourceDictionary with PhoneChartStyle, PhoneChartPortraitTemplate, and PhoneChartLandscapeTemplate. These are the same basic custom customized Style and ControlTemplates from before, but now they're named according to the platform convention for resources and bundled in a single, self-contained XAML file. This makes it easy for customers to incorporate the Windows Phone look into their charting applications.
  • Modified the sample to merge the contents of PhoneDataVisualizationResources.xaml into the application-level ResourceDictionary via MergedDictionaries in App.xaml:
    <Application.Resources>
        <!-- Merge resources from PhoneDataVisualizationResources.xaml (Build Action=Page) -->
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionarySource="/DataVisualizationOnWindowsPhone;component/PhoneDataVisualizationResources.xaml"/>
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Application.Resources>
    With the customizations now located off in their own dedicated file, the Chart in MainPage.xaml is simple and uncluttered:
    <charting:Chart
        x:Name="myChart"
        Title="my activity"
        LegendTitle="legend"
        Style="{StaticResource PhoneChartStyle}">
        <!-- ... -->
    </charting:Chart>
  • Changed the fills for DataPoints to use a solid color instead of a gradient. The default Brush used to paint the inside of data points has a gradient effect which looks quite nice on the desktop, but that gradient exhibits noticeable color banding on the actual phone because the displays used for mobile devices are often incapable of displaying the full range of colors humans perceive. While it's possible to minimize the impact of this by dithering (here's a nice article that shows how), you don't get that for free. Because gradients aren't consistent with the "Metro" user interface guidelines anyway, I've made all the default colors solid (as seen in the image at the start of the post).
  • Switched to using standard phone resources (ex: PhoneForegroundBrush) where applicable so Charts automatically respect the current phone appearance. Specifically, this enables seamless support for the "Dark" and "Light" phone themes without any effort on the part of the developer/designer.
  • Customized the Legend Template to include a horizontal scroll bar when rendered (horizontally) in Portrait orientation. The default Legend for Landscape orientation already had a vertical scroll bar, so this change makes it possible to scroll long lists in both layouts.
  • Switched to the official Silverlight 3 System.Windows.Controls.dll assembly now that the platform supports using strongly-named assemblies.

 

[Click here to download the updated Windows Phone 7 Data Visualization sample application.]

 

Important: After downloading the ZIP file above, you should "unblock" it before extracting its contents to avoid warnings from Visual Studio: Security Warning, You should only open projects from a trustworthy source. and The "ValidateXaml" task failed unexpectedly. System.IO.FileLoadException: Could not load file or assembly .... Unblocking is a simple matter of right-clicking on the ZIP file, choosing Properties from the context menu, then clicking the "Unblock" button in the lower, right-hand corner of the resulting dialog and hitting "OK" or "Apply":

Unblock button in Properties dialog

If they build it, I will come (and link to it) [WPPFormatter plug-in now available for TextAnalysisTool.NET]

I went public with TextAnalysisTool.NET a few years back - it's a handy tool for interactive log file analysis that's popular with people in many parts of the company. The introductory post has more background and detail, but this snippet from the README gives an overview:

The Problem: For those times when you have to analyze a large amount of textual data, picking out the relevant line(s) of interest can be quite difficult. Standard text editors usually provide a generic "find" function, but the limitations of that simple approach quickly become apparent (e.g., when it is necessary to compare two or more widely separated lines). Some more sophisticated editors do better by allowing you to "bookmark" lines of interest; this can be a big help, but is often not enough.

The Solution: TextAnalysisTool.NET - a program designed from the start to excel at viewing, searching, and navigating large files quickly and efficiently. TextAnalysisTool.NET provides a view of the file that you can easily manipulate (through the use of various filters) to display exactly the information you need - as you need it.

And here's an animated GIF showing TextAnalysisTool.NET working with some MSBuild output:

TextAnalysisTool.NET demonstration

 

I don't talk about it in the introductory post, but one of the things TextAnalysisTool.NET supports is a flexible plug-in architecture for handling custom file formats. Here's what the included documentation says:

TextAnalysisTool.NET's support for plug-ins allows users to add in their own code that understands specialized file types. Every time a file is opened, each plug-in is given a chance to take responsibility for parsing that file. When a plug-in takes responsibility for parsing a file, it becomes that plug-in's job to produce a textual representation of the file for display in the usual line display. If no plug-in supports a particular file, then it gets opened using TextAnalysisTool.NET's default parser (which displays the file's contents directly). One example of what a plug-in could do is read a binary file format and produce meaningful textual output from it (e.g., if the file is compressed or encrypted). Another plug-in might add support for the .zip format and display a list of the files within the archive. A particularly ambitious plug-in might translate text files from one language to another. The possibilities are endless!

 

Over the years, I've been contacted by various people wanting to use the plug-in architecture to add support for specialized file formats. One of those people is Tomer Rotstein who recently released a WPPFormatter plug-in. (If the acronym "WPP" isn't familiar to you, you might start by reading the Windows software trace preprocessor entry on Wikipedia - it's the file format used by the event tracing infrastructure in Windows.) But Tomer has gone above and beyond what I ever had in mind - as you can see from the following screen shot of WPPFormatter's "open file" dialog:

WPPFormatter demonstration

I encourage people to read Tomer's post to get a full sense of what WPPFormatter does. There's a download link at the end of that post, so please try it out if it seems useful!

 

And for those of you who aren't already TextAnalisisTool.NET users, please:

[Click here to download the latest version of TextAnalysisTool.NET.]

 

Aside: Tomer's accomplishment is even more notable when you realize that TextAnalysisTool.NET was the first .NET application I ever wrote and that it targets .NET 1.1 - from a time before there were generics, extensibility frameworks, and all the other goodness we've come to take for granted! Truth be told, the plug-in model for TextAnalysisTool.NET is downright wacky in some ways, so I congratulate Tomer on succeeding in spite of my goofy design. :)

New phone bits, same pretty charts [Upgraded my Windows Phone 7 Charting example for the Windows Phone Developer Tools Beta]

The recent release of the Windows Phone Developer Tools Beta introduces a variety of notable changes to the Windows Phone platform and I've been meaning to update my Data Visualization on Windows Phone sample so I could continue to refer people to it for educational purposes. (Just like I did when the April Tools Refresh came out.) I finally got some time and have updated the sample so it targets the Beta Tools and works "out of the box".

 

Sample in portrait orientation Sample in landscape orientation

 

[Click here to download the updated Windows Phone 7 Data Visualization sample application.]

 

Rather than relying on an automated project upgrade, I simply diff-ed the existing project against a new project I created width the Beta Tools and made the necessary changes myself in Notepad. It was all pretty straightforward - here are the things that stood out for me:

  • App.xaml.cs: There's a lot more initialization code by default.
  • App.xaml: There's no longer a need to bake all those phone-specific styles into every application, so the default App.xaml is nearly empty (like it should be)! Also, the new PhoneApplicationService events are hooked up by default.
  • SplashScreenImage.jpg: An appropriately-named splash screen is now created and used automatically.
  • MainPage.xaml: The platform-specific Phone* assemblies and default namespaces have shifted around a bit. SupportedOrientations is now set in XAML instead of code.
  • DataVisualizationOnWindowsPhone.csproj: The WINDOWS_PHONE symbol is now #define-ed by default, giving shared code now a standard way of detecting the Windows Phone platform. The phone assembly references are slightly different and the splash screen is included by default.
  • WMAppManifest.xml: The property PlaceHolderString on the DefaultTask element has been renamed.

While I was at it, I also tweaked the way the data source is created (by making it a class and moving it to the Resources section) so now the charts show up at design-time, too. I'm using the exact same System.Windows.Controls.DataVisualization.Toolkit.dll and System.Windows.Controls.dll assemblies as before (see notes 4 and 5 of the previous post for more context) - though there should no longer be a need to "un-sign" the Silverlight Toolkit assembly to use it on Windows Phone.

 

This was all pretty basic stuff and it's important to note that none of the application code was affected. Changes like this are a natural part of the platform and project templates maturing - and it's great to see improvements like the default styles moving into the platform!

Hopping outside the box [How to: Leapfrog Bindings (bridge DataContexts) in Silverlight and WPF]

The data binding infrastructure for Silverlight and WPF is extremely powerful and makes it easy to write powerful, dynamic applications without worrying about synchronizing the UI with every change to the underlying data. By leveraging this power, developers can think entirely in terms of the data model and be confident that any changes they make will be reflected by the interface automatically.

The way this works is that every element has a DataContext property, and the value of that property is inherited. DataContext can be assigned any object and acts as the default source for all Bindings applied to the element's properties. The object acting as DataContext can be as simple or complex as an application needs and is free to "shape" the data however is most convenient for the UI and Bindings. Collection-based controls like ItemsControl (and its subclasses ListBox, TreeView, etc.) automatically set the DataContext of their containers to the specific item they represent. This is what you'd expect and enables a great deal of flexibility when displaying collections. However, each element has a only one DataContext - so sometimes it's useful to "reach outside" that and bind to properties on some other object.

The good news is that there's an easy way to leapfrog the DataContext and "bridge" the two elements! The trick is to use an ElementName Binding to point to that other element - then to root the Binding's Path at that element's DataContext and "dot-down" to the property of interest. An example should make things clearer...

 

LeapFroggingDataContexts sample

The sample above has an application-wide DataContext model object ApplicationViewModel containing a bool property ShowHeight that's TwoWay-bound to the CheckBox control. For convenience, ShowHeight is also exposed as a Visibility value via HeightVisibility - which is much more convenient to bind the Visibility property to. There's also a Mountains property containing a collection of MountainViewModel model objects. Each mountain in the list is meant to show or hide its height according to the setting on ApplicationViewModel - but because the DataContext of the ListBoxItem containers is set to instances of MountainViewModel, they can't "see" the HeightVisibility property... The most direct option is probably to propagate this value "down" to the MountainViewModel instances by adding a property to the class for that purpose and keeping it in sync with the "real" property on ApplicationViewModel.

However, that feels like a bit of overkill for the current situation. Instead, if we just "leapfrog" the Binding in the item content, we can make use of the ApplicationViewModel directly and everything stays nice and simple!

 

[Click here to download the Silverlight 4 source code for the LeapFroggingDataContexts sample.]

 

Here's what it looks like in XAML:

<UserControl x:Class="LeapFroggingDataContexts.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:LeapFroggingDataContexts">

    <UserControl.Resources>
        <local:ApplicationViewModel x:Key="ApplicationViewModel"/>
    </UserControl.Resources>

    <Grid
        x:Name="LayoutRoot"
        DataContext="{StaticResource ApplicationViewModel}"
        Width="150"
        HorizontalAlignment="Center"
        VerticalAlignment="Center">

        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition/>
        </Grid.RowDefinitions>

        <CheckBox
            Grid.Row="0"
            Content="Show height"
            IsChecked="{Binding ShowHeight, Mode=TwoWay}"/>

        <ListBox
            Grid.Row="1"
            ItemsSource="{Binding Mountains}">
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <StackPanel Orientation="Horizontal">
                        <TextBlock Text="{Binding Name}"/>
                        <TextBlock
                            Text="{Binding Height, StringFormat=' [{0:n0}m]'}"
                            Visibility="{Binding DataContext.HeightVisibility, ElementName=LayoutRoot}"/>
                    </StackPanel>
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>
    </Grid>
</UserControl>

ElementName points the Binding at an element with the DataContext of interest, then the Path selects the DataContext as a starting point (an instance of the ApplicationViewModel class in this case), hooks up to that object's HeightVisibility property - and we're done!

 

While architectural purists might argue this technique is bad form, I've seen it come in handy for enough customer scenarios that it seems a worthwhile approach to keep in mind. Please don't abuse it - but feel free to use it! :)

Spin Spin Sugar [Updated code to easily animate orientation changes for any Windows Phone application]

A few weeks after the initial release of the Windows Phone Developer Tools in April, I blogged a sample showing how to easily animate device orientation changes for Windows Phone 7 applications. I noted at the time that the default behavior for application projects is no animation - the app immediately switches to the new orientation when the device is rotated (click here for a brief video of the default behavior). While this is clearly the simplest approach, I wanted to enable something a little fancier by smoothly animating the application's rotation from one orientation to another - just like the built-in applications do.

To show what I'm talking about, here's a video of my implementation running on the emulator:

Video of animated orientation change behavior

FYI: The buttons in the video rotate the device one quarter turn clockwise or counter-clockwise. I begin by rotating counter-clockwise once, then clockwise once back to the starting orientation, then clockwise three more times to loop all the way around. And then show off just a bit. :)

Note: I made that video back in April; the emulator looks a little different now, but the animation works just the same.

 

As of yesterday's release of the Windows Phone Developer Tools Beta, some of the platform changes have invalidated my original implementation. But that's okay, because I've updated the code to work with the latest release - and made a few improvements along the way! :)

 

[Click here to download the AnimateOrientationChanges sample for Windows Phone 7.]

 

Notes:

  • [Please review the "Notes" section of my previous blog post for additional information.]
  • The implementation is now based off PhoneApplicationFrame which simplifies things a bit by avoiding the need to track state across pages. Conveniently, this also makes the setup process a bit easier. What's more, I was able to get rid of the TranslateTransform based on a suggestion by Seema Ramchandani, one of the Silverlight performance experts. And I've added a bit of code to avoid unnecessary calls to InvalidateArrange which helps free up a few extra CPU cycles.
  • One of my earlier concerns was that I hadn't been able to run code on actual Windows Phone hardware, so I wasn't sure the snappy performance I saw under the emulator carried over to the device. Fortunately, I've gotten access to an actual prototype and have verified the sample application runs quite smoothly in the real world, thank you very much. :)
  • That said, my sample is pretty simple - Seema expressed concern about possible slowness for the more complex layouts real applications might use. And while I share her concerns, the options for improving the situation in such cases all seem like they'll degrade the visual quality of the rotation animation. The current implementation gives the best quality it can on any device for any layout (and automatically degrades as necessary to catch up if it falls behind); the alternatives create animations that just won't be as smooth. :( So I'm reluctant to start down that path until I know it's necessary. Please let me know what your experience is - whether good or bad - so I can make an informed decision!
  • Here are the complete directions for adding rotation animation to a new application (if you're upgrading an existing application, please undo all the previous install steps first!):
    1. Enable support for orientation changes by making the following change to the XAML for the relevant PhoneApplicationPages (note that rotation is no longer enabled by default for new pages):
      SupportedOrientations="Portrait"
      SupportedOrientations="PortraitOrLandscape"
    2. Verify (by running in the emulator) the pages you expect to support rotation really do - if things aren't rotating at this point, trying to animate the rotations won't help. :)
    3. Add the AnimateOrientationChangesFrame.cs code file from the sample to your project/solution.
    4. Open App.xaml.cs and change the RootFrame initialization in the InitializePhoneApplication method (you may need to expand the Phone application initialization region at the bottom):
      RootFrame = new PhoneApplicationFrame();
      RootFrame = new Delay.AnimateOrientationChangesFrame();
    5. Done; rotations will now be animated! To customize the animation, use the Duration, EasingFunction, and IsAnimationEnabled properties which are all still present and continue to work as outlined in the previous post.
  • The phone emulator has an annoying bug where it frequently fails to notify applications about an orientation change that just happened. This problem has nothing to do with my code (you can reproduce it by enabling rotation for any application), but shows up regularly when playing around with the sample app in the emulator. When the orientation gets "stuck" like that, just rotate a few more times and things sort themselves out. (Fortunately, this problem does not exist on actual phone hardware!)
  • The PageOrientation.PortraitDown value remains unsupported with this release because the platform still does not support it.

 

On the one hand, animating orientation changes is kind of a whimsical behavior to add to an application - but on the other hand, it helps to create the kind of pleasing, platform-consistent experience that users appreciate and enjoy. AnimateOrientationChangesFrame isn't going to turn a crummy application into a good one - but it just might add some polish to help turn a good application into a great one.

I've had fun creating this sample - I hope you enjoy using it!