Delay's Blog

Silverlight, WPF, Windows Phone, Web Platform, .NET, and more...

Posts
  • Delay's Blog

    Turn your head and check out this post [How to: Easily rotate the axis labels of a Silverlight/WPF Toolkit chart]

    • 46 Comments

    When someone asked me how to rotate the axis labels of a chart from the Data Visualization package of the Silverlight Toolkit/WPF Toolkit earlier today, I realized it was time for a quick blog post. Because when I've answered a question two or three times, it's usually a pretty good sign that I'll keep on answering it for some time. I usually try to head that kind of thing off at the pass, so here's my post on the topic for the benefit of future generations. :)

    The typical scenario here is that someone has a chart and it's working well, but their axis labels are very long and end up overlapping - even after the default axis behavior of putting them in alternating rows to prevent such a problem kicks in:

    Overlapping axis labels

     

    The typical solution is to rotate the axis labels - and it's easy once you know where to look. The key here is to customize the Template of the AxisLabel instances that are used to render the labels. And it's quite simple to do so by providing a Style with a Template Setter for the AxisLabelStyle property of the Axis subclass in question:

    Rotated axis labels on WPF

    Yeah, it looks great on paper; but that description was a mouthful...

    It's probably easier to understand in XAML - here's the complete code for the sample above with the interesting part highlighted:

    <Window x:Class="WpfApplication1.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:charting="clr-namespace:System.Windows.Controls.DataVisualization.Charting;assembly=System.Windows.Controls.DataVisualization.Toolkit"
            xmlns:sys="clr-namespace:System;assembly=mscorlib"
            xmlns:spec="clr-namespace:System.Collections.Specialized;assembly=System"
            Title="RotatedAxisLabelsWPF"
            Width="500"
            Height="350">
        <Grid>
            <charting:Chart
                Title="Animals With Long Names">
                <charting:ColumnSeries
                    Title="Character count"
                    DependentValueBinding="{Binding Length}"
                    IndependentValueBinding="{Binding}">
                    <charting:ColumnSeries.ItemsSource>
                        <spec:StringCollection>
                            <sys:String>Bumblebee</sys:String>
                            <sys:String>Caterpillar</sys:String>
                            <sys:String>Hippopotamus</sys:String>
                            <sys:String>Rhinoceros</sys:String>
                            <sys:String>Velociraptor</sys:String>
                        </spec:StringCollection>
                    </charting:ColumnSeries.ItemsSource>
                    <charting:ColumnSeries.IndependentAxis>
                        <charting:CategoryAxis
                            Orientation="X">
                            <charting:CategoryAxis.AxisLabelStyle>
                                <Style TargetType="charting:AxisLabel">
                                    <Setter Property="Template">
                                        <Setter.Value>
                                            <ControlTemplate TargetType="charting:AxisLabel">
                                                <TextBlock Text="{TemplateBinding FormattedContent}">
                                                    <TextBlock.LayoutTransform>
                                                        <RotateTransform Angle="-60"/>
                                                    </TextBlock.LayoutTransform>
                                                </TextBlock>
                                            </ControlTemplate>
                                        </Setter.Value>
                                    </Setter>
                                </Style>
                            </charting:CategoryAxis.AxisLabelStyle>
                        </charting:CategoryAxis>
                    </charting:ColumnSeries.IndependentAxis>
                </charting:ColumnSeries>
            </charting:Chart>
        </Grid>
    </Window>

    Like I said, it's all pretty standard stuff once you know where to look. Of course, you can rotate the labels all the way to 90 degrees if you want them to take the least amount of space possible. But 60 degrees seemed like a suitably rakish angle. ;)

     

    Unfortunately, we can't declare "Mission Accomplished" quite yet... While the Data Visualization assembly itself works exactly the same on WPF and Silverlight, the platforms themselves aren't identical quite yet. Specifically, there's no support for LayoutTransform in Silverlight (and RenderTransform is simply not appropriate here). Fortunately, I've filled the LayoutTransform gap with my LayoutTransformer class - and it's already part of the Silverlight Toolkit!

    The syntax changes just a bit, but the concept is exactly the same:

    <UserControl x:Class="SilverlightApplication1.MainPage"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:charting="clr-namespace:System.Windows.Controls.DataVisualization.Charting;assembly=System.Windows.Controls.DataVisualization.Toolkit"
        xmlns:sys="clr-namespace:System;assembly=mscorlib"
        xmlns:toolkit="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Toolkit"
        xmlns:layout="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Layout.Toolkit">
        <Grid>
            <charting:Chart
                Title="Animals With Long Names">
                <charting:ColumnSeries
                    Title="Character count"
                    DependentValueBinding="{Binding Length}"
                    IndependentValueBinding="{Binding}">
                    <charting:ColumnSeries.ItemsSource>
                        <toolkit:ObjectCollection>
                            <sys:String>Bumblebee</sys:String>
                            <sys:String>Caterpillar</sys:String>
                            <sys:String>Hippopotamus</sys:String>
                            <sys:String>Rhinoceros</sys:String>
                            <sys:String>Velociraptor</sys:String>
                        </toolkit:ObjectCollection>
                    </charting:ColumnSeries.ItemsSource>
                    <charting:ColumnSeries.IndependentAxis>
                        <charting:CategoryAxis
                            Orientation="X">
                            <charting:CategoryAxis.AxisLabelStyle>
                                <Style TargetType="charting:AxisLabel">
                                    <Setter Property="Template">
                                        <Setter.Value>
                                            <ControlTemplate TargetType="charting:AxisLabel">
                                                <layout:LayoutTransformer>
                                                    <layout:LayoutTransformer.LayoutTransform>
                                                        <RotateTransform Angle="-60"/>
                                                    </layout:LayoutTransformer.LayoutTransform>
                                                    <TextBlock Text="{TemplateBinding FormattedContent}"/>
                                                </layout:LayoutTransformer>
                                            </ControlTemplate>
                                        </Setter.Value>
                                    </Setter>
                                </Style>
                            </charting:CategoryAxis.AxisLabelStyle>
                        </charting:CategoryAxis>
                    </charting:ColumnSeries.IndependentAxis>
                </charting:ColumnSeries>
            </charting:Chart>
        </Grid>
    </UserControl>

    Mission accomplished:

    Rotated axis labels on Silverlight

     

    There you have it: AxisLabelStyle is your new best friend. A friend with benefits, one might say, because there are other cool things you can do by customizing the AxisLabel Style.

    So please: go forth and enjoy your new friend!

  • Delay's Blog

    Silverlight "Surface" Demonstration [Silverlight implementation of Surface's "photo table" UI]

    • 43 Comments

    Yesterday, Microsoft announced its Surface product to much buzz and excitement. The demo videos I saw featured a "photos on a table" user interface that displayed a handful of photos sitting on the Surface. The interface allowed people to easily move the virtual photos around by touching them in the center and sliding them to a different location on the screen. By touching the corners instead, photos could be sized and rotated with ease.

    It struck me that this interface would be pretty easy to replicate with Silverlight and I decided to do so as a learning exercise. I spent some time on this last night and tonight and came up with an application that looks like this:

    Silverlight "Surface" Demonstration

    You can click here (or on the image above) to play with the application in your browser. The code's quite simple; click here to download the source code and play around with it yourself! (To build the project, you'll want to use Visual Studio Orcas Beta 1 and the Silverlight Tools.)

    Notes:

    • When the application loads, it randomly lays out the sample photos. To interact with a photo, simply move the mouse pointer over the photo and click+drag on one of the yellow control elements that appears. The center element moves the photo around and the corner elements all rotate/resize it. Click on any part of a photo to bring it to the top of the pile.
    • Without the snazzy Surface hardware support, this application doesn't support the Human Finger 1.0 input device. :) However, if you run it on a Tablet PC or hooked up to something like the Wacom Cintiq, you can get pretty close.
    • As expected, Silverlight's XAML support makes this kind of interface pretty easy to build. I understand the Surface UI was written using WPF (Silverlight's big brother), so I suspect some of the techniques they used are fairly similar to what's being done here.
    • The sample images are a few of my favorite wallpapers that come with Windows Vista. I have not included them with the code download because they're large and it's easy enough to copy them from your own %windir%\Web\Wallpaper directory. Or just use your own favorite images!
    • I don't demonstrate it here (partly because I don't have a good nature video), but it would be easy to add video support so that each of the photos was a streaming video instead. Hum, maybe I'll do a follow-up post... :)

    It's pretty obvious that XAML lets you do some pretty neat things with ease - I look forward to even more compelling new interfaces based on Silverlight and WPF!

  • Delay's Blog

    Bringing a bit of HTML to Silverlight [HtmlTextBlock makes rich text display easy!]

    • 42 Comments

    Lately I've seen a few people wanting to display rich text in a Silverlight application, but having no way to do so easily. Most recently, I saw Tim Heuer bump into this when displaying RSS content in a neat demo of his. The basic problem is that Silverlight's primary text display object, TextBlock, does not natively have a way to display HTML - and that's the format most rich text is in these days. It seemed that if only there were a TextBlock that took HTML as input and built up a collection of Run and LineBreak objects corresponding to that HTML, things would be easier...

    So I wrote HtmlTextBlock, a "plug-compatible" replacement for TextBlock that knows how to take simple HTML (technically XHTML, see the notes below) and display it in a manner that fairly closely approximates how a web browser does. A picture is worth a thousand words, so here's what HtmlTextBlock looks like in action:

    HtmlTextBlock Demonstration Page

    You can click here (or on the image above) to experiment with HtmlTextBlock in your own browser in an interactive demo page. As usual, I've made the complete source code available, so click here to download the source code and play around with it yourself! (To build the project, you'll want to use Visual Studio 2008 Beta 2 and the latest Silverlight Tools.)

    Notes:

    • HtmlTextBlock supports the following HTML elements: <A>, <B>, <BR>, <EM>, <I>, <P>, <STRONG>, and <U>. Attributes are not supported with the exception of the <A> element's HREF attribute (see below).
    • I initially planned to have HtmlTextBlock derive from TextBlock and simply override its Text property. However, TextBlock is sealed, so that wasn't going to work. The next obvious approach was to have HtmlTextBlock be a Control with a TextBlock inside it, so that's what I've done here. My goal of "plug-compatibility" (i.e., the ability to easily replace TextBlock with HtmlTextBlock) meant that I needed to manually implement the various TextBlock properties on HtmlTextBlock and pass them through to the underlying TextBlock. So HtmlTextBlock doesn't actually derive from TextBlock, but it behaves as though it did.
    • Having HtmlTextBlock use TextBlock internally and represent the HTML markup with Run and LineBreak elements is nice because it means that just about everything people already know about TextBlock automatically applies to HtmlTextBlock And I get correct word wrapping behavior for free. :) However, the glitch is that the Run element does not support the MouseLeftButton* family of events, making the handling of <A> link elements difficult. Because HtmlTextBlock can't really tell when the user clicks on a link, it displays the URL of each link so the user can type it in themselves (and typing is necessary because TextBlock doesn't support select+copy either). I can think of a few reasons why Run might not support mouse events, but in this case it would be convenient if it did. :)
    • HtmlTextBlock uses an XmlReader to parse its input when creating the Run and LineBreak elements for display. This makes the parsing implementation simple and robust, but breaks down when the input is not XHTML (such as invalid HTML or valid HTML where empty elements don't have a trailing '/' (ex: "<br>" instead of "<br />")). In the event of a parsing error, HtmlTextBlock's default behavior is to display the input text as-is, just like TextBlock always does. Unfortunately, non-XHTML is pretty common, so certain scenarios may benefit from a custom HTML parser implementation that is more flexible in this regard.
    • HTML rendering is covered by a detailed specification, but some of the rules are not what one might expect. Specifically, the rules for handling spacing and whitespace (' ', '\n', '\t', etc.) can be tricky to get right. I've made some attempt to ensure that HtmlTextBlock follows the rules where possible and convenient, but it was not my goal to achieve 100% compliance in this area. Playing around in the demo application, HtmlTextBlock should pretty closely match the browser's HTML rendering of valid input, but if you know the rules, it's not too difficult to trigger whitespace rendering differences.
    • Silverlight's default font, "Portable User Interface"/"Lucida Sans Unicode"/"Lucida Grande" doesn't seem to render bold or italic text any differently than normal text. This always surprises people the first time they try to figure out why their text isn't bold/italic. I don't know why this is myself, but I'd guess it relates to keeping the download size of Silverlight to a minimum.
    • The demo page makes use of JavaScript -> C# function calls. Hooking this up in code is surprisingly easy, though I did run into a small glitch. If the sample text is deleted entirely (leaving a blank text box) in IE, the JS -> C# call originates from the JS side as it should, but never makes it through to the C# side. Looking at the call to SetText in the debugger, textAreaSampleText.value is "", so this should work in IE just like it does in Firefox - but it didn't for me. The simple workaround was to pass textAreaSampleText.value + "" instead and then things worked in IE, too.

    HtmlTextBlock is obviously nothing like a complete HTML rendering engine [that's what web browsers are for! :) ]. However, if you want to add simple support for rich text to your Silverlight application without a lot of work, HtmlTextBlock may be just the thing for you!

  • Delay's Blog

    Alive and kickin' [New Silverlight 4 Toolkit released with today's Silverlight 4 RTW!]

    • 42 Comments

    The Silverlight team released Silverlight 4 today and it includes a variety of compelling new features and great improvements for all kinds of scenarios. You can learn more about the new platform hotness in the Silverlight 4 Technical Feature Overview.

    Congratulations to the team on another great release!

    To help celebrate the event, we have just published the April '10 release of the Silverlight Toolkit! If you're new to Silverlight 4 or the Silverlight Toolkit, you might start by reviewing my November 09 Toolkit announcement to see what was in the Silverlight 4 Beta Toolkit. Once you've done that, you're ready for my take on the...

     

    Silverlight 4 Toolkit April '10 Release Notes

     

    ContextMenu (New!)

    ContextMenu

    Silverlight 4 allows applications to handle the right mouse button (via the UIElement.MouseRightButtonDown event), and the showcase control for that is ContextMenu! (If you're not familiar with ContextMenu, here's an overview of the WPF version.). While I considered starting from an existing implementation, the handful I saw didn't match the WPF class hierarchy. Because compatibility is a big priority for us, I wrote the Toolkit's ContextMenu from scratch - and this is a 100% WPF-compatible subset implementation. Which means that existing properties/methods/events should behave the same on Silverlight as they do on WPF - something that makes porting existing code from WPF to Silverlight very straightforward.

    That said, please pay attention to the word "subset"; the Toolkit's ContextMenu doesn't have all the features of WPF's quite yet. Notably, it doesn't support multi-level menu item nesting. However, none of the customers I asked felt that nesting was necessary right now, so I don't expect its absence to be a big limitation.

    Okay, enough about what's not there; here's what is there:

    XAML for the example above:

    <Button
        x:Name="MyButton"
        Content="Button with simple ContextMenu">
        <toolkit:ContextMenuService.ContextMenu>
            <toolkit:ContextMenu>
                <toolkit:MenuItem
                    Header="Simple menu item"
                    Click="MenuItem_Click"/>
                <toolkit:MenuItem
                    Header="Another menu item"
                    Click="MenuItem_Click"/>
                <toolkit:Separator/>
                <toolkit:MenuItem
                    Header="Disabled menu item"
                    IsEnabled="False"/>
                <toolkit:Separator/>
                <toolkit:MenuItem
                    Header="With a pretty icon"
                    Click="MenuItem_Click">
                    <toolkit:MenuItem.Icon>
                        <Image Source="Paste.png"/>
                    </toolkit:MenuItem.Icon>
                </toolkit:MenuItem>
            </toolkit:ContextMenu>
        </toolkit:ContextMenuService.ContextMenu>
    </Button>

    If you're looking for more examples of how to use ContextMenu, just about any WPF sample you find should be useful. The one thing to keep in mind is that Silverlight 4 doesn't have the FrameworkElement.ContextMenu property that's often used to attach a ContextMenu to an element. Instead, it is always necessary to use the (functionally equivalent and more flexible) ContextMenuService.ContextMenu attached property like I show above.

    ContextMenu is useful in a variety of scenarios - some of them are obvious (like the example above) and some a little less so. For an example, please...

    [Click here for my follow-up post about ContextMenu.]

    [Or click here to download the sample application for this post containing all the examples.]

     

    Stacked series support (New!)

    StackedSeries

    One common feature request for the Data Visualization assembly in the Silverlight Toolkit (and WPF Toolkit) has been support for stacked series (like you see in the example above). This release of the Silverlight Toolkit includes full stacking support, which means all of Excel's top-level series types are supported in both normal and stacked modes! The following eight series classes are now available in the Data Visualization assembly:

    • StackedBarSeries
    • StackedColumnSeries
    • StackedLineSeries
    • StackedAreaSeries
    • Stacked100BarSeries
    • Stacked100ColumnSeries
    • Stacked100LineSeries
    • Stacked100AreaSeries

    Another common request has been better performance - and that's something else this release includes. While existing applications aren't likely to see a big difference by default, there's a trivial change that can be made to many scenarios to enable significant performance gains for BarSeries, ColumnSeries, LineSeries, AreaSeries, and ScatterSeries.

    And because customer scenarios involving selection (including "drill down") come up a lot more often than you'd think, the new stacked series types also offer full ListBox-style selection support, including multi-select and all the associated events/properties.

    Based on the fact that a stacked series showing a single set of data is morally equivalent to the corresponding non-stacked series, it's clear that you can get these benefits for existing scenarios, too. In fact, I've added five additional wrapper classes to make it even easier to do so!

    XAML for the example above:

    <toolkit:Chart
        Title="Student Test Scores"
        LegendTitle="Subject">
        <toolkit:Chart.Resources>
            <Style TargetType="toolkit:LinearAxis">
                <Setter Property="Minimum" Value="0"/>
            </Style>
        </toolkit:Chart.Resources>
        <toolkit:StackedColumnSeries>
            <toolkit:SeriesDefinition
                Title="Math"
                ItemsSource="{Binding}"
                DependentValuePath="MathScore"
                IndependentValuePath="StudentName"/>
            <toolkit:SeriesDefinition
                Title="Language"
                ItemsSource="{Binding}"
                DependentValuePath="LanguageScore"
                IndependentValuePath="StudentName"/>
            <toolkit:SeriesDefinition
                Title="Science"
                ItemsSource="{Binding}"
                DependentValuePath="ScienceScore"
                IndependentValuePath="StudentName"/>
        </toolkit:StackedColumnSeries>
    </toolkit:Chart>

    There's a lot that went on under the hood to add this new support, and some of the details are pretty interesting - but it's necessary to understand a bit about how stacked series came about in the first place. Because I want to keep these release notes brief, please...

    [Click here for my follow-up post about stacked series support.]

    [Or click here to download the sample application for this post containing all the examples.]

     

    SystemColors theme (New!)

    SystemColors theme SystemColors theme (high contrast)

    Some customers have asked for an easy way to get a Silverlight application to honor the color settings of the host operating system. This typically comes up in the context of high contrast mode (shown above on the right) and improved accessibility support, but it's also generally applicable. So the UX team created a custom theme for this, and I've incorporated their work into this release of the Silverlight Toolkit as the new SystemColors theme. Just like the 11 themes already in the Toolkit, using it is as easy as wrapping some content in an instance of the SystemColors theme container - everything inside automatically gets the new styles!

    XAML for the example above:

    <toolkit:SystemColorsTheme>
        <StackPanel>
            <Button Content="Button"/>
            <CheckBox Content="CheckBox"/>
            ...
        </StackPanel>
    </toolkit:SystemColorsTheme>

    Actually, it can be even easier than that! :) But to understand how, you need to know about the...

     

    Theme base class improvements

    Theme base class

    Because Silverlight 4's support for implicit styling makes theming so much easier, the Theme base class has been enhanced and is now more useful than ever. For starters, there's a ThemeUri property which can point to an external file to use as the theme. This reference can be a URL for a web resource like /MySiteWideTheme.xaml which gives sites the freedom to change a theme after an application has been deployed. Or it can point to an assembly resource via the /AssemblyShortName;component/ResourceLocation syntax discussed here. In this second case, the theme is loaded immediately; otherwise it is downloaded asynchronously and applied as soon as the download completes. Even better, ThemeUri can be changed dynamically by applications that allow users to change their themes "on the fly".

    The format of a theme file is exactly what you'd expect: a generic.xaml-like ResourceDictionary containing one or more implicit styles. This is how the Toolkit classes store their theme resources, so those themes are accessible in the same manner. This makes it easy to dynamically switch among any of the Toolkit themes, too!

    Okay, that's pretty cool and all, but sometimes you want to theme an entire application and you don't want to have to wrap every page in its own theme container. No sweat, that's what the new ApplicationThemeUri attached property is all about! Just open your project's App.xaml and set the ApplicationThemeUri property on the Application object (just as you would for ThemeUri on the Theme class). When ApplicationThemeUri is used, the corresponding implicit styles are added at the application level and automatically apply to every page.

    Can it get any easier? Yes! :) Every theme that comes with the Toolkit also exposes an IsApplicationTheme attached property. Much like ApplicationThemeUri, you set this property in App.xaml on the Application object. But because IsApplicationTheme is a simple bool value, you don't have to mess with ugly, confusing URI formats or even know what a URI is! Just set it (to True), and forget it!

    XAML for the example above:

    <toolkit:Theme ThemeUri="/ToolkitSamplesApril10;component/CustomTheme.xaml">
        <StackPanel>
            <Button Content="Button"/>
            <CheckBox Content="CheckBox"/>
            ...
        </StackPanel>
    </toolkit:Theme>
    <ResourceDictionary
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
    
        <Style TargetType="Button">
            <Setter Property="FontFamily" Value="Comic Sans MS"/>
            <Setter Property="Foreground" Value="Red"/>
        </Style>
    
        <Style TargetType="CheckBox">
            <Setter Property="FontFamily" Value="Comic Sans MS"/>
            <Setter Property="Foreground" Value="Orange"/>
        </Style>
    
        ...
        
    </ResourceDictionary>

    Alternatively:

    <Application xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
                 x:Class="ToolkitSamplesApril10.App"
                 xmlns:toolkit="http://schemas.microsoft.com/winfx/2006/xaml/presentation/toolkit"
                 toolkit:Theme.ApplicationThemeUri="/ToolkitSamplesApril10;component/CustomTheme.xaml">
        <Application.Resources>
        </Application.Resources>
    </Application>

    Or:

    <Application xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
                 x:Class="ToolkitSamplesApril10.App"
                 xmlns:toolkit="http://schemas.microsoft.com/winfx/2006/xaml/presentation/toolkit"
                 toolkit:SystemColorsTheme.IsApplicationTheme="True">
        <Application.Resources>
        </Application.Resources>
    </Application>

    [Click here to download the sample application for this post containing all the examples.]

     

    Test Framework improvements and new UI

    Unit Test Framework

    The Silverlight Unit Test Framework that comes with the Toolkit has been updated to include:

    • Out-of-browser support for Silverlight applications
    • Support for testing Windows Phone 7 applications
    • A new, more functional user interface
    Note: The two Unit Test Framework assemblies (Microsoft.Silverlight.Testing and Microsoft.VisualStudio.QualityTools.UnitTesting.Silverlight) are no longer located in the Bin directory of the Toolkit install - they have been moved to a new Testing directory instead. This was done to prevent many of the classes used by the new UI from showing up in the Visual Studio and Blend designers and confusing people. However, this location change could cause existing applications that reference the test assemblies to fail to compile due to the "missing" references. If that happens, please delete the two broken references and create references to these assemblies in their new location. Everything else should continue to work as expected.

    For more information about changes to the Silverlight Unit Test Framework, please have a look at Jeff Wilcox's blog.

     

    PanelDragDropTarget (New!)

    PanelDragDropTarget

    A new DragDropTarget has been added to make it easy to support drag/drop scenarios with Panel subclasses like Grid and WrapPanel. Simply wrap the relevant Panel in an instance of PanelDragDropTarget (like you see below), and the Toolkit's drag/drop framework automatically allows users to drag items out of that container with full visual feedback about what's being dragged and what the drop action will be. To allow dropping items into the container, just set its AllowDrop property to True and the details are handled seamlessly.

    XAML for the example above:

    <Border Grid.Column="0">
        <Grid>
            <TextBlock Text="Drag colored squares from here"/>
            <toolkit:PanelDragDropTarget>
                <toolkit:WrapPanel>
                    <Rectangle Fill="Red"/>
                    <Rectangle Fill="Orange"/>
                    <Rectangle Fill="Green"/>
                    <Rectangle Fill="Blue"/>
                    <Rectangle Fill="Purple"/>
                </toolkit:WrapPanel>
            </toolkit:PanelDragDropTarget>
        </Grid>
    </Border>
    
    <Border Grid.Column="1">
        <Grid>
            <TextBlock Text="Drop colored squares on here"/>
            <toolkit:PanelDragDropTarget>
                <toolkit:WrapPanel
                    AllowDrop="True"
                    Background="Transparent"/>
            </toolkit:PanelDragDropTarget>
        </Grid>
    </Border>

    For more about other DragDropTargets and how they work, please have a look at Jafar Husain's series of posts on the topic.

    [Click here to download the sample application for this post containing all the examples.]

     

    TreeView Scenario Examples (New!)

    TreeView connecting lines

    There are some fairly common TreeView scenarios that tend to stymie people who try to implement them in WPF or Silverlight. So this release of the Toolkit includes complete, run-able examples of some of these scenarios in the Toolkit sample application. None of the scenarios requires changing TreeView code - but there are a couple of fancy XAML tricks in there. :) The complete source code for the sample application is installed with the Toolkit and also available within the sample application (just expand the tabs at the bottom of each sample page for the syntax-highlighted code). Please feel free to make use of the same techniques in your own applications.

    For a look at the new samples, please visit the live Toolkit samples application, navigate to the TreeView node, and switch to the "Templating" tab.

     

    Improved XmlnsDefinition support

    Now that Silverlight 4 honors the URI form of XmlnsDefinitionAttribute, we use that to simplify the list of namespace mappings needed by typical applications. Specifically, the primary namespaces of all Toolkit assemblies share the URI http://schemas.microsoft.com/winfx/2006/xaml/presentation/toolkit which maps to the prefix "toolkit" by default.

    So instead of wrestling with this big glob of XAML goo:

    xmlns:controls="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Toolkit"
    xmlns:layout="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Layout.Toolkit"
    xmlns:input="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Input.Toolkit" 
    xmlns:datavis="clr-namespace:System.Windows.Controls.DataVisualization;assembly=System.Windows.Controls.DataVisualization.Toolkit" 
    xmlns:charting="clr-namespace:System.Windows.Controls.DataVisualization.Charting;assembly=System.Windows.Controls.DataVisualization.Toolkit"
    xmlns:theming="clr-namespace:System.Windows.Controls.Theming;assembly=System.Windows.Controls.Theming.Toolkit"

    The following is now all you need (and will be automatically used by both Visual Studio and Blend):

    xmlns:toolkit="http://schemas.microsoft.com/winfx/2006/xaml/presentation/toolkit"
    Aside: If you're someone like me who writes your XAML by hand, there's no need to memorize the URI above - just copy the default xmlns from the top of any XAML file and add "/toolkit" to the end!

    You can read more about this feature in the Silverlight SDK article about a similar change for the SDK assemblies.

     

    Changed AssemblyFileVersion for all assemblies

    AssemblyFileVersion

    Based on feedback from the Visual Studio and Blend teams regarding design-time issues when the Silverlight 3 and Silverlight 4 Toolkits were both installed, we've changed the AssemblyFileVersion of all Silverlight 4 Toolkit assemblies. Where it used to be 2.0.5.0 (which is what's used by versions of Silverlight so far), Toolkit assemblies now specify 4.0.5.0. This change resolves the confusion for both design tools by uniquely distinguishing the Silverlight 3 and Silverlight 4 Toolkit assemblies.

     

    Removed the Reactive Extensions dependency

    In previous releases, limited parts of the Silverlight Toolkit made use of the Reactive Extensions for .NET (Rx) library internally. Those Toolkit assemblies therefore had a direct dependency on the version of the Rx assemblies we referenced when we compiled the Toolkit. However, Rx binaries released by the Rx team in the time since we published the last Toolkit have included some breaking changes. This has caused frustration for customers wanting to use more recent Rx bits in their own applications because of the fact that a XAP can contain only one instance of a particular assembly. Those customers were often unable to upgrade successfully as a result of mismatched Rx assembly versions. Because of the feedback we've received here, Toolkit assemblies no longer have a dependency on the Rx assemblies and applications are again free to reference whatever version of Rx they wish!

    Note: Most applications should be completely unaffected by this change. However, if your project has specific references to any of the Rx assemblies that were previously located in the Bin directory of the Toolkit install (System.CoreEx.dll, System.Interactive.dll, or System.Reactive.dll), those references are no longer valid and should be removed. In rare cases, it may be necessary to add an explicit reference to their replacement assembly, System.Windows.Controls.Toolkit.Internals.dll, which is now present in the Bin directory instead.

     

    Removed System.ComponentModel.Composition.Packaging.Toolkit

    MEF's PackageCatalog class was renamed to DeploymentCatalog and moved to the System.ComponentModel.Composition.Initialization assembly that is part of the Silverlight 4 SDK. The System.ComponentModel.Composition.Packaging.Toolkit assembly was left with nothing in it and therefore removed from the Silverlight Toolkit.

     

    Various bug fixes

    This release also includes bug fixes for a variety of problems customers have reported or that were identified internally.

     

    As you can see, the Toolkit has a lot of new stuff for Silverlight 4! Of course, the first thing to do is install Silverlight 4! Once you've done that, please check out the live Toolkit samples application, download the Toolkit installer, and start writing great applications!

     

    We hope you enjoy the new release!! :)

  • Delay's Blog

    No rest for the weary [Free tool and source code to temporarily prevent a computer from entering sleep mode - now available for .NET, 32-bit, and 64-bit!]

    • 41 Comments

    It was over a year ago that I wrote and shared the first version of my Insomnia utility. A few months later, I published a new version of Insomnia to satisfy the two most popular feature requests. Here's how I explained Insomnia's motivation at the time:

    The default power settings for Windows are set up so a computer will go to sleep after 15 to 30 minutes of inactivity (i.e., no mouse or keyboard input). This is great because a computer that's not being used doesn't need to be running at full power. By letting an idle machine enter sleep mode, the user benefits from a significant reduction in electricity use, heat generation, component wear, etc.. And because sleep mode preserves the state of everything in memory, it's quick to enter, quick to exit, and doesn't affect the user's work-flow. All the same applications continue running, windows stay open and where they were, etc.. So sleep mode is a Good Thing and I'm a fan.

    However, sometimes a computer is busy even though someone isn't actively using the mouse and keyboard; common examples include playing a movie, burning a DVD, streaming music, etc.. In these cases, you don't want the machine to go to sleep because you're using it - even though you're not actually using it! So most media players and disc burners tell Windows not to go to sleep while they're running. In fact, there's a dedicated API for exactly this purpose: the SetThreadExecutionState Win32 Function.

    But what about those times when the computer is busy doing something and the relevant program doesn't suppress the default sleep behavior? For example, it might be downloading a large file, re-encoding a music collection, backing up the hard drive, or hashing the entire contents of the disk. You don't want the machine to go to sleep for now, but are otherwise happy with the default sleep behavior. Unfortunately, the easiest way I know of to temporarily suppress sleeping is to go to Control Panel, open the Power Options page, change the power plan settings, commit them - and then remember to undo everything once the task is finished. It's not hard; but it's kind of annoying...

     

    I've gotten a bunch of positive feedback on Insomnia and heard from a lot of people who use it for exactly the kinds of things I expected. (Thanks, everyone!) But I've also heard from a number of people who use Insomnia for a slightly different purpose: as an override of their machine's current power settings. For one reason or another, these people don't have control over their power configuration (perhaps because their domain enforces the relevant group policy), but still want to prevent their computer from going to sleep. This is usually because they want to connect to their machine remotely (ex: via file sharing or Remote Desktop) and can't do that if the machine is forced to sleep...

    Insomnia application

    These people tend to put a shortcut to Insomnia in their Startup group (click Start Menu, All Programs, Startup) and set the "Minimize" flag to automatically run Insomnia as an icon in the notification area whenever they log in. Because they leave Insomnia running all the time, their computer stays awake and they're able to use it whenever they need to. I've always thought this is a cool scenario - and felt that maybe a lower-overhead version of Insomnia would be particularly compelling here.

     

    I wrote the original Insomnia using the .NET Framework - which meant it was super-easy to write and took practically no time or effort on my part. That's the way (uh-huh, uh-huh!) I like it, because the thing I have the least of is spare time and so anything that makes me more productive is full of win. And in my experience, .NET isn't just more productive, it's dramatically more productive.

    That said, everything has a cost, and one of the common downsides of .NET is longer startup time and higher memory use. All the stuff .NET does for you (comprehensive APIs, high-level abstractions, garbage collection, etc.) takes extra time to load and extra memory to keep around. That said, I'll confidently suggest these costs are negligible in the majority of cases and the benefits of .NET (developer productivity, rich feature set, security, etc.) are overwhelmingly in its favor. But Insomnia is a little different than most programs: the application exists only as a wrapper for a single API (SetThreadExecutionState), so the people who run it all the time aren't really benefitting from the power of .NET...

    Insomnia minimized to the notification area

    I thought it might be fun to re-implement Insomnia in native code (vs. managed code) since it was such a simple program. I've done exactly that - and the result is that Insomnia is now available in three versions: .NET, 32-bit native, and 64-bit native!

     

    [Click here to download all three flavors of Insomnia along with the complete source code for managed and native.]

     

    Of course, while it was neat to do some Windows API coding for a change, the experience served to reinforce my belief in the fundamental productivity benefits of .NET and the power of the WPF/Silverlight layout system. About half the code in native Insomnia exists for the purpose of layout - which the .NET version accomplishes much more succinctly with XAML. The other half of the code handles basic framework-y stuff like creating a window, configuring it, etc. - more things WPF handles for you or makes quite convenient. And the last half of the code [ ;) ] deals with a variety of little things that are quite simple in the managed world, but require non-trivial effort in native code (ex: copying strings, setting up a complicated function call, etc.).

    Please don't get me wrong; I completely agree that native code has its place in life (and there's a certain feeling of power one gets from using it). But I've had enough of that for now and will be happy to return to .NET for my next project. :)

     

    Notes:

    • The most common question I get about Insomnia is whether it also disables the screen saver - it does not. When Insomnia is running, the screen saver will continue to kick in and turn off the monitor on its usual schedule. The difference is that the computer itself will not be allowed to enter sleep mode. Once the Insomnia window is closed, the computer can sleep again and will do so on its usual schedule.

    • The second most common question I get is why the Insomnia window stays above other windows. As I explained in the introductory post, this is so Insomnia is always visible when running and people will be less likely to accidentally leave their computers sleep-less by forgetting they've started it. This seems like the right behavior for the original "temporarily prevent sleep mode" scenario; for the "always on" scenario, minimizing Insomnia to the notification area seems to work well for everyone I've talked to.

    • My goal for the native version of Insomnia was to duplicate the functionality of the .NET version as closely as possible - not because I think the .NET version is perfect, but because it works well and it's what people are already familiar with. So while I wasn't obsessive about matching the font face and size exactly, a side-by-side comparison of the two programs shows a very strong correlation. :)

    • The one thing I didn't port from the .NET version was the "/minimize" command-line switch. Originally intended to make it easy for users to start Insomnia minimized, commenter rbirkby reminded me that Windows shortcuts already made that easy enough. It didn't seem necessary to duplicate this somewhat unnecessary feature, so I've omitted it to keep the native version just a bit simpler.

    • To start Insomnia minimized, just create a shortcut to it (right-click+drag+drop its icon somewhere (like the Start Menu / All Programs / Startup folder) then edit the shortcut's settings (right-click it and choose Properties) and choose "Minimized":

      Shortcut to start Insomnia minimized

      Alternatively, the following syntax will do the same thing from a batch file:

      start /MIN Insomnia.exe
    • Because the whole point of this exercise was to reduce Insomnia's run-time footprint, it's interesting to see how things worked out. Here's a table of the Resource Monitor statistics for each flavor as measured by opening and minimizing them all on my 64-bit Windows 7 machine:

      Flavor Commit (KB) Private (KB)
      .NET 66,972 17,940
      64-bit 2,144 1,876
      32-bit 1,652 1,332

      (Note that the .NET version uses somewhat less memory on a 32-bit version of Windows 7.)

    • Not only do the native versions of Insomnia use less memory - they start faster, too! Because they have fewer dependencies, there's simply less stuff to load from disk - and because disk access is (relatively) slow, minimizing it can do a lot to improve startup speeds.

    • Something that made life a little more pleasant for the native version was the SysLink common control - specifically its LM_GETIDEALSIZE message. This message is used to "Retrieve[s] the preferred height of a link for the control's current width." and it enabled me to approximate something kind of like WPF's measure/arrange-based layout system without having to write a bunch of code myself. So while I'd originally thought to use SysLink only for the hyperlink (duh!), I ended up using it for the version text and message as well!

    • As you'd expect, the Visual Studio solution/project for the new, native version of Insomnia uses the Visual Studio 2010 format. However, the solution/project for the original, managed version is still in the 2008 format I originally released it in (of course, opening it in VS 2010 automatically "upgrades" it).

  • Delay's Blog

    There are lots of ways to ask for a date [Creating custom DatePicker/TimePicker experiences is easy with the Windows Phone Toolkit]

    • 40 Comments
    DatePicker and TimePicker sample picker

    In my previous post announcing the release of the Windows Phone Toolkit, I explained the motivation for the PhoneApplicationPage-based design of the DatePicker and TimePicker controls. Though justified, the current implementation is fairly unusual and has the potential to trip people up if they're not ready for it. So I highly recommend reading the details at the end of that post if you're using DatePicker or TimePicker.

    [Waits patiently...]

    Okay, if you read the notes, then you understand what we've done and why. And while I'm happy to discuss the pros and cons of the current implementation, that's what we've got for now and I'm going to spend a little time highlighting its extensibility!

     

    As with the rest of the Phone Toolkit, our primary goal was to make it easy for developers to match the core Windows Phone 7 experience in their own applications. Toward that end, adding an instance of DatePicker or TimePicker to a page gives the same "small button expands to full-page date/time picker with infinite scrolly thingies" experience that's found in the core Settings, Calendar, and Alarms applications. But sometimes you want to do things a little differently, and maybe you've got a date/time picking interface in mind that's more relevant to your scenario...

     

    Aside: If what you want to do is change the format used to display the date/time on the face of the initial "button", then all you need to do is set the ValueStringFormat property to the standard date and time format string or custom date and time format string of your choice!

    But because format strings include "{" and "}", they're a little tricky to code in XAML; you need to escape the special characters. here's an example of what this looks like in its simplest form: ValueStringFormat="{}{0:d}"

    And here are a few practical examples (using the culture settings for USA):

    Description XAML Sample
    Short date format (DatePicker default) "{}{0:d}" 9/20/2010
    Short time format (TimePicker default) "{}{0:t}" 4:00 PM
    Long date format "{}{0:D}" Monday, September 20, 2010
    Long time format "{}{0:T}" 4:00:00 PM
    Explicit month/day/year format "{}{0:MM-dd-yyyy}" 09-20-2010
    Explicit 24-hour time format with text "{}The 24-hour time is {0:HH:mm}." The 24-hour time is 16:00.

     

    Customizing the full-screen picker page is as simple as setting the DatePicker.PickerPageUri or TimePicker.PickerPageUri property to point to a PhoneApplicationPage that implements the IDateTimePickerPage interface. The PickerPageUri property uses the standard assembly-relative syntax ("/assemblyShortName;component/resourceLocation"), so it should be familiar to most folks. The IDateTimePickerPage interface is a new interface, but I think you'll agree it's pretty basic:

    namespace Microsoft.Phone.Controls.Primitives
    {
        /// <summary>
        /// Represents an interface for DatePicker/TimePicker to use for communicating with a picker page.
        /// </summary>
        public interface IDateTimePickerPage
        {
            /// <summary>
            /// Gets or sets the DateTime to show in the picker page and to set when the user makes a selection.
            /// </summary>
            DateTime? Value { get; set; }
        }
    }

     

    When it's time to display the picker page, DatePicker and TimePicker navigate to the page specified by PickerPageUri via the NavigationService.Navigate("...") method, wait for the page to load, get a reference to its IDateTimePickerPage interface, and set its Value property to the starting DateTime. After that, the custom picker page is in complete control and can display any UI it wants. When the custom picker is done (either because the user picked a date/time or because the user cancelled the operation), the picker should set its Value property to the selected date/time (or null if cancelled) and then call NavigationService.GoBack to return to the previous page. DatePicker and TimePicker automatically detect this, update their own Value property (which automatically updates their UI), and fire their ValueChanged event.

    In some ways, it's actually kind of elegant! :) And the fact that the picker is entirely implemented by its own PhoneApplicationPage makes it really easy to work with at design-time in Visual Studio and Blend.

     

    To help illustrate the process, I created a sample application that uses custom pickers for DatePicker and TimePicker which let the user pick times relative to the current time:

    CustomDateTimePickerPage sample application Custom DatePickerPage example Custom TimePickerPage example

    The sample application shows the current date and time because what the emulator reports isn't always the same as what the host operating system reports! In my experience, it's often an hour or more off - so please keep in mind that the times used by the sample are relative to emulator time!

     

    Here's the XAML for the main page that hosts the DatePicker and TimePicker controls:

    <toolkit:DatePicker
        Value="10/10/2010"
        Header="Custom Date Picker Page"
        PickerPageUri="/CustomDateTimePickerPage;component/CustomDatePickerPage.xaml"/>
    
    <toolkit:TimePicker
        Value="{x:Null}"
        Header="Custom Time Picker Page"
        PickerPageUri="/CustomDateTimePickerPage;component/CustomTimePickerPage.xaml"/>
    Aside: Just for fun, I've explicitly set the Value properties here. Although the controls' default behavior of using the current date/time is likely to be what you want most of the time, applications are free to override the initial date/time (as DatePicker does above) or to provide a blank starting value (as TimePicker does with the x:Null markup extension).

     

    The implementation of the custom PhoneApplicationPage implementation is so basic, I'm going to show the complete date picker here! (The custom time picker implementation is nearly identical.) I've highlighted the particularly interesting bits, but by now they should be familiar.

    First the XAML, then the code:

    <phone:PhoneApplicationPage
        x:Class="CustomDateTimePickerPage.CustomDatePickerPage"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        FontFamily="{StaticResource PhoneFontFamilyNormal}"
        FontSize="{StaticResource PhoneFontSizeNormal}"
        Foreground="{StaticResource PhoneForegroundBrush}"
        SupportedOrientations="PortraitOrLandscape" Orientation="Portrait"
        mc:Ignorable="d" d:DesignHeight="800" d:DesignWidth="480">
    
        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="*"/>
            </Grid.RowDefinitions>
    
            <TextBlock
                Text="CHOOSE CUSTOM DATE"
                Margin="12"
                FontWeight="Bold"/>
    
            <StackPanel
                Grid.Row="1"
                VerticalAlignment="Center">
                <Button Content="Tomorrow" Click="TomorrowButtonClick"/>
                <Button Content="Next Week" Click="NextWeekButtonClick"/>
                <Button Content="Next Month" Click="NextMonthButtonClick"/>
                <Button Content="[Cancel]" Click="CancelButtonClick"/>
            </StackPanel>
        </Grid>
    
    </phone:PhoneApplicationPage>
    public partial class CustomDatePickerPage : PhoneApplicationPage, IDateTimePickerPage
    {
        /// <summary>
        /// Gets or sets the DateTime value for the IDateTimePickerPage interface.
        /// </summary>
        public DateTime? Value { get; set; }
    
        public CustomDatePickerPage()
        {
            InitializeComponent();
        }
    
        private void TomorrowButtonClick(object sender, RoutedEventArgs e)
        {
            ChooseDate(DateTime.Now.AddDays(1));
        }
    
        private void NextWeekButtonClick(object sender, RoutedEventArgs e)
        {
            ChooseDate(DateTime.Now.AddDays(7));
        }
    
        private void NextMonthButtonClick(object sender, RoutedEventArgs e)
        {
            ChooseDate(DateTime.Now.AddMonths(1));
        }
    
        private void ChooseDate(DateTime date)
        {
            // Commit date and close picker
            Value = date;
            NavigationService.GoBack();
        }
    
        private void CancelButtonClick(object sender, RoutedEventArgs e)
        {
            // Cancel selection and close picker
            Value = null;
            NavigationService.GoBack();
        }
    }

     

    As you can see, creating a custom picker page for DatePicker and TimePicker is really easy! As long as you play by a few basic rules, you have complete freedom to style the entire picker experience in a self-contained, interactive, designer-friendly way. It's kind of like re-Templating - but easier and more flexible - in a weird kind of way!

     

    [Click here to download the complete source code for the CustomDateTimePickerPage sample application.]

     

    Now get to it - I'm looking forward to seeing the kinds of custom picker experiences you all come up with! :)

  • Delay's Blog

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

    • 38 Comments

    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...

  • Delay's Blog

    Chart tweaking made easy [How to: Make four simple color/ToolTip changes with Silverlight/WPF Charting]

    • 37 Comments

    While answering a support forum question I'd seen a couple of times before, I figured it would be helpful to write a post showing how to make simple styling changes to charts created by the Charting controls that come with the Silverlight Toolkit. Note that I said simple changes - if you want to make more dramatic changes, you should go read some of the excellent tutorials Pete Brown has written on the topic. Links to Pete's posts (and other interesting posts) can be found on my latest Charting links post.

     

    The sample application we'll be working with here shows off four scenarios and looks like this:

    Styling Sample

     

    Simple Color Change

    In this scenario we have a basic Chart with a ColumnSeries and want to change the color to purple. As you'd expect, this is quite easy to do: we provide a custom Style that sets the Background color to purple and we're done!

    It's worth pointing out that we could add more Setters to this Style to customize things further - but I promised I'd keep this simple, so we won't do that right now. :)

    <charting:Chart
        Title="Simple Color Change">
        <charting:ColumnSeries
            ItemsSource="{Binding}"
            DependentValueBinding="{Binding Length}"
            IndependentValueBinding="{Binding}">
            <charting:ColumnSeries.DataPointStyle>
                <Style TargetType="charting:ColumnDataPoint">
                    <Setter Property="Background" Value="Purple"/>
                </Style>
            </charting:ColumnSeries.DataPointStyle>
        </charting:ColumnSeries>
    </charting:Chart>
    

     

    Custom ToolTip (Column)

    For this example, the setup is the same as last time except that now we want to change the default ToolTip that appears when the user hovers over any of the columns. As with just about every other visual default, the standard ToolTip is part of the ColumnDataPoint's default Template. So in order to customize it we start with a copy of that Template and modify it to suit our needs. Blend makes this easy, but I'm most comfortable in Visual Studio, so what we'll do here is go to the source code for ColumnDataPoint.xaml and copy the Style there to the Application.Resources section of our App.xaml.

    Aside: You can also use my handy-dandy SilverlightDefaultStyleBrowser for this. What's more, SilverlightDefaultStyleBrowser works even when you don't have access to source code for the control you're styling, so it's something to keep in mind for those occasions when Blend isn't readily available. :)

    Copying done, we can tweak the ToolTip to include a custom message as follows:

    <ToolTipService.ToolTip>
        <StackPanel>
            <ContentControl
                Content="Custom ToolTip"
                FontWeight="Bold"/>
            <ContentControl
                Content="{TemplateBinding FormattedDependentValue}"/>
        </StackPanel>
    </ToolTipService.ToolTip>
    

    After that, and it's simply a matter of assigning our customized Style/Template to the DataPointStyle property of the ColumnSeries:

    <charting:Chart
        Title="Custom ToolTip">
        <charting:ColumnSeries
            ItemsSource="{Binding}"
            DependentValueBinding="{Binding Length}"
            IndependentValueBinding="{Binding}"
            DataPointStyle="{StaticResource MyColumnDataPointStyle}"/>
    </charting:Chart>
    

     

    Simple Palette Change

    What we've done so far will work for all of the current series except for PieSeries which is special because each of its PieDataPoints gets a unique Style. In other words, there's no DataPointStyle property on PieSeries because one value just isn't enough! Therefore, PieSeries exposes a StylePalette property just like Chart does and we can use that to provide a collection of Styles for the pie slices. (Note that we can provide as many or as few as we want; PieSeries will start at the beginning and automatically loop through the collection as necessary.)

    In this case, we know our data has exactly four items, so we'll provide exactly four custom Styles to set the colors we want. Other than using a list this time around, it's just like the first example we saw:

    <charting:Chart
        Title="Simple Palette Change">
        <charting:PieSeries
            ItemsSource="{Binding}"
            DependentValueBinding="{Binding Length}"
            IndependentValueBinding="{Binding}">
            <charting:PieSeries.StylePalette>
                <datavis:StylePalette>
                    <Style TargetType="charting:PieDataPoint">
                        <Setter Property="Background" Value="Red"/>
                    </Style>
                    <Style TargetType="charting:PieDataPoint">
                        <Setter Property="Background" Value="Orange"/>
                    </Style>
                    <Style TargetType="charting:PieDataPoint">
                        <Setter Property="Background" Value="Green"/>
                    </Style>
                    <Style TargetType="charting:PieDataPoint">
                        <Setter Property="Background" Value="Blue"/>
                    </Style>
                </datavis:StylePalette>
            </charting:PieSeries.StylePalette>
        </charting:PieSeries>
    </charting:Chart>
    

     

    Custom ToolTip (Pie)

    Finally, let's customize the ToolTip for the slices of a PieSeries. Like before, we'll start by copying the default Style/Template from the source code for PieDataPoint.xaml and then customize the ToolTip found within:

    <ToolTipService.ToolTip>
        <StackPanel>
            <ContentControl
                Content="Custom ToolTip"
                FontWeight="Bold"/>
            <ContentControl
                Content="{TemplateBinding FormattedDependentValue}"/>
            <ContentControl
                Content="{TemplateBinding FormattedRatio}"/>
        </StackPanel>
    </ToolTipService.ToolTip>
    

    Because we want our PieSeries to use all the same colors as the default, the next step is to copy the default StylePalette from the code for Chart.xaml and add a single Setter for the Template property of each of the Styles within. All of which point to the one Template we just customized, so if we make any changes in the future there's exactly one place we need to touch and our changes automatically shows up everywhere they should:

    <datavis:StylePalette
        x:Key="MyStylePalette">
        <!--Blue-->
        <Style TargetType="Control">
            <Setter Property="Template"
                    Value="{StaticResource MyPieDataPointTemplate}"/>
            <Setter Property="Background">
                <Setter.Value>
                    ...
                </Setter.Value>
            </Setter>
        </Style>
        <!--Red-->
        <Style TargetType="Control">
            <Setter Property="Template"
                    Value="{StaticResource MyPieDataPointTemplate}"/>
            <Setter Property="Background">
                <Setter.Value>
                    ...
                </Setter.Value>
            </Setter>
        </Style>
        ...
    

    With that out of the way, all that remains is to use our customized StylePalette by assigning it to the StylePalette property of PieSeries:

    <charting:Chart
        Title="Custom ToolTip">
        <charting:PieSeries
            ItemsSource="{Binding}"
            DependentValueBinding="{Binding Length}"
            IndependentValueBinding="{Binding}"
            StylePalette="{StaticResource MyStylePalette}">
        </charting:PieSeries>
    </charting:Chart>
    

     

    Done!

    If you've gotten this far, I hope that you've gained at least a somewhat better understanding of how to perform some basic style changes to the Toolkit's Charting controls. We've really only scratched the surface, though, so I encourage interested readers to have a look at some of the other charting links for more details, ideas, and inspiration!

     

    [Please click here to download the source code for the sample application.]

  • Delay's Blog

    Powerful log file analysis for everyone [Releasing TextAnalysisTool.NET!]

    • 36 Comments

    A number of years ago, the product team I was on spent a lot of team analyzing large log files. These log files contained thousands of lines of output tracing what the code was doing, what its current state was, and gobs of other diagnostic information. Typically, we were only interested in a handful of lines - but had no idea which ones at first. Often one would start by searching for a generic error message, get some information from that, search for some more specific information, obtain more context, and continue on in that manner until the problem was identified. It was usually the case that interesting lines were spread across the entire file and could only really be understood when viewed together - but gathering them all could be inconvenient. Different people had different tricks and tools to make different aspects of the search more efficient, but nothing really addressed the end-to-end scenario and I decided I'd try to come up with something better.

    TextAnalysisTool was first released to coworkers in July of 2000 as a native C++ application written from scratch. It went through a few revisions over the next year and a half and served myself and others well during that time. Later, as the .NET Framework became popular, I decided it would be a good learning exercise to rewrite TextAnalysisTool to run on .NET as a way to learn the Framework and make some architectural improvements to the application. TextAnalysisTool.NET was released in February of 2003 as a fully managed .NET 1.0 C# application with the same functionality of the C++ application it replaced. TextAnalysisTool.NET has gone through a few revisions since then and has slowly made its way across parts of the company. (It's always neat to get an email from someone in a group I had no idea was using TextAnalysisTool.NET!) TextAnalysisTool.NET is popular enough among its users that I started getting requests to make it available outside the company so that customers could use it to help with investigations.

    The effort of getting something posted to Microsoft.com seemed overwhelming at the time, so TextAnalysisTool.NET stayed internal until now. With the latest request, I realized my blog would be a great way to help internal groups and customers by making TextAnalysisTool.NET available to the public!

    TextAnalysisTool.NET Demonstration

    You can download the latest version of TextAnalysisTool.NET by clicking here (or on the image above).

    In the above demonstration of identifying the errors and warnings from sample build output, note how the use of regular expression text filters and selective hiding of surrounding content make it easy to zoom in on the interesting parts of the file - and then zoom out to get context.

    Additional information can be found in the TextAnalysisTool.NET.txt file that's included in the ZIP download (or from within the application via Help | Documentation). The first section of that file is a tutorial and the second section gives a more detailed overview of TextAnalysisTool.NET (excerpted below). The download also includes a ReadMe.txt with release notes and a few other things worth reading.

    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.

    Filters: Before displaying the lines of a file, TextAnalysisTool.NET passes the lines of that file through a set of user-defined filters, dimming or hiding all lines that do not satisfy any of the filters. Filters can select only the lines that contain a sub-string, those that have been marked with a particular marker type, or those that match a regular expression. A color can be associated with each filter so lines matching a particular filter stand out and so lines matching different filters can be easily distinguished. In addition to the normal "including" filters that isolate lines of text you DO want to see, there are also "excluding" filters that can be used to suppress lines you do NOT want to see. Excluding filters are configured just like including filters but are processed afterward and remove all matching lines from the set. Excluding filters allow you to easily refine your search even further.

    Markers: Markers are another way that TextAnalysisTool.NET makes it easy to navigate a file; you can mark any line with one or more of eight different marker types. Once lines have been marked, you can quickly navigate between similarly marked lines - or add a "marked by" filter to view only those lines.

    Find: TextAnalysisTool.NET also provides a flexible "find" function that allows you to search for text anywhere within a file. This text can be a literal string or a regular expression, so it's easy to find a specific line. If you decide to turn a find string into a filter, the history feature of both dialogs makes it easy.

    Summary: TextAnalysisTool.NET was written with speed and ease of use in mind throughout. It saves you time by allowing you to save and load filter sets; it lets you import text by opening a file, dragging-and-dropping a file or text from another application, or by pasting text from the clipboard; and it allows you to share the results of your filters by copying lines to the clipboard or by saving the current lines to a file. TextAnalysisTool.NET supports files encoded with ANSI, UTF-8, Unicode, and big-endian Unicode and is designed to handle large files efficiently.

    I maintain a TODO list with a variety of user requests, but I thought I'd see what kind of feedback I got from releasing TextAnalysisTool.NET to the public before I decide where to go with the next release. I welcome suggestions - and problem reports - so please share them with me if you've got any!

    I hope you find TextAnalysisTool.NET useful as I have!

  • Delay's Blog

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

    • 35 Comments

    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. :)
Page 2 of 28 (277 items) 12345»