Delay's Blog

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

Posts
  • Delay's Blog

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

    • 19 Comments

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

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

     

    Here's the relevant portion:

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

    Sweet

    Actually renders like this:

    Whoops

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

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

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

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

     

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

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

    LayoutTransformer on Windows Phone

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

     

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

     

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

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

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

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

     

    Yep, it's really that easy!

     

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

  • Delay's Blog

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

    • 19 Comments

    In the time since sharing my last collection of Silverlight/WPF Charting links, there have been some great new articles I'd like to highlight. And in case you haven't heard, we published the October 09 release of the Silverlight Toolkit last week, so please consider upgrading if you haven't already!

    Here are the latest links (FYI: previously published links are gray):

    Overviews (100 level)

    Scenarios (200 level)

    Internals (300 level)

    Team Member posts (Partner level)

    My posts (Ego level)

    Many, many thanks to everyone who's spent time helping others learn how to use Silverlight/WPF Data Visualization!

    PS - If I've missed any good resources, please leave a comment with a link - I'm always happy to find more good stuff! :)

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

  • Delay's Blog

    Creating something from nothing - and knowing it [Developer-friendly virtual file implementation for .NET refined!]

    • 19 Comments

    A couple of weeks ago I wrote about VirtualFileDataObject, my developer-friendly virtual file implementation for .NET and WPF. I followed that up by adding support for asynchronous behavior to improve the user experience during long-running operations. Last week, these posts got a shout-out from blogging legend Raymond Chen, whose work provided the inspiration for the project. [Best week ever! :) ] Now it's time for one last tweak to wrap things up!

     

    Recall that the Windows APIs for drag-and-drop and clipboard operations deal with objects that implement the IDataObject COM interface. And while the native DoDragDrop function tries to provide a mechanism for the source and target to communicate (via the pdwEffect parameter and the function's return value), there's nothing similar available for the SetClipboardData function. And once you enable asynchronous drag-and-drop, it's clear that the return value of DoDragDrop isn't going to work, either, because the call returns immediately (before the operation has completed). So it seems like there must be some other way for the source and target to share information...

    Sure enough, there are some shell clipboard formats specifically for enabling communication between the source and target! For the purposes of this sample, we're interested in using two of them:

    And here's another which is relevant enough that I wrote code to support it, though I'll only mention it once more here:

    • CFSTR_PASTESUCCEEDED - Indication that a paste succeeded and what kind of operation (copy/move/link) it did

     

    Armed with that knowledge, let's tweak the sample application so one of the scenarios does a move/cut instead of a copy:

    VirtualFileDataObjectDemo sample application

    The first tweak to the code modifies our helper function to accept a DragDropEffects parameter so the caller can indicate its copy/move preference with the new PreferredDropEffect property in the clipboard scenario:

    private static void DoDragDropOrClipboardSetDataObject(MouseButton button, DependencyObject dragSource,
        VirtualFileDataObject virtualFileDataObject, DragDropEffects allowedEffects)
    {
        try
        {
            if (button == MouseButton.Left)
            {
                // Left button is used to start a drag/drop operation
                VirtualFileDataObject.DoDragDrop(dragSource, virtualFileDataObject, allowedEffects);
            }
            else if (button == MouseButton.Right)
            {
                // Right button is used to copy to the clipboard
                // Communicate the preferred behavior to the destination
                virtualFileDataObject.PreferredDropEffect = allowedEffects;
                Clipboard.SetDataObject(virtualFileDataObject);
            }
        }
        catch (COMException)
        {
            // Failure; no way to recover
        }
    }
    

    Then we can tweak the VirtualFileDataObject constructor to pass a custom "end" action that uses the new PerformedDropEffect property to find out what action took place. If the target performed a move (or a cut with the clipboard), then we'll hide the corresponding "button" to reflect that fact:

    private void VirtualFile_MouseButtonDown(object sender, MouseButtonEventArgs e)
    {
        var virtualFileDataObject = new VirtualFileDataObject(
            null,
            (vfdo) =>
            {
                if (DragDropEffects.Move == vfdo.PerformedDropEffect)
                {
                    // Hide the element that was moved (or cut)
                    // BeginInvoke ensures UI operations happen on the right thread
                    Dispatcher.BeginInvoke((Action)(() => VirtualFile.Visibility = Visibility.Hidden));
                }
            });
    
        // Provide a virtual file (generated on demand) containing the letters 'a'-'z'
        ...
    
        DoDragDropOrClipboardSetDataObject(e.ChangedButton, TextUrl, virtualFileDataObject, DragDropEffects.Move);
    }
    

    Please note that the begin/end actions are now of type Action<VirtualFileDataObject>. The new parameter is always a reference to the active VirtualFileDataObject instance and is provided to make it easy to use anonymous methods like you see above.

    Aside: Otherwise you'd need to capture the VirtualFileDataObject instance so you could pass it to the action being provided to that same instance's constructor! (Catch-22 much?) While there may be a clever way to do this without making the begin/end actions properties of the class (which I didn't want to do because that wouldn't ensure they're invariant), passing the VirtualFileDataObject in this manner is both easy and obvious.
    Further aside: As a consequence of the changes to support these new properties, VirtualFileDataObject now implements the IDataObject::SetData method for HGLOBAL-style data. So if there's some other CFSTR_ property that's relevant to your scenario, you can query it off the VirtualFileDataObject instance in much the same manner!

     

    [Click here to download the complete code for VirtualFileDataObject and the sample application.]

     

    And that's pretty much all there is to it! With the addition of PreferredDropEffect and PerformedDropEffect (and PasteSucceeded, though it's not demonstrated above), VirtualFileDataObject makes it easy for your application to provide a seamless virtual file drag/drop experience with all the sophisticated nuances users expect from a polished Windows application.

    Enjoy!

  • Delay's Blog

    Columns of a different color [Customizing the appearance of Silverlight charts with re-templating and MVVM]

    • 19 Comments

    When we created Silverlight Charting (background reading here and here), we tried to make things as designer-friendly as possible. So friendly, in fact, that it would be possible for someone to take the default look-and-feel of what we'd released and significantly enhance it without changing the Charting framework at all. :) That said, it's worth noting that Charting controls are a little different than typical WPF/Silverlight controls: while it might make sense to completely change how a ListBox looks, there are certain aspects of a chart that can't be changed without rendering the visualization meaningless. And so there are certain assumptions behind our Charting implementation around things we didn't expect users to want to change. But that's the great thing about users: they want to change these things anyway! :)

    One of the fundamentals of column/bar charts is that the columns/bars of a single series are all drawn the same; that's what ties them together and makes it clear they represent a single series. If you create a column chart in Excel, the default color for the columns is blue. It's easy to change that color to orange or green or plaid, but by default all of the columns of the series change together because they're all part of the same series. (Incidentally, it is possible to change the colors of an individual column in Excel, but it's not entirely obvious how to do so and it's clearly not a mainline scenario.) With that in mind, it's no surprise that our charts behave similarly: you can provide whatever look you want for the columns and bars (via the ColumnSeries.DataPointStyle property, perhaps), but the columns and bars of a particular series always look the same.

    But what if your scenario is such that you want to do things a little differently and you want more control over the colors of individual columns and bars? Well, you take advantage of re-templating and Model-View-ViewModel (MVVM), that's what! :) You're reading this blog, so I'll assume you already know what re-templating is - if not, here's a good place to start. Model-View-ViewModel (MVVM) is probably less well known to date - it's an approach to application development commonly used with WPF and Silverlight where simple wrapper classes are used to expose aspects of the underlying data types in a manner that's easy for the UI layer to deal with. You can read lots more about MVVM on John Gossman's blog or this recent MSDN article by Josh Smith. But I'm not here to teach you what re-templating or MVVM are - I'm here to show you how to use them with Charting to implement the multi-colored column scenario!

     

    [Click here to download the complete Silverlight 2 source code for the sample application shown/discussed below.]

     

    Imagine that you're a teacher and you want to chart the grades of your students. You've already got a basic Student class that exposes some basic properties and you can create instances from a database or a file or something. The Student class probably looks like this:

    // Standard data object representing a Student
    public class Student : INotifyPropertyChanged
    {
        // Student's name
        public string Name { get; private set; }
    
        // Student's favorite color
        public Brush FavoriteColor { get; private set; }
    
        // Student's grade
        public double Grade
        {
            get { return _grade; }
            set
            {
                _grade = value;
                Helpers.InvokePropertyChanged(PropertyChanged, this, "Grade");
            }
        }
        private double _grade;
    
        // Student constructor
        public Student(string name, Brush favoriteColor)
        {
            Name = name;
            FavoriteColor = favoriteColor;
        }
    
        // INotifyPropertyChanged event
        public event PropertyChangedEventHandler PropertyChanged;
    }
    

    The class above exposes a name and a favorite color (which I've implemented here as a Brush for convenience). There's also a grade, but we'll come back to that shortly... The goal is for each column representing a student to be drawn using that student's favorite color. To accomplish this, all we need to do is re-template. Using a designer tool like Blend or something simple like my SilverlightDefaultStyleBrowser, we can copy the default Style for ColumnDataPoint and paste it into our project's resources. By removing stuff that's not relevant to the demonstration and making a single change (highlighted below), we arrive at something like the following:

    <Style
        x:Key="ColorByPreferenceColumn"
        TargetType="charting:ColumnDataPoint">
        <Setter Property="Background" Value="DarkGray"/>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate
                    TargetType="charting:ColumnDataPoint">
                    <Border
                        BorderBrush="{TemplateBinding BorderBrush}"
                        BorderThickness="{TemplateBinding BorderThickness}">
                        <Grid Background="{Binding FavoriteColor}">
                            <Rectangle>
                                <Rectangle.Fill>
                                    <LinearGradientBrush>
                                        <GradientStop Color="#77ffffff" Offset="0"/>
                                        <GradientStop Color="#00ffffff" Offset="1"/>
                                    </LinearGradientBrush>
                                </Rectangle.Fill>
                            </Rectangle>
                            <Border BorderBrush="#ccffffff" BorderThickness="1">
                                <Border BorderBrush="#77ffffff" BorderThickness="1"/>
                            </Border>
                        </Grid>
                    </Border>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
    

    This is just a tweak of the default template so that each column pulls its Background Brush from the FavoriteColor property of the underlying data object. Hook that up to a Chart/ColumnSeries in XAML, and that's all there is to it:

    Color from Student

    By the way, here's the XAML for that Chart:

    <charting:Chart
        x:Name="FavoriteColorColumnChart"
        Title="Grades - By Favorite Color"
        Grid.Column="0">
        <charting:ColumnSeries
            DependentValueBinding="{Binding Grade}"
            IndependentValueBinding="{Binding Name}"
            DataPointStyle="{StaticResource ColorByPreferenceColumn}">
            <charting:ColumnSeries.DependentRangeAxis>
                <charting:LinearAxis
                    Minimum="0"
                    Maximum="100"
                    Title="Grade"
                    ShowGridLines="True"/>
            </charting:ColumnSeries.DependentRangeAxis>
        </charting:ColumnSeries>
    </charting:Chart>
    
    Aside: This process is even easier on WPF! (Assuming I had access to something like daily builds of Charting for WPF, I might have even mocked this up quickly to prove it to myself...) Unfortunately, the necessary "Binding in a Setter" capability is not supported by Silverlight 2 in XAML or code:
    <Style
        x:Key="ColorByPreferenceColumn"
        TargetType="charting:ColumnDataPoint">
        <Setter Property="Background" Value="{Binding FavoriteColor}"/>
    </Style>
    

    So that's how easy it is to get custom column and bar colors if your data objects already expose the information you need!

    But what if you want to base the custom colors on something that's not directly available on the data objects and you also don't have the freedom to change the data objects themselves? In other words - continuing the example above - let's say we decided to change things so the columns are colored according to each student's current grade: great grades get green columns, satisfactory grades get yellow columns, and unsatisfactory grades get red columns.

    The first thing to consider when faced with a problem like this is whether an IValueConverter will work. I've written about the usefulness of IValueConverter before, so I won't spend more time on that here. IValueConverter is great if you want to take a single property and mutate it as part of a Binding. But what if you want to do something more complicated than that? Well, on WPF there's IMultiValueConverter which might do the trick, but that's not available on Silverlight and it's not always the answer anyway. So let's take advantage of MVVM to wrap our existing Student data objects with an object that's more view-friendly: StudentViewModel. Here's a trivial StudentViewModel class that exposes a Student and a Brush that's colored according to the Student's Grade property. Because Student implements INotifyPropertyChanged (like a well behaved class should), StudentViewModel can listen for changes to the Grade property and update its Brush automatically. StudentViewModel also implements INotifyPropertyChanged - so that anything referencing it will be notified about changes to the GradeColor property it exposes. Here's how it looks in code:

    // Custom data object to wrap a Student object for the view model
    public class StudentViewModel : INotifyPropertyChanged
    {
        // Student object
        public Student Student { get; private set; }
    
        // Color representing Student's Grade
        public Brush GradeColor { get; private set; }
    
        // StudentViewModel constructor
        public StudentViewModel(Student student)
        {
            Student = student;
            student.PropertyChanged += new PropertyChangedEventHandler(HandleStudentPropertyChanged);
        }
    
        // Detect changes to the Student's grade and update GradeColor
        void HandleStudentPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if ("Grade" == e.PropertyName)
            {
                if (Student.Grade < 50)
                {
                    GradeColor = new SolidColorBrush { Color = Colors.Red };
                }
                else if (Student.Grade < 80)
                {
                    GradeColor = new SolidColorBrush { Color = Colors.Yellow };
                }
                else
                {
                    GradeColor = new SolidColorBrush { Color = Colors.Green };
                }
                Helpers.InvokePropertyChanged(PropertyChanged, this, "GradeColor");
            }
        }
    
        // INotifyPropertyChanged event
        public event PropertyChangedEventHandler PropertyChanged;
    }
    
    Aside: I've typically seen view model classes implemented by re-exposing each of the interesting data object properties - so for each property Foo on the data object, there will be a property Foo' on the view model object (which is either identical to the original property or some derivative of it). While I can see the value of this approach in some cases, the duplication of properties always bothers me and so I've instead exposed the entire Student object from the StudentViewModel object as a property (along with the new GradeColor property). This saves me from duplicating any existing properties, exposes the entire Student object to users of the StudentViewModel object, and is completely future-proof because any updates to the Student implementation will automatically show up for users of StudentViewModel.

    Now that we've got a view model class that exposes a view-friendly property that is exactly what we need, our job is easy: change the chart to use StudentViewModels and change the custom template to reference the GradeColor property. Here's the new template (with the same kind of change as before):

    <Style
        x:Key="ColorByGradeColumn"
        TargetType="charting:ColumnDataPoint">
        <Setter Property="Background" Value="DarkGray"/>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate
                    TargetType="charting:ColumnDataPoint">
                    <Border
                        BorderBrush="{TemplateBinding BorderBrush}"
                        BorderThickness="{TemplateBinding BorderThickness}">
                        <Grid Background="{Binding GradeColor}">
                            <Rectangle>
                                <Rectangle.Fill>
                                    <LinearGradientBrush>
                                        <GradientStop Color="#77ffffff" Offset="0"/>
                                        <GradientStop Color="#00ffffff" Offset="1"/>
                                    </LinearGradientBrush>
                                </Rectangle.Fill>
                            </Rectangle>
                            <Border BorderBrush="#ccffffff" BorderThickness="1">
                                <Border BorderBrush="#77ffffff" BorderThickness="1"/>
                            </Border>
                        </Grid>
                    </Border>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
    

    The XAML for this chart is nearly identical and the end result looks just how we wanted it to:

    Color from Grade

    Having shown off how re-templating and MVVM enable more advanced Charting customization scenarios, I've accomplished what I set out to do and could have stopped here... But there was still one customer scenario I wanted to address: synchronizing the colors of pie slices in a pie chart with the colors of columns in a column chart. Given what we've just discussed, the solution is easy: just repeat the re-templating process for a second chart with a PieSeries and PieDataPoints. Because the column/slice colors come from the data objects and because both charts are sharing the same data objects, the color for every data object (student) will naturally be the same across both charts. The re-templated XAML is the same as before and the final result is exactly what we want:

    Color from Student as Pie

    Well, actually, that's not entirely true; Getting the pie slices right was trivial - but there was a bit of additional effort required to synchronize the colors of the pie chart's legend items with the pie slices...

    The way things work is that the Series creates whatever LegendItems it needs. As part of that creation, it also creates a "fake" DataPoint that's styled just like the "real" ones displayed in the chart. This fake data point exists so that the LegendItem's default Template can create Bindings for things like the Background and BorderBrush properties. (Recall that users can completely change the look of a DataPoint, so the only way we have to know how something will look is to create it and see.) This approach works out pretty well, but there was an oversight that caused problems for me when I tried to provide my own PieSeries.LegendItemStyle: the DataContext of the fake PieDataPoint wasn't set to the corresponding slice's data object. Normally, that's no big deal because it's unused - however in this case it's a problem because the custom Template we created above gets its color from the data object. Without a bound data object to provide context, the legend items weren't using the right colors. :(

    I thought about a few ways to work around this, but eventually decided the fix (the setting of the DataContext property for the fake PieDataPoint) belonged in the Charting code itself. Fortunately, Charting is open source, so it's easy for anybody to make such changes if/when the need arises! I've included a copy of the relevant source file with the one-line change I made (Changes\PieSeries.cs, line 317) and changed the sample project to use a custom build of Charting's Microsoft.Windows.Controls.DataVisualization.dll assembly that includes this change.

    And because I'm a nice guy, I also made the same change to the actual charting source code that's under development, got it reviewed, and submitted it (along with an associated unit test) for inclusion in the next official release of the Silverlight Toolkit! After all, if I needed this to work for my sample, chances are good that someone else might need it to work for their application as well. :)

    With that fix in place, here is the Style that applies the proper color to the LegendItems:

    <Style
        x:Key="ColorByPreferenceLegendItem"
        TargetType="charting:LegendItem">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="charting:LegendItem">
                    <StackPanel Orientation="Horizontal">
                        <Rectangle
                            Width="8" Height="8"
                            Fill="{Binding DataContext.FavoriteColor}"
                            Stroke="{Binding BorderBrush}"
                            StrokeThickness="1" Margin="0,0,3,0"/>
                        <datavis:Title Content="{TemplateBinding Content}"/>
                    </StackPanel>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
    

     

    And there you have it: a few simple ways to take Charting and extend it to do exactly what you want! The examples here are fairly simple, but re-templating and MVVM are very powerful concepts which enable a high degree of customization for Silverlight and WPF applications that's pretty hard to come by in other platforms. If you're trying to do something unique and you're not having any luck the "normal" way, please take a few moments to consider the techniques discussed here - you may find that your problem has an easy solution after all!

  • Delay's Blog

    Phone-y charts [Silverlight/WPF Data Visualization Development Release 4 and Windows Phone 7 Charting sample!]

    • 18 Comments

    The April '10 release of the Silverlight Toolkit brought stacked series support to the Data Visualization assembly on the Silverlight 4 platform! You can read an overview here and get a detailed description here. Almost simultaneously, the WPF team released WPF 4 as part of .NET 4 and Visual Studio 2010.

    So it's time for an updated version of my Silverlight/WPF Data Visualization Development Release. Like the previous version, this one continues to support the four platforms of interest to most .NET developers: Silverlight 3, Silverlight 4, WPF 3.5, and WPF 4. But what about Windows Phone 7, the new fifth platform for .NET developers? Yeah, it's supported, too! :)

     

    Silverlight/WPF Data Visualization Development Release 4

    As with previous Data Visualization Development Releases, I've updated the code and binaries to match the most recent Toolkit release. The Silverlight 4 Toolkit shipped most recently, so the code in this Development Release is identical to what just went out with that. Which means people using Data Visualization on Silverlight 3, Windows Phone 7, WPF 3.5, or WPF 4 can also take advantage of the latest round of improvements by updating their applications to use the binaries included with this Development Release (or by compiling the included source code themselves).

    Notes:

    • The directions for using this release are the same as last time, so please have a look at earlier posts if you're new to this or need a refresher.
    • It's easy to use Data Visualization on Windows Phone 7: simply add a reference to the pre-compiled System.Windows.Controls.DataVisualization.Toolkit.dll assembly from this Development Release and a reference to the System.Windows.Controls.dll assembly from the Silverlight 3 SDK (it contains the implementation of Legend's base class HeaderedItemsControl). Then write the same code/XAML you would for any of the other platforms - it's all interchangeable!
      Aside: The version of Silverlight used by Windows Phone 7 is conceptually equivalent to Silverlight 3 with a variety of Silverlight 4 enhancements throughout.
    • All the same cross-platform shared code/XAML goodness of previous Development Releases still applies.

    [Click here to download the SilverlightWpfDataVisualization solution including complete source code and pre-compiled binaries for all platforms.]

     

    Windows Phone 7 Data Visualization Sample

    Trying to use the November 2009 version of the Data Visualization assembly on Windows Phone 7 doesn't work because there's a bug in the Phone version of the .NET Framework that causes bogus exceptions. That bug will be fixed in a future drop of Windows Phone 7 developer bits, but for now it was simplest to work around it myself so folks could start playing around with Data Visualization on the phone today. I wrote the sample application below to show that the Silverlight 3 version of Data Visualization now works seamlessly on Windows Phone 7. And while I was at it, I went ahead and customized the default UI a bit (mostly just tweaking colors and backgrounds) so things really fit in with the overall look and feel of the platform:

    Sample in portrait orientation

    As part of this, I made some trivial Template customizations for the Chart element and added just a smidge of code to switch Templates when the device orientation changes. This allows the sample application to make better use of the available screen space (note how the Legend moves and changes shape):

    Sample in landscape orientation

    Notes:

    • Please be sure to open MainPage.xaml in the development environment before hitting F5 to run the sample - otherwise it seems that deployment of the application to the emulator fails with the message "Object reference not set to an instance of an object.".
    • For convenience, I've included the two required assemblies in the ZIP for the sample - all you need in order to build and run is the Windows Phone Developer Tools CTP which can be downloaded for free here.
    • Though this is hardly the most interesting chart ever, I hope some of what I show here inspires others to achieve greater things! :)

     

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

  • Delay's Blog

    Silverlight Charting remains just a click away - and runs on WPF, too!! [ChartBuilder sample and source code updated for Charting's March 09 release]

    • 17 Comments

    In yesterday's announcement of the March 09 release of the Silverlight Toolkit and Silverlight Charting, I gave an overview of some new Charting features. One of the things I always do right after we release a new version of Charting is update my ChartBuilder sample/application/learning tool. If you're not already familiar with ChartBuilder, here's some background reading: Introduction/user's guide for November 08 release, Fix for non-US cultures, Update for December 08 release.

    This time around is no different and I've just updated the live ChartBuilder application! It's a typical Silverlight 2 application, so you can run it with Silverlight 2 OR the newly released Silverlight 3 Beta.

     

    Please click this text or the image below to run the latest ChartBuilder in your browser.

    ChartBuilder on Silverlight

    You can click here to download the complete ChartBuilder source code.

     

    Release notes:

    • Added support for new series type AreaSeries.
    • Slightly better round-tripping behavior for doubles and DateTimes from the helper class Pair.
    • Various other minor improvements.
    • Updated the version to 2009-03-20.

     

    Although we have - literally - thousands of automated test cases for Charting, ChartBuilder remains our de facto testing tool and has helped us to prevent or debug countless issues during the months we've been developing the Charting framework. I use it regularly to investigate strange behavior or to provide the scaffolding for one-off test cases that would otherwise require quite a bit of time and effort to create.

    ChartBuilder won't win any awards for great user interface design, but it has been worth its weight in gold to us. I hope it helps you, too!

     

     

    Oh, and one more thing... :)

    It's always been our goal with Charting to support both Silverlight and WPF - and we've been compiling Charting for the WPF platform as part of our daily builds for the past few months. I thought it would be neat to show off a sneak preview of WPF Charting here, so I created a WPF build of ChartBuilder and hooked it up to the WPF build of Charting and - yup - it just worked. Well, mostly... :)

    Warning: We have done absolutely no testing of the WPF Charting bits and if you decide to pull them out of the source code download and play around with them, then you do so at your own risk!

    The great thing about what's going on here is that Silverlight Charting and WPF Charting are both compiled from exactly the same source code and XAML and allow you to use exactly the same source code and XAML in your projects. So you'll be able to create a chart on one platform and share it seamlessly with the other platform and you won't have to do any extra work! To help prove my point, ChartBuilder itself is built from exactly the same source code and XAML for both Silverlight and WPF!

     

    ChartBuilder on WPF

     

    The most obvious problem I've seen from playing around with WPF ChartBuilder for a little is that sometimes dynamically added/removed DataPoints don't play their reveal/show/hide transition properly. Some of the points coming in are obviously present in the chart because there's space reserved for them and their tooltips work properly - but they're not visible. And some of the points going out just don't go away like they're supposed to. My primary suspect here is VSM (the Visual State Manager; a Silverlight concept that's been ported to WPF); WPF Charting uses the WPF Toolkit's preview implementation of VSM and there may be some incompatibilities between that implementation and the one in Silverlight. Charting on WPF also has a few fit-and-finish issues; these are probably easy to fix, but like I said we haven't spent any time working on Charting for WPF.

    Important: If you do decide to try WPF Charting, please note that it's necessary to add references to both the DataVisualization assembly and the WPFToolkit assembly or else you'll get a weird error from WPF when we attempt to use VSM and its implementation isn't present.

     

    Okay, that's all I've got for now. :) I hope you have fun with Charting - on whatever platform you choose!

  • Delay's Blog

    Silverlight Charting gets a host of improvements [Silverlight Toolkit December 08 release now available!]

    • 17 Comments

    The December 08 release of the Silverlight Toolkit was published a short while ago. Just about every control in the Toolkit got some love and attention for this release and I encourage you to have a look, download it, and enjoy!

    That said, readers of my blog know that I'm all about the Charting. :) So just like my original Charting announcement and overview post, here's an announcement of the December 08 Charting release and an overview of some of the high points. By now, I assume folks have had time to play around with Charting and are starting to get into some more advanced scenarios, so the samples are going to be a little more technical than before. If you want a refresher on basic Charting concepts, please have a look at my original overview - all of those concepts still apply.

    Though the Charting team was down to just two of us for this release, Jafar and I have done our best to deliver some pretty compelling new features. It's important to note that there are a few breaking changes from the November 08 release, but I hope you'll agree that the new functionality is worth the minor inconvenience of updating existing Charting code. As it happens, if you don't explicitly use the Axis class, you probably won't need to change anything at all!

     

    I wrote the following summary for the release notes, and it seems like a good way to begin:

    Notable Changes

    Support has been added for arbitrary numbers of Axes for a Chart. A key and significant consequence of this change is that it is now possible to mix previously incompatible Series in the same chart (example: Column and Bar). The former "automatically share axes when possible" behavior remains present and can be used to render two series with a shared independent axis and different dependent axes in order to display related-but-differently-scaled values (example: an engine's torque and RPM).

    The Column/Bar/Line/Scatter Series classes expose two new (optional) properties which specify a particular Axis instance to be used by the Series (rather than letting the Series choose from the Chart's Axes collection as it does by default). These properties allow very specific customization of each Series' axes in situations where maximum control is desired.

    A design-time assembly for Charting has been created which is automatically used by Blend and Visual Studio to enhance the design-time experience. Most class properties are now categorized into custom "Data Visualization" and "Data Visualization Styles" categories to make them easier to find - while particularly common properties are now found in the "Common" category. ToolTips identify the purpose of each control and property, and Toolbox icons are automatically associated with each class. (Note: Some of these features are only supported by one of the design tools.)

    The design-time behavior of DataPoints has been enhanced: DataPoints are now visible by default when dropped onto the design surface in Blend, so styling them is easier. PieDataPoint now creates a default Geometry (that can be customized interactively with the ActualRatio and ActualOffsetRatio properties) which makes it possible to see and understand relevant styling changes. DataPoints in a Chart show up in Visual Studio without a refresh of the design surface.

    The Chart class is now decorated with ContentPropertyAttribute("Series") which means that it is no longer necessary to explicitly wrap Series with <charting:Chart.Series> ... </charting:Chart.Series> in XAML. Additionally, this enables Blend to display a Chart's Series in the "Objects and Timeline" pane and makes accessing the properties of a Series considerably easier. Example of simplified XAML syntax:

        <charting:Chart>
            <charting:PieSeries ... />
        </charting:Chart>
    

    The behavior of the Axis classes during animations that expand or shrink the range of displayed data has been changed so that the animation of the size change is smooth (vs. jumping between axis intervals). This significantly increases the ease with which dynamic data changes can be observed and understood by the viewer.

    The DataPointSeries class (a subclass of Series) has been unsealed to make the task of writing a custom Series considerably easier. While this change doesn't expose the entire hierarchy on which the "in-box" Series are built, it is a significant benefit to developers because DataPointSeries implements many of the key Series notions: the ItemsSource property, DataPoint creation, dynamic data detection, change animation, show/hide transitions, DataPoint selection, and more.

    A new Series type, BubbleSeries, has been added (along with its associated BubbleDataPoint class). BubbleSeries is similar in nature to ScatterSeries, but conveys additional information by using the size of the data points to display an additional dependent value for each of the data points.

    In scenarios where multiple columns/bars (of ColumnSeries/BarSeries) share the same category, all such data points will now be automatically displayed overlapping so that smaller values will no longer be obscured from view by larger values.

    Breaking Changes

    The Axis.AxisType property has been replaced by a corresponding hierarchy of Axis classes. The concrete classes map directly to the AxisType values that were removed: LinearAxis, DateTimeAxis, CategoryAxis. In addition to improving overall API consistency, a consequence of this refactoring is that the LinearAxis.Minimum/Maximum properties are now strongly typed as double and the corresponding properties on DateTimeAxis are strongly typed as DateTime - both of which improve development-time and design-time usability.

    The Axis.ShouldIncludeZero property has been removed; this property is has no meaning for some axis types and is typically not needed because the same thing can be accomplished by setting Minimum or Maximum accordingly. A consequence of this is that Column/Bar charts will usually not include the 0 value by default (and therefore the "beginnings" of the columns/bars can be truncated). This behavior is consistent with Excel (demonstrated by creating a column chart of the values 10, 11, and 12 in Excel), though truncation is more likely because Charting's heuristics are more aggressive with regard to excluding 0.

    Other Changes

    Various UI improvements and bug fixes.

    Whew - there's a lot of good stuff in there! :)

     

    So now that we have an idea what's new, let's start by looking at the support for multiple axes with a common scenario: charting two quantities that are related, but measured in different units and/or on different scales. For the purposes of this demonstration, let's chart the performance characteristics of an imaginary engine:

    Chart with multiple axes

    [Note: Complete source code for all of the sample charts here can be found in the ChartingIntroduction.zip file attached to this post.]

    The XAML for this Chart is straightforward (and discussed below):

    <charting:Chart Title="Engine Performance">
        <!-- Power curve -->
        <charting:LineSeries
            Title="Power"
            ItemsSource="{StaticResource EngineMeasurementCollection}"
            IndependentValueBinding="{Binding Speed}"
            DependentValueBinding="{Binding Power}"
            MarkerWidth="5"
            MarkerHeight="5">
            <!-- Vertical axis for power curve -->
            <charting:LineSeries.DependentRangeAxis>
                <charting:LinearAxis
                    Orientation="Vertical"
                    Title="Power (hp)"
                    Minimum="0"
                    Maximum="250"
                    Interval="50"
                    ShowGridLines="True"/>
            </charting:LineSeries.DependentRangeAxis>
        </charting:LineSeries>
        <!-- Torque curve -->
        <charting:LineSeries
            Title="Torque"
            ItemsSource="{StaticResource EngineMeasurementCollection}"
            IndependentValueBinding="{Binding Speed}"
            DependentValueBinding="{Binding Torque}"
            MarkerWidth="5"
            MarkerHeight="5">
            <!-- Vertical axis for torque curve -->
            <charting:LineSeries.DependentRangeAxis>
                <charting:LinearAxis
                    Orientation="Vertical"
                    Title="Torque (lb-ft)"
                    Minimum="50"
                    Maximum="300"
                    Interval="50"/>
            </charting:LineSeries.DependentRangeAxis>
        </charting:LineSeries>
        <charting:Chart.Axes>
            <!-- Shared horizontal axis -->
            <charting:LinearAxis
                Orientation="Horizontal"
                Title="Speed (rpm)"
                Interval="1000"
                ShowGridLines="True"/>
        </charting:Chart.Axes>
    </charting:Chart>
    

    We start with the usual Chart object to contain the series - but now we can put the series directly inside the Chart tags instead of inside nested Chart.Series tags (though that syntax still works and is fully supported). There are two LineSeries here (one for torque and one for power) and each starts out as you'd expect by hooking up to the data and doing a bit of customization. But then there's something new: the LineSeries.DependentRangeAxis property is used to identify a specific LinearAxis. Every series type (other than PieSeries which doesn't have axes) now exposes a Dependent???Axis property and an Independent???Axis property (where "???" is "Range" or "Category" depending on which series it is).

    Normally, a series will search the Chart.Axis collection to find an axis that it can use - but when these new properties are set, it always uses the specified axis. So in this example we're providing a specific LinearAxis for each LineSeries to use and we're customizing it slightly to get the chart looking just how we want. Having specified both LineSeries in this manner, a third LinearAxis is added to the Chart.Axis collection where it will be found - and used - by both LineSeries when they acquire an independent value axis.

     

    Moving on, here's a chart using the new BubbleSeries in a financial scenario (for a fictional stock ticker symbol). It's the usual "stock price by date" chart we've all seen before - but this chart is also showing the trading volume each day by varying the size the data points:

    Bubble chart

    The XAML for that Chart is what you'd expect:

    <charting:Chart Title="Stock Performance">
        <!-- Stock price and volume -->
        <charting:BubbleSeries
            Title="ABCD"
            ItemsSource="{StaticResource StockDataCollection}"
            IndependentValueBinding="{Binding Date}"
            DependentValueBinding="{Binding Price}"
            SizeValueBinding="{Binding Volume}"
            DataPointStyle="{StaticResource CustomBubbleDataPointStyle}"/>
        <charting:Chart.Axes>
            <!-- Axis for custom labels -->
            <charting:DateTimeAxis
                Orientation="Horizontal">
                <charting:DateTimeAxis.AxisLabelStyle>
                    <Style TargetType="charting:DateTimeAxisLabel">
                        <Setter Property="StringFormat" Value="{}{0:MMM d}"/>
                    </Style>
                </charting:DateTimeAxis.AxisLabelStyle>
            </charting:DateTimeAxis>
        </charting:Chart.Axes>
    </charting:Chart>
    

    BubbleSeries is used just like a ScatterSeries, but it exposes an additional property SizeValueBinding that works just like Independent/DependentValueBinding to identify the source of the size values. The DataPointStyle property specifies a custom style for BubbleDataPoint (not shown here) where we've added the day's volume to the ToolTip of each bubble. This custom style was created in the usual manner - by starting from the default style for BubbleDataPoint and making the desired changes. (I did so in Visual Studio's XAML editor, but it could just as easily be done in Blend.)

    Tweaking things just a little more, the independent value axis is customized by setting its AxisLabelStyle property and providing a style for the new DateTimeAxisLabel class. DateTime/NumericAxis/AxisLabel are the classes used to display an axis's labels. In this case we've provided a custom StringFormat so that the dates are all displayed in the friendly Month+Day pattern seen above. DateTimeAxisLabel offers specific StringFormat properties for each supported IntervalType - but in this case we've simply used the StringFormat property which conveniently overrides the other, more specific types.

     

    Next up is a fairly typical column chart showing made-up bowling scores for some of the people on the Toolkit team. But there's twist because there are two "Shawn"s on the team:

    Chart with overlapping columns

    By now, the XAML is probably kind of boring in its predictability:

    <charting:Chart Title="Bowling Scores">
        <!-- Scores -->
        <charting:ColumnSeries
            Title="Score"
            ItemsSource="{StaticResource ScoreDataCollection}"
            IndependentValueBinding="{Binding Player}"
            DependentValueBinding="{Binding Score}">
            <charting:ColumnSeries.IndependentCategoryAxis>
                <!-- Axis for automatic sorting -->
                <charting:CategoryAxis
                    Orientation="Horizontal"
                    SortOrder="Ascending"/>
            </charting:ColumnSeries.IndependentCategoryAxis>
        </charting:ColumnSeries>
    </charting:Chart>
    

    This is a standard ColumnSeries, which is specifying a IndependentCategoryAxis so the new SortOrder property of CategoryAxis can be used to automatically sort the category names. This data set contains two items with the independent value "Shawn". Previously, both would have been displayed in the same category slot with the same width which means the column in front could have obscured the one in back and led viewers to believe that only four scores were being displayed. But now ColumnSeries and BarSeries automatically detect this situation and overlap columns/bars in the same category slot (after sorting them by size). This behavior makes it clear that five values are being shown and makes it easy to hover over either of the "Shawn"s to get a ToolTip with the associated score.

     

    The previous chart is interesting for another reason, too: it's using the natural, simple way to display the data, but a purist might argue that it's also the "wrong" way to do so... [Jafar and I actually had this "argument" - I won't say who took which position. :) ] Categorizing by player name works well enough, but what if we also had an image for each player and we wanted to display that image instead of their name? Changing the ColumnSeries.IndependentValueBinding property might work, but it's become obvious that we're mixing display concerns and data concerns.

    When maintaining developer/designer separation is critical, what's probably more appropriate is to categorize by the actual data object - and then display it however we want. Here's a chart that does just that:

    Chart with unique categories

    And here's the XAML - which is a bit more complicated than before, but still quite manageable:

    <charting:Chart>
        <!-- Customized Title -->
        <charting:Chart.Title>
            <StackPanel>
                <TextBlock
                    Text="Bowling Scores"
                    HorizontalAlignment="Center"/>
                <TextBlock
                    Text="(Alternate Approach)"
                    FontSize="10"
                    HorizontalAlignment="Center"/>
            </StackPanel>
        </charting:Chart.Title>
        <!-- Scores -->
        <charting:ColumnSeries
            Title="Score"
            ItemsSource="{StaticResource ScoreDataCollection}"
            IndependentValueBinding="{Binding}"
            DependentValueBinding="{Binding Score}">
            <charting:ColumnSeries.IndependentCategoryAxis>
                <!-- Axis for automatic sorting and custom labels -->
                <charting:CategoryAxis
                    Orientation="Horizontal"
                    SortOrder="Ascending">
                    <charting:CategoryAxis.AxisLabelStyle>
                        <Style TargetType="charting:AxisLabel">
                            <Setter Property="Template">
                                <Setter.Value>
                                    <ControlTemplate TargetType="charting:AxisLabel">
                                        <TextBlock Text="{Binding Player}"/>
                                    </ControlTemplate>
                                </Setter.Value>
                            </Setter>
                        </Style>
                    </charting:CategoryAxis.AxisLabelStyle>
                </charting:CategoryAxis>
            </charting:ColumnSeries.IndependentCategoryAxis>
        </charting:ColumnSeries>
    </charting:Chart>
    

    Now that we've set IndependentValueBinding to the data objects themselves, there are automatically five categories (one for each of the five objects in the data set). (Aside: CategoryAxis will still sort them for us because the data objects implement the IComparable interface.) Once the data is set up "properly", all that's left to do is customize how the objects are displayed - and this is done by providing a custom template for the AxisLabel class (something that's already been discussed).

    One other thing to note above is how the Chart.Title property is set to a tree of UI elements instead of the usual string. Chart.Title (and other such properties in Charting) follows the ContentControl model because it makes simple things simple while also allowing more advanced scenarios. In this case, all we've done is provide a "subtitle" - but we could also have gone much further and created something very customized.

     

    The last thing I wanted to highlight was some of the design-time enhancements for Charting in this release. Data points were previously a little tricky to customize because their default state is not visible (which allows the reveal/show animation to fade them in without flicker). Data points now check if they're being used in design-time and will automatically play their reveal/show animation so that they're visible on the design surface by default. PieDataPoint used to be extra tricky because its Geometry property is only set when it's actually being used by PieSeries. But now it knows about design-time and its ActualRatio/ActualOffsetRatio properties can be changed to interactively evaluate different styling approaches. Here's a stand-alone PieDataPoint in Blend showing off both of these improvements (along with the new "Data Visualization" property category):

    PieDataPoint in Blend

     

    I hope you're excited by some of the new functionality we've just looked at from the December 08 release of Charting! And naturally, we've made a some other improvements, too! :) You can browse the live Charting sample page to find out more - then download the December 08 Toolkit release and start playing around with the new stuff! As always, if you have questions about Charting, you can ask them in the Silverlight Controls forum. If you think you've found a bug, please report it with the Issue Tracker in the Toolkit's CodePlex site.

     

    PS - If you're looking for an update to my ChartBuilder application (Background reading: Introduction, Update) to show off the new Charting functionality, please stay tuned because it will be available very soon! :)

    PPS - I'm on vacation in December, so responses to blog comments and emails may be delayed. But please don't let that stop you from contacting me: I'll follow up on everything I get - I just might be a little slower than usual. :)

  • Delay's Blog

    Smaller is better! [A simple step to shrink the download size of Silverlight 2 applications]

    • 16 Comments

    When putting content on the web, it's important to get that content to the user as quickly as possible. In general, reducing the amount of data users need to download is a big step in the right direction. As such, the XAP packaging format used by Silverlight 2 wraps everything in a single, compressed archive file that's browser-friendly. At the moment, the compression for XAP files in Beta 2 is not as efficient as it could be, and XAP files tend to be a bit larger than necessary. Until this is addressed in a future release of the Silverlight tools, here is something that's almost as good. :)

    XAP files are really just standard ZIP files with a different extension, so we can use ZIP-file tools to improve the XAP situation quite easily. I wrote a simple batch file named XapReZip.cmd (contents below) that can be added to a Visual Studio 2008 Silverlight project to automatically re-compress the project's XAP file more efficiently. All you need to do is add a single command to your project's post-build events (detailed below) and from then on all your XAP files will be even smaller and quicker to download!

    To demonstrate its effectiveness, I've run XapReZip against three test applications, two simple demonstration applications of mine, and the current version of two professional-grade applications you're probably already familiar with. The table below shows the starting XAP file size, the size after running XapReZip, the difference between the two, and the percent difference. (All sizes are in bytes.)

    Scenario Starting Size After XapReZip Difference Reduction
    New Silverlight Project 3,980 3,202 778 20%
    ... with DatePicker 73,228 56,513 16,715 23%
    ... and DataGrid 194,452 151,646 42,806 22%
    HtmlTextBlock 12,360 10,312 2,048 17%
    SimpleSilverlightXpsViewer 13,879 11,408 2,471 18%
    NBC Olympics Video Player 789,523 615,239 174,284 22%
    Line Rider 789,496 538,784 250,712 32%

    Basically, you can expect to see about a 22% reduction in size for XAP files in general - slightly less for code-intensive XAPs and potentially much more for larger, richer XAPs. For example, it looks like Line Rider could shave a quarter of a megabyte off its download size without breaking a sweat - which is even more compelling given how easy it would be! :)

    The complete code for XapReZip.cmd is below. As written, XapReZip assumes that Zip.exe and UnZip.exe will be located in the same directory as XapReZip.cmd (this is easy to change) and that all of the files that are part of the XAP are available in the same directory as the XAP file itself (which just happens to be true after compiling with Visual Studio or MSBuild). For the purposes of this demonstration, I've used Zip 2.32 and UnZip 5.52 from the Info-ZIP group because they're free and have a nice license. Of course, you can use whatever tools you're most comfortable with.

    @REM XapReZip - Recompresses XAP files smaller
    
    @REM Invoke as a VS post-build event like so:
    @REM C:\XapReZip\XapReZip.cmd $(TargetName).xap
    
    @echo off
    setlocal
    
    REM Define paths to zip.exe and unzip.exe
    set ZIPEXE="%~p0zip.exe"
    set UNZIPEXE="%~p0unzip.exe"
    
    REM Define paths for intermediate files
    set XAP=%1
    set XAPDIR=%~p1
    set XAPBAK=%1.bak
    set XAPZIP=%1.zip
    
    REM Output a banner message
    echo XapReZip: %XAP%
    
    REM Change to XAP file directory
    pushd %XAPDIR%
    
    REM Create new XAP file with maximum compression
    %UNZIPEXE% -Z -1 %XAP% | %ZIPEXE% -@ -9 -X %XAPZIP%
    
    REM Abort if something went wrong
    if ERRORLEVEL 1 goto :DONE
    
    REM Replace original XAP file with smaller one
    copy /y %XAP% %XAPBAK% > NUL
    copy /y %XAPZIP% %XAP% > NUL
    del %XAPZIP%
    
    REM Output a success message
    echo XapReZip: Success
    
    :DONE
    
    REM Restore previous directory
    popd
    
    endlocal

    Adding XapReZip to a Silverlight Project in Visual Studio is easy. Just go to the Project menu, choose Properties (it's the last item), switch to the Build Events tab, and set a post-build event command line like this: C:\XapReZip\XapReZip.cmd $(TargetName).xap. Here's what it looks like in Visual Studio:

    Configuring XapReZip as a post-build event in Visual Studio

    After you've set that one command, you can forget about it - XapReZip will go to work every time you recompile the application and you'll be able to run and debug it just like always. Except a little smaller. :)

    So if you're eager to keep your Silverlight applications as lean as possible, consider adding XapReZip to your project. It'll probably be the easiest 22% savings you get all day!

  • Delay's Blog

    Where's your leak at? [Using WinDbg, SOS, and GCRoot to diagnose a .NET memory leak]

    • 16 Comments

    In my last post, I explained how it was possible for "hidden" event handlers to introduce memory leaks and showed an easy way to prevent such leaks. I used a sample application to contrast a leaky implementation with one that uses the WeakEventListener class (included as part of the post) to avoid leaking on Silverlight. The changes required to patch the leak were fairly minimal and the entire process was pretty straightforward. But I glossed over one important point...

    What if you don't know the source of the memory leak in the first place? Knowing how something is leaking is the first step to fixing it, and the web has some great resources for learning more about tracking down managed memory leaks in WPF and Silverlight applications. I am not going to try to duplicate that information here. :) Instead, I'll refer interested readers to these excellent resources and recommend a bit of web searching if additional background is needed:

    As luck would have it, there are also a number of fine tools available to help find managed memory leaks. Being a rather frugal individual myself, I nearly always prefer to use free stuff and it just so happens that two of the best tools available are free from Microsoft! They are:

    Again, this post is not a tutorial for either tool. :) Instead, it will demonstrate how to use these tools together to answer a specific question: What part of the sample application's LeakyControl code is causing a leak? The basic technique I'll use is described in the following two posts which do a great job covering the topic:

    Now that we're ready to go, let's remind ourselves what the demo application looked like:

    WeakEventListener Sample Application

    To reproduce the leak, build the sample application and run it outside the Visual Studio debugger (because we'll be using WinDbg instead). You can do this by hitting Ctrl+F5 ("Start Without Debugging") or by double-clicking the TestPage.html file in the Bin\Debug folder. As before, click "Remove From UI" to discard both controls, then click "Garbage Collect" to perform a collection, then click "Check Status". You'll see that FixedControl is gone, but LeakyControl is still present despite our attempt to get rid of it.

    Now start WinDbg from the Start Menu, hit F6 to "Attach to a Process", and pick the iexplore.exe instance corresponding to the test application. (If there are multiple instances of iexplore.exe, you can determine the proper PID via Task Manager or you can just guess - it's nearly always the one at the bottom of the list!) If all went well, you'll see a bunch of modules get loaded and a couple of them should have "Silverlight" in their path. (If not, try again and attach to a different instance of iexplore.exe.) Great, now we're ready to go!

    First, we'll load the SOS debugging extension:

    0:012> .loadby sos coreclr
    

    In this case, we know we're leaking an instance of the LeakyControl class, so what we'll do is find all of the instances of LeakyControl in the managed heap. We expect there to be zero at this point, so if one is present, then it has been leaked. DumpHeap tells us this easily:

    0:012> !DumpHeap -type LeakyControl
     Address       MT     Size
    03ff6df4 02f43c80       56
    total 1 objects
    Statistics:
          MT    Count    TotalSize Class Name
    02f43c80        1           56 WeakEventListenerDemo.LeakyControl
    Total 1 objects
    

    Yep, we're leaking an instance of LeakyControl... Let's find out what reference is keeping this instance alive:

    0:012> !GCRoot 03ff6df4
    Note: Roots found on stacks may be false positives. Run "!help gcroot" for
    more info.
    Scan Thread 10 OSTHread 7ec
    Scan Thread 11 OSTHread e44
    Scan Thread 12 OSTHread e04
    DOMAIN(003CB450):HANDLE(Pinned):2f512f8:Root:  04fe78e0(System.Object[])->
      03fef5bc(WeakEventListenerDemo.App)->
      03ff0710(WeakEventListenerDemo.Page)->
      03ff77b0(System.Collections.ObjectModel.ObservableCollection`1[[System.Int32, mscorlib]])->
      03ff7a20(System.Collections.Specialized.NotifyCollectionChangedEventHandler)->
      03ff7a08(System.Object[])->
      03ff7920(System.Collections.Specialized.NotifyCollectionChangedEventHandler)->
      03ff6df4(WeakEventListenerDemo.LeakyControl)->
    

    I find it's usually easiest to start from the bottom of GCRoot output: in this case we see the LeakyControl instance is referenced by an instance of NotifyCollectionChangedEventHandler. Now, in the trivial sample application that's all we need to identify the source of the leak and we could stop here. But in a larger, more realistic application there might be many places where a NotifyCollectionChangedEventHandler is created - let's see if we can narrow this down even further:

    0:012> !DumpObj 03ff7920
    Name:        System.Collections.Specialized.NotifyCollectionChangedEventHandler
    MethodTable: 02e9dcc0
    EEClass:     02ea0760
    Size:        32(0x20) bytes
    File:        c:\Program Files\Microsoft Silverlight\2.0.40115.0\System.Windows.dll
    Fields:
          MT    Field   Offset                 Type VT     Attr    Value Name
    02a444e8  40001e0        4        System.Object  0 instance 03ff6df4 _target
    02c13c58  40001e1        8 ...ection.MethodBase  0 instance 00000000 _methodBase
    02b00a90  40001e2        c        System.IntPtr  1 instance  2f4c14c _methodPtr
    02b00a90  40001e3       10        System.IntPtr  1 instance        0 _methodPtrAux
    02a444e8  40001e4       14        System.Object  0 instance 00000000 _invocationList
    02b00a90  40001e5       18        System.IntPtr  1 instance        0 _invocationCount
    

    What would be nice is if we could figure out which method in LeakyControl corresponds to that _methodPtr because doing so would tell us which particular event hook-up was involved. Let's try the easy way first:

    0:012> !IP2MD 2f4c14c
    Failed to request MethodData, not in JIT code range
    

    Okay, so much for the easy way; the method hasn't been JITted yet. Let's look at the code corresponding to _methodPtr next:

    0:012> !U 2f4c14c
    Unmanaged code
    02f4c14c b84c3cf402      mov     eax,2F43C4Ch
    02f4c151 89ed            mov     ebp,ebp
    02f4c153 e9bc46ceff      jmp     02c30814
    02f4c158 00b000eb18b0    add     byte ptr [eax-4FE71500h],dh
    02f4c15e 02eb            add     ch,bl
    02f4c160 14b0            adc     al,0B0h
    02f4c162 04eb            add     al,0EBh
    02f4c164 10b006eb0cb0    adc     byte ptr [eax-4FF314FAh],dh
    02f4c16a 08eb            or      bl,ch
    02f4c16c 08b00aeb04b0    or      byte ptr [eax-4FFB14F6h],dh
    

    Because the method hasn't been JITted, it's a pretty safe bet that we're looking at a thunk here. Let's take the value it's using and try to examine that as a MethodDesc:

    0:012> !DumpMD 2F43C4C
    Method Name: WeakEventListenerDemo.LeakyControl.OnCollectionChanged(System.Object, System.Collections.Specialized.NotifyCollectionChangedEventArgs)
    Class:       02f41b18
    MethodTable: 02f43c80
    mdToken:     06000013
    Module:      02f433bc
    IsJitted:    no
    CodeAddr:    ffffffff
    

    Success! We see this event handler is for the LeakyControl.OnCollectionChanged method - so now we know exactly which event handler hook-up is responsible for the leak. And, armed with the knowledge from my last post, we've got everything we need to fix this code and patch the leak!

    Once we do, our code will be a little better behaved, our developers won't have to track down leaks in our code, and our users will see more solid, more predictable behavior with lower memory use. Nice work - it's donut time! :)

  • Delay's Blog

    You voted lots, we fixed lots [AJAX Control Toolkit release!]

    • 16 Comments

    Last night we published the 10920 release of the AJAX Control Toolkit. This release continued our trend of focusing on the most popular bugs and work items identified by the user community in the support forum and online issue tracker. A number of popular issues got fixed in this release, addressing nearly 1000 user votes!

    The release notes from the sample web site detail the improvements:

    General fixes:

    • Controls with Embedded styles (Calendar, Tabs and Slider): Toolkit controls no longer need explicit style references when loaded asynchronously. For example, if a Calendar control is placed inside an UpdatePanel and made visible on an UpdatePanel postback, the embedded styles are now loaded properly.
    • PopupBehavior positioning (AutoComplete, Calendar, DropDown, HoverMenu, ListSearch, PopupControl and ValidatorCallout): PopupBehavior now respects the position of its parent element even when the browser window is very narrow or the parent element is close the window edge.
    • Focusing extended controls (Accordion, CollapsiblePanel, DropShadow, Tabs): Pages that use Toolkit controls which re-parent DOM elements can use a workaround to focus a specific element on page load. The new method Utility.SetFocusOnLoad ensures that the desired control receives focus.

    Control specific fixes:

    • Calendar: Property to specify the position of Calendar, a default date feature that allows the calendar to start out with a selected date, and a consistent show, hide and focus story that makes the Calendar user experience more intuitive.
    • ModalPopup: Ability to disable repositioning of the ModalPopup in response to window resize and scroll.
    • ConfirmButton: ModalPopup functionality now supported in addition to the regular windows alert dialog.
    • MaskedEdit: Extended Textbox no longer uses Invariant culture if no CultureName is specified and falls back to the Page Culture.
    • AutoComplete: Allow users to associate additional data with the AutoComplete suggestions.
    • Slider: Slider can be easily customized using its various CSS properties.

    As with the previous release, we have published "source" and "no-source" versions for .NET 2.0/Visual Studio 2005 as well as for .NET 3.5/Visual Studio 2008 (still in Beta). Unique to the 3.5/2008 versions are the following:

    Features:

    • JavaScript IntelliSense support: We have added reference tags to all Toolkit JavaScript files that enables you to take advantage of new features in Visual Studio 2008 Beta 2. With the multi-targeting support in this Visual Studio Beta, IntelliSense will be available for the ASP.NET AJAX 1.0 flavor of the Toolkit as well. This article discusses the reference tag feature in detail.
    • Extender designer support: Enhanced designer support for Toolkit controls using the new "Add Extender" user interface.

    One thing we'd hoped to include with this release didn't quite make it in: our new automated testing framework. This framework is based on a different approach than our current framework - one that makes it easy to add additional test cases and leverage existing ones across new scenarios. The new testing framework has already dramatically improved our test coverage, helped identify new issues, and made fixing existing issues less risky!

    But we've been iterating on the new framework for the past couple of weeks and faced the usual decision when it came time to finalize this release: slip or ship. We slipped our release date a little in the hopes that we'd be able to include the new framework with this release, but eventually decided not to delay all the great new Toolkit code any longer. We wanted our users to take advantage of the new bits ASAP - so stay tuned for more on the new testing framework in a future release!

    As always, it's easy to sample any of the controls (no install required). You can also browse the project web site, download the latest Toolkit, and start creating your own controls and/or contributing to the project!

    If you have any feedback, please share it with us on the support forum!

Page 5 of 28 (277 items) «34567»