Delay's Blog

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

Posts
  • Delay's Blog

    Dynamic content made easy *redux* [How to: Use the new dynamic population support for Toolkit controls]

    • 23 Comments

    I previously blogged a sample demonstrating how to use the AJAX Control Toolkit's dynamic population functionality with a data-bound ModalPopup. That sample was well received, but some of the changes that happened as part of the ASP.NET AJAX Beta 1 and Beta 2 releases have rendered the sample code I originally posted un-runnable on current builds.

    No worries - it's a simple matter to update the sample and the exercise should serve as a good example of some of the things that need to be done as part of an upgrade. (For a far more in depth guide to upgrading, please refer to Shawn's AJAX Control Toolkit Migration Guide and follow-up about upgrading Web Services/Methods.)

    Referring to the sample code I posted earlier as a starting point, the 5 things we need to do to update it are:

    1. Change the tag prefix for ScriptManager from "atlas" to "asp" to point to the new location for ScriptManager
    2. Change the @Register tag to refer to the AjaxControlToolkit by its new name ("AtlasControlToolkit"->"AjaxControlToolkit" twice and "atlasToolkit"->"ajaxToolkit" once)
    3. Change the corresponding tag prefix for ModalPopupExtender from "atlasToolkit" to "ajaxToolkit"
    4. Copy the properties of the ModalPopupProperties element to the ModalPopupExtender element and remove the empty ModalPopupProperties element now that the XxxProperties elements are no longer used
    5. Update the web method definition by making the GetContent method static and adding the [Microsoft.Web.Script.Services.ScriptMethod] attribute

    That's it! As you can see, it's mostly simple "find-and-replace" naming changes that can easily be applied to an entire site at once. The XxxProperties change is slightly more involved, but requires very little effort since it's basically just a "cut-and-paste" operation. The web method changes are also a little bit of effort, but the changes themselves are simple and it's easy to find where to make them by searching for the pre-existing "WebMethod" attribute. Nothing here is risky or error-prone and - with the exception of the web method changes - the compiler will report all of them as errors until they're fixed correctly.

    For completeness, here's a reminder of what dynamic population looks like in action:

    Animated demonstration

    And here's the complete sample code after the changes discussed above have been applied. This sample runs fine on the ASP.NET AJAX Beta 2 and the AJAX Control Toolkit 61106 release:

    <%@ Page Language="C#" %>
    <%
    @ Register Assembly="AjaxControlToolkit" Namespace="AjaxControlToolkit"
      TagPrefix="ajaxToolkit" %>
    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
      "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

    <
    script runat="server">
      [System.Web.Services.WebMethod]
      [Microsoft.Web.Script.Services.
    ScriptMethod]
      
    public static string GetContent(string contextKey)
      {
        
    // Create a random color
        string color = (new Random()).Next(0xffffff).ToString("x6");
        
    // Use the style specified by the page author
        string style = contextKey;
        
    // Display the current time
        string time = DateTime.Now.ToLongTimeString();
        
    // Compose the content to return
        return "<span style='color:#" + color + "; " + style + "'>" + time + "</span> ";
      }
    </script>

    <
    html xmlns="http://www.w3.org/1999/xhtml">
    <
    head id="Head1" runat="server">
      <title>Dynamic ModalPopup Demonstration</title>

      <%-- Style the page so it looks pretty --%>
      
    <style type="text/css">
        body { font-family:Verdana;     font-size:10pt; }
        
    .background { background-color:Gray; }
        
    .popup { width:200px;             padding:10px;        background-color:White;
                
    border-style:solid;      border-color:Black;  border-width:2px;
                
    vertical-align: middle;  text-align:center; }
      
    </style>
    </
    head>
    <
    body>
      <form id="form1" runat="server">

        <%-- Atlas pages need a ScriptManager --%>
        
    <asp:ScriptManager ID="ScriptManager1" runat="server" />

        <%-- A very simple data source to drive the demonstration --%>
        
    <asp:XmlDataSource ID="XmlDataSource1" runat="server">
          <Data>
            <items>
              <item style="font-weight:bold" />
              <item style="font-style:italic" />
              <item style="text-decoration:underline" />
            </items>
          </Data>
        </asp:XmlDataSource>

        <%-- A simple list of all the data items available --%>
        
    <asp:DataList ID="DataList1" runat="server" DataSourceID="XmlDataSource1">

          <HeaderTemplate>
            How would you like your dynamic content styled?
          
    </HeaderTemplate>

          <ItemTemplate>
            &bull; <asp:LinkButton ID="LinkButton" runat="server" Text='<%# Eval("style") %>' />

            <%-- The ModalPopupExtender, popping up Panel1 and dynamically populating Panel2 --%>
            
    <ajaxToolkit:ModalPopupExtender ID="ModalPopupExtender" runat="server"
                TargetControlID="LinkButton" PopupControlID="Panel1" OkControlID="Button1"
                BackgroundCssClass="background" DynamicControlID="Panel2"
                DynamicContextKey='<%# Eval("style") %>' DynamicServiceMethod="GetContent" />
          </ItemTemplate>

        </asp:DataList>

        <%-- All ModalPopups share the same popup --%>
        
    <asp:Panel ID="Panel1" runat="server" CssClass="popup" style="display:none;">
          <p>This popup popped at <asp:Label ID="Panel2" runat="server" /> and all was well.</p>
          <p><asp:Button ID="Button1" runat="server" Text="OK" /></p>
        </asp:Panel>

      </form>
    </
    body>
    </
    html>

    Enjoy!

  • Delay's Blog

    Script combining made easy [Overview of the AJAX Control Toolkit's ToolkitScriptManager]

    • 22 Comments

    Note: Recent versions of ASP.NET AJAX include the ScriptManager.CompositeScript property which performs much the same functionality as discussed here. More information is available in the article Combining Client Scripts into a Composite Script.

     

    Introduction

    The 10606 release of the AJAX Control Toolkit includes ToolkitScriptManager, a new class that derives from the ASP.NET AJAX ScriptManager and performs automatic script combining.

    What is meant by "script combining" and why is it desirable?

    ASP.NET AJAX Behaviors are typically implemented by JavaScript (JS) files that are downloaded by the browser as part of the web page's content/resources (like CSS files, images, etc.). Each JS file typically contains a single Behavior, so if a web page uses lots of Behaviors, it's going to download lots of Behavior JS files. The cost to download a single JS file is fairly minimal, but when there are many of them on a page, the serialized nature of Behaviors (later ones may depend on earlier ones and can't be loaded until the earlier ones have finished) means that it may take a bit of time for all the JS a page needs to download to the user's browser. The time in question is typically on the order of milliseconds, but every little bit helps when you're looking to give users the best possible experience!

    Script combining is beneficial because fewer JS files means fewer request/response operations by the browser - which translates directly into quicker page load times for users and less load on the web server. Furthermore, there will be less network traffic because the HTTP headers associated with each unnecessary request/response operation don't need to be transmitted (saving around 750 bytes for each combined script).

    At the extreme, one could combine all (~40) the Behaviors in the Toolkit into a single, monolithic JS file (either manually or as part of the build process) and always send that file to the browser. While there are certain benefits to this, we chose not to do so for two main reasons: 1) the ASP.NET AJAX framework the Toolkit builds upon is an object-oriented framework and it's beneficial to maintain the mental/physical separation that comes from keeping each behavior isolated and 2) any page that used any part of the Toolkit would be forced to download the entire set of scripts in the Toolkit.

    ToolkitScriptManager gives us the best of both worlds by combining exactly the relevant JS files used by each page into one file so the browser downloads only what's necessary for each page. ToolkitScriptManager's script combining is more than a simple concatenation of all the script files in the Toolkit; it's a dynamic merge of only the scripts that are actually being used by a page each time it's loaded by the browser. If a page has an ASP.NET AJAX UpdatePanel on it and additional scripts need to be sent as part of an async postback, then ToolkitScriptManager will automatically generate a combined script file containing only those scripts that the browser hasn't already downloaded.

    ToolkitScriptManager automatically compresses the combined script file if the browser indicates it supports compression - achieving slightly better compression in the process because most adaptive dictionary-based compression techniques (like HTTP's GZIP and Deflate) tend to compress data better in one chunk than in multiple chunks (because the dictionary doesn't keep getting reset). The combined script file is cached and reused by the browser just like the corresponding script files would have been if ToolkitScriptManager weren't being used.

    Basically, script combining effortlessly creates faster loading web pages - and happier users by extension!

    How do I use it?

    Simple: Just replace <asp:ScriptManager ... /> with <ajaxToolkit:ToolkitScriptManager ... /> in your ASPX page and you're done! (Of course, if you're not using the default namespaces "asp" and "ajaxToolkit", you'll need to substitute your own namespaces.) The scripts in the AJAX Control Toolkit are already enabled for combining, so it's really that easy!

    ToolkitScriptManager derives from ScriptManager, so it can be substituted trivially. Configuration-wise, it exposes a single new property beyond what ScriptManager already has: bool CombineScripts. The default value of "true" means that scripts will be automatically combined - specifying the value "false" disables the combining. (Alternatively, just switch back to ScriptManager for the same result.)

    How do I enable combining for my custom Behavior's scripts?

    As a security precaution to prevent malicious users from taking advantage of ToolkitScriptManager to access embedded resources from unrelated DLLs, the new assembly-level ScriptCombine attribute must be used to indicate that a particular assembly/script is allowed to take part in the script combining process. By default, none of the scripts in an assembly without the ScriptCombine attribute will take part in script combining. Adding the ScriptCombine attribute to an assembly indicates that all of its scripts can take part in script combining and ToolkitScriptManager will automatically include the relevant ones when generating a combined script file. For finer control over individual scripts in an assembly, the ScriptCombine attribute exposes an ExcludeScripts property and an IncludeScripts property - both are comma-delimited lists of individual script files. As stated, when neither property is specified, the default behavior is that all scripts are combinable. Once the IncludeScripts property is set, only the scripts explicitly specified by it are combinable. The ExcludeScripts property excludes any listed scripts from combining (whether or not IncludeScripts is set). Of course, most people will simply add the ScriptCombine attribute to their assembly and the default behavior does what they want. (As mentioned above, Toolkit scripts are already enabled for script combining.)

    How do scripts actually get combined? [Technical]

    Script combining is a two-stage process.

    The first stage takes place during the normal ASP.NET page lifecycle when ToolkitScriptManager overrides ScriptManager's OnLoad method to initialize its state and its OnResolveScriptReference method to find out when script references are being resolved. The initialization code in OnLoad consists of adding a HiddenField to the page and using it to track which scripts have already been loaded by the browser. The handling of OnResolveScriptReferences is a little more involved: the script reference is checked for combinability (i.e., does the assembly's ScriptCombine attribute allow the script to take part in script combining) and the script reference is changed to point to the URL of a combined script file. In this manner, all scripts that are part of the same combined script file get the same URL and the ScriptManager class outputs that URL to the page exactly once. Notably, because scripts may have a strict ordering, the presence of an uncombinable script in the middle of combinable scripts will result in two combined script files being generated (the first consisting of the scripts coming before the uncombinable script and the second consisting of the scripts after it).

    The URL of the combined script file is currently of the form: .../Page.aspx?_scriptcombiner_=;Assembly1.dll Version=1:MVID1:Script.Name.1.js:Script.Name.2.js;Assembly2.dll Version=2:MVID1:Script.Name.3.js. What this means is that the ASPX page itself is referenced with the special request parameter "_scriptcombiner_" and a semicolon-delimited list of assemblies with a colon-delimited list of the required scripts from each of them. The strong name of the assembly is used to avoid potential confusion if multiple versions of an assembly are present and the ModuleVersionID (MVID) is used to ensure that any changes to the assembly itself automatically invalidate all combined script files that reference it. In this manner, recompiling one of the assemblies contributing to a combined script file will cause the new scripts to be downloaded by the browser next time the page is loaded.

    The second stage of script combining takes place when the page is referenced with the "_scriptcombiner_" request parameter. ToolkitScriptManager overrides OnInit (one of the first parts of the page lifecycle) and uses that opportunity to generate the combined script file based on the value of the request parameter, outputs the combined script file to the browser, and stops further processing of the page lifecycle. Of note, the cache settings of the combined script file are set to the same values that the individual script files would have had if ToolkitScriptManager weren't being used and the combined script file is automatically compressed according to the browser's wishes. Similarly, any localized script resources for a script file that is in the process of being combined are loaded and sent to the browser as part of the combined script file. After all combined scripts are output, ToolkitScriptManager appends a small bit of script to the end of the file to update the page's HiddenField with the scripts that have just been added. In this manner, any additional scripts added during an async postback are automatically tracked by the page and subsequent async postbacks will know exactly which scripts have already been loaded by the browser.

    Why piggyback a request parameter on the same page instead of using an IHttpHandler? [Technical]

    ScriptManager uses ScriptResourceHandler (an IHttpHandler) to serve (uncombined) scripts, so it's natural to wonder why ToolkitScriptManager wouldn't do the same. The reason is that the AjaxControlToolkit DLL is often run in partial trust scenarios where it couldn't add such a handler to the system itself - and because we don't want people to have to modify their web.config file just to enable script combining. By making use of the same page for serving combined script files, ToolkitScriptManager offers a seamless experience that's simple to configure, simple to manage, and that works even for folks who don't have control over the web server that's hosting their content.

    Are there any tradeoffs when switching to ToolkitScriptManager?

    There are no significant tradeoffs that we know of, but there are a couple of implications it's good to be aware of. For one, the current combined script URL format is currently pretty verbose and can lead to unusually long URLs. While this hasn't been a problem so far, it will be easy to change the format in the future (with no impact to users) and we're already considering ways of doing so. Another thing to be aware of is that reusing the page to serve the combined script file means that there is some additional server processing that happens before/during the OnInit stage of the page lifecycle when processing a combined script file. (Though the additional work here is offset by the savings of not having to serve multiple JS files.) Again, this hasn't been an issue, but it's something to keep in mind if things behave differently after adding ToolkitScriptManager to a page.

    So just how risky is it to switch to ToolkitScriptManager?

    That's a loaded question [ :) ], but it's informative to note that all AJAX Control Toolkit sample pages (including the sample web site, automated tests, manual tests, etc.) have been converted over to use ToolkitScriptManager with only one issue: The Slider's SliderBehavior.js script uses a fairly obscure feature enabled by the PerformSubstitution property of the WebResource attribute that allows <%= WebResource/ScriptResource %> tags to be embedded in JS files and get resolved before the script is sent to the browser. This behavior isn't currently supported by ToolkitScriptManager (it will throw an informative Exception if it detects the presence of this construct), so the ExcludeScripts property of the ScriptCombine attribute on the AjaxControlToolkit DLL has been used to exclude the SliderBehavior.js file from being combined.

    Summary

    ToolkitScriptManager works seamlessly in every page of the AJAX Control Toolkit, so we encourage folks to give it a try if they're interested in the benefits it offers! As always, if you encounter any problems, please let us know by posting a detailed description of the problem to the AJAX Control Toolkit support forum.

    Happy script combining!!

  • Delay's Blog

    No trees were harmed in the making of this blog post [How to: Successfully print a Chart with the Silverlight 4 Beta]

    • 22 Comments

    One of the big requests people have had for Silverlight was the ability to print, so one thing that's new in the Silverlight 4 Beta is the PrintDocument class which enables applications to print anything they want! There isn't an excess of fancy bells and whistles quite yet, but the support Silverlight provides is enough to do just about everything - it's just that sometimes you might need to do a little more than you expect. :) It's the classic "crawl, walk, run" approach to feature delivery, and this is a great first step that will be a welcome addition for everyone who's been wanting to print with Silverlight.

    Okay, so that's all well and good and people can print any content they want and it all looks beautiful and works perfectly, right? Pretty much, yes! Well, except for certain scenarios involving charts from the Silverlight/WPF Data Visualization assembly. :( To be clear, if you try to print a Chart that's already in the visual tree, it works just fine and what you see really is what you get. But if you've tried to create custom content specifically for printing (for example: a different layout, maybe some page headers and footers, etc.) and that content included a Chart instance, you probably noticed a minor issue: none of the data points showed up on the page. Darn, it sure is hard to tell what's going on without those data points...

     

    Fortunately, a bit of mental debugging reveals what's going on here. Charting's data points fade in by default - from Opacity 0 to 1 over the course of 0.5 seconds via a Visual State Manager transition. That's cool and it works great in the visual tree - however, when you're creating print-specific visuals and immediately printing them, those data points don't have a chance to do much fading in... So they're actually all there on the printed page, but they're at Opacity 0, so you can't see them!

    What to do? Well, the most convenient thing would be for the Chart code to somehow know it was going to be printed and SkipToFill those pesky animations. But there's no official way for a control to know it's being created for printing. There are a few tricks the Chart could use to make an educated guess, but it doesn't do so yet - and I'm not sure that's the right answer anyway. So the next most obvious thing would be to re-Template the DataPoints to replace the default fading Template with one that doesn't fade, but instead starts fully visible. And as you'd expect, this works just fine - the data points show up and look great!

    However, that's a lot of work that you probably don't want to have to do. It would be nice if there were a middle ground - some technique that was maybe a little bit of a hack, but that was really easy to implement and worked just about all the time...

     

    And the good news is that there is such a hack! The idea builds upon the observation that printing a Chart from the visual tree works correctly - so let's start by popping our new, print-ready chart into the visual tree to give it a chance to get all pretty, then print it once that's done. That seems easy enough, let's try it:

    // Create customized content for printing
    var chart = new Chart { Title = "My Printed Chart" };
    var series = new LineSeries
    {
        ItemsSource = (PointCollection)Resources["Items"],
        DependentValuePath = "Y",
        IndependentValuePath = "X",
    };
    chart.Series.Add(series);
    var content = new Border
    {
        BorderBrush = new SolidColorBrush(Colors.Magenta),
        BorderThickness = new Thickness(10),
        Child = chart,
    };
    
    // Create container for putting content in the visual tree invisibly
    var visualTreeContainer = new Grid
    {
        Width = 1000,
        Height = 1000,
        Opacity = 0,
    };
    
    // Put content in the visual tree to initialize
    visualTreeContainer.Children.Add(content);
    LayoutRoot.Children.Add(visualTreeContainer);
    
    // Prepare to print
    var printDocument = new PrintDocument();
    printDocument.PrintPage += delegate(object obj, PrintPageEventArgs args)
    {
        // Unparent the content for adding to PageVisual
        visualTreeContainer.Children.Remove(content);
        args.PageVisual = content;
    };
    
    // Print the content
    printDocument.Print();
    
    // Remove container from the visual tree
    LayoutRoot.Children.Remove(visualTreeContainer);

    Note that the sample application is creating a completely new Chart instance here (with a print-specific title and custom magenta frame) - and that the printed output looks just like it should:

    ChartPrinting sample output

     

    [Click here to download the source code for the ChartPrinting sample for the Silverlight 4 Beta as a Visual Studio 2010 solution]

     

    The Chart is added to the visual tree before the print dialog is shown - so it has plenty of time for those 0.5 second animations to play to completion while the user interacts with the dialog. The only sketchy bit is that an exceedingly fast user could possibly manage to print a Chart before the animations had run all the way to completion. Yes, well, I said it was a hack up front, didn't I? :) If you want rock-solid printing behavior, you probably ought to be re-templating the data points in order to ensure the initial visuals are exactly as you want them. But if you're just interested in printing something attractive with a minimum of fuss or inconvenience - and your users aren't hopped up on performance enhancing drugs - you'll probably be quite successful with this technique instead.

     

    PS - The XPS Printer driver is a fantastic tool for testing printing scenarios like this one. By printing to a file and opening that file in the XPS Viewer application that comes with Windows, I was able to avoid wasting a single sheet of paper during the course of this investigation. Highly recommended!

  • Delay's Blog

    Nobody likes a show-off [Today's DataVisualizationDemos release includes new demos showing off stacked series behavior]

    • 21 Comments

    My DataVisualizationDemos application is a collection of nearly all the Data Visualization samples I've posted to my blog. And just like the Data Visualization assembly (get Silverlight/WPF Data Visualization Development Release 4 here!), the demo application runs on Silverlight 3, Silverlight 4, WPF 3.5, and WPF 4 and shares the same code and XAML across all platforms. It's a handy way to look at bunch of sample code running on any of the supported platforms. What's more, each demo page has hyperlinks to relevant blog post(s) for more detail about each scenario, so it's a good learning tool as well! :)

     

    Click here to download the complete source code for the cross-platform DataVisualizationDemos sample application.

     

    Stacked Series demo

    Of the two completely new demos, the prettiest is definitely the "Stacked Series" sample shown above. Its interface is simple - you choose one to three sets of stacked data for examples of all four stacking types and can control the nature of that data: positive/mixed/negative small/large ints/doubles. As you might guess, the corresponding value changes are fully animated, so it's pleasing to watch the charts flow from one configuration to another.

    There's also a check-box for switching the examples from "normal" stacked mode into "100%" stacked mode. This transition is not animated because what goes on behind the scenes is a swap of the series types in use. Similarly, choosing a different kind of independent value from the combo-box (string, consecutive numbers, random numbers, or dates) re-creates the data and is not animated.

    Disclaimer: As soon as you start playing around with negative values, you'll think you've found a bad rendering bug. But before you fire off an email telling me what a bone-head I am for getting things wrong, please finish reading this post... :)

     

    Text-To-Chart demo

    The less attractive - but arguably more interesting - of the two new demos is the "Text-To-Chart" sample shown above. What it lacks in sophisticated UI polish, it makes up for in flexibility. The idea is that you (or me when I'm testing things!) can paste simple tabular data directly from Excel and see how it looks in any of the eight new stacked series types. The dependent values of a single series go down and different series go across (using tab, space, or ';' as a delimiter). It's flexible, so you can have any number of dependent values or series! Independent values are generated automatically according to row number; the only requirement is that every row needs to have the same number of values (i.e., no missing data). If everything checks out, you get a pretty chart of the data - if not, the text turns red to indicate a problem that needs to be corrected.

    After a little while playing around with negative numbers, you might end up with the chart shown below;

    Stacked area series with negative values - TextToChart

    Clearly, this is a rendering bug - that can't possibly be correct! Let's try it in Excel to see how it's supposed to look so we can tell the developer what a complete bone-head he is:

    Stacked area series with negative values - Excel

    Oh, snap, it's not a bug after all! :) Whereas the behavior of stacked bar and column charts is fairly intuitively obvious in the presence of negative values, the behavior of stacked line and area charts is not. But it turns out that Excel implements a fairly simple algorithm here - and once I stared at things long enough to figure out what it was, my stacked series charts suddenly matched Excel in all the weird scenarios I've tried so far.

    Of course, that's not to say there aren't still bugs in the stacked series implementation - there are bound to be because everything has bugs. But the good news is that I may not be a total bone-head after all. That said, if you run into a scenario where the output from the stacked series code differs from that of Excel, I'd love to hear about it - and "Text-To-Chart" provides a really easy way of sending a bug report! :)

     

    Performance Tweaks with a Compatible series

    The last thing I wanted to call out is that I've updated the "Performance Tweaks" sample (above) to allow the creation of a "Compatible" ScatterSeries (shown above) in addition to the normal kind. You can read more about what I mean in my detailed discussion of the new stacked series support. What's important to know is that holding down the Ctrl key switches the "Add Series" button to an "Add Compatible Series" button - and the resulting series name in the legend will be "Standard" or "Compatible" accordingly.

    This makes it easy to see some of the performance improvements I discuss in that blog post for yourself. One good example is the following: Reset, Create Chart, Add Series, Populate, [watch as the points fade in], Change Values, [watch as they move], Change Values, etc.. Now repeat those same steps with a Compatible series and note how everything runs faster!

    Aside: If you're on WPF, you may need to increase the number of points before you start seeing a difference.

     

    And there you have it: cool new samples to go with the new Data Visualization features. The DataVisualizationDemos application is one of my favorite test cases because it implements a bunch of different scenarios with a variety of different techniques and it runs everywhere the Data Visualization assembly does. (Or at least it did until Windows Phone 7 showed up... Hum, maybe I'll port to that platform next time...)

    DataVisualizationDemos has been a tremendous help for me and I hope you enjoy it, too!

  • Delay's Blog

    "Your feedback is important to us; please stay on the line..." [Improving Windows Phone 7 application performance is even easier with these LowProfileImageLoader and DeferredLoadListBox updates]

    • 21 Comments

    A few months ago I began a similar post about LowProfileImageLoader/DeferredLoadListBox updates by saying:

    Windows Phone 7 applications run on hardware that's considerably less powerful than what drives typical desktop and laptop machines. Therefore, tuning phone applications for optimum performance is an important task - and a challenging one! To help other developers, I previously coded and blogged about two classes: LowProfileImageLoader (pushes much of the cost of loading images off the UI thread) and DeferredLoadListBox (improves the scrolling experience for long lists). These two classes can be used individually or together and have become a regular part of the recommendations for developers experiencing performance issues.

     

    In the time since, I've continued to hear from people who are benefitting from LowProfileImageLoader and DeferredLoadListBox - and the code has even been incorporated into the WP7Contrib project! Along the way, I've also collected some great feedback, so I recently dedicated time to make a few improvements:

    PhonePerformance List Scrolling sample
    • The most significant change is that I've removed the use of the UIElement.TransformToVisual platform-level method from DeferredLoadListBox because it has proven to be unreliable on Windows Phone 7 by throwing exceptions unexpectedly. Because this is not the first time I've had to fix crashes due to random ArgumentExceptions ("The parameter is incorrect."), I recommend not using the TransformToVisual method in Windows Phone 7 applications until/unless the underlying problem is fixed. In the meantime, it has been my experience that the LayoutInformation.GetLayoutSlot method can often be used as a substitute with just a little bit of extra effort.

      I'd like to thank Tore Lervik, Baldelli Gabriele, and Holger Schmeken for reporting this problem.

      Aside: Another time I had to remove TransformToVisual was for the Silverlight for Windows Phone Toolkit's ContextMenu control. (This fix was part of the November 2010 release).
    • I've previously explained why DeferredLoadListBox requires every container to have a height (note: each height can be different!). However, there are some scenarios where the Windows Phone 7 platform will report ActualHeight to be 0 for a container even though its height has been explicitly and correctly set (ex: via ItemContainerStyle). (Note: This seems to occur most often during scrolling.) Fortunately, I found an easy workaround that appears to resolve this problem in cases where the platform is misbehaving: a call to the UpdateLayout API is sufficient to correct the value of ActualHeight.

      I'd like to thank Rich Griffin and Michael James for reporting this problem.

    • LowProfileImageLoader originally used a Queue to implement "first in, first out" (FIFO) behavior of the image downloads it performs. This is a "fair" implementation and is ideal for slowly scrolling up/down a list that uses LowProfileImageLoader and DeferredLoadListBox together. However, for the scenario of quickly scrolling such a list in a single direction, FIFO behavior means the images you see on the screen will be among the last to load. The "obvious" fix is to switch from a Queue to a Stack which gives "last in, first out (LIFO) behavior instead. But while that's better for the second scenario, it's worse for the first one - and it leads to a weird visual effect in apps like my ImageLoading sample (part of the download) because the "wall" of images loads bottom-to-top instead of "top-to-bottom" as people expect.

      Clearly, there's no perfect answer here, so the solution is to do well on average! The classic way of amortizing unpredictable cost is to introduce randomness (ex: the QuickSort algorithm) - so instead of processing FIFO or LIFO, LowProfileImageLoader now works through its queue of pending work in random order. As a result, both the fast and the slow scrolling scenarios show images quickly and the application appears more responsive overall!

      Aside: The way I've implemented randomization is a slight variation of the solution to a classic programming puzzle: How do you sort a deck of N cards in linear time and constant space? If you haven't seen this one before, take a minute to think about it before following this link to a description of the Fisher-Yates/Knuth shuffle.
    • Though I initially meant for LowProfileImageLoader and DeferredLoadListBox to be used together, there's no reason LowProfileImageLoader can't be used on its own. In fact, I previously ensured that it works fine when used with the default ListBox/VirtualizingStackPanel combination. However, when the user is scrolling such a list very quickly, the default container recycling behavior means there will be multiple data bindings applied to a particular container in rapid succession. Every one of these will enqueue a request for LowProfileImageLoader to download the corresponding image - but only the most recent one matters. Any previous requests are "stale" and although it's safe to satisfy them, it's also unnecessary. Therefore, I've made a change with this update to detect stale requests and discard them before making an expensive web request on their behalf. This difference doesn't matter in non-virtualizing scenarios, but for virtualizing scenarios the amount of unnecessary work it saves can quickly add up!

    • Another consequence of using LowProfileImageLoader in the presence of container recycling is that re-used Image elements kept their old content until new content had been downloaded. This could lead to temporarily misleading UI where images show up alongside content they aren't associated with. It happens because LowProfileImageLoader didn't previously "null-out" the Source property when a new request was made. I've modified the code so it does now - and the virtualizing experience is nicer because of it.

    • When implementing the worker thread logic for LowProfileImageLoader, I intended for it to process WorkItemQuantum number of items each time through the loop until the queue of requests was exhausted. I wrote the following code:

      for (var i = 0; (i < pendingRequests.Count) && (i < WorkItemQuantum); i++)

      I'd like to thank Ashish Gupta for pointing out a bug here; what I meant was:

      for (var i = 0; (0 < pendingRequests.Count) && (i < WorkItemQuantum); i++)

      Coding errors in loops can cause serious problems if they result in an attempt to process too many or too few items. I got lucky here because there's no functional bug due to the original typo - the only downside is that performance might be a little worse because it takes a couple of extra passes through the loop to complete once the count drops below WorkItemQuantum. Fortunately, the value of WorkItemQuantum is only 5, so the real-world impact of this is minimal. However, the whole point of this code is to help improve performance, so I've fixed the oversight. :)

    • And finally, because I recently became a NuGet publishing "expert", I've created a package for the PhonePerformance assembly to make it easy to reference for all the NuGet fans out there. It contains the same binary you'd download below, but it contains only the assembly (and its XML IntelliSense file) - the three sample projects are available only with the ZIP download. This split seems like a reasonable compromise to me: reference from the NuGet gallery if you know what you're doing and just need to add the binary to your project - or - read the relevant blog posts and download the samples if you're getting started.

     

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

    -OR-

    [Click here to visit the NuGet gallery page for a package containing the PhonePerformance assembly.]

     

    Windows Phone 7 developers must pay attention to performance because otherwise it's easy to end up with a slow, badly-behaved application. The PhonePerformance assembly focuses on two common scenarios (image loading and list scrolling) and attempts to improve upon the default experience by making it easy to avoid known problem areas. As with any performance technique, results can vary greatly depending on the specifics of each scenario, so it's important to take measurements and test everything on real phone hardware.

    Many developers have told me they had success with the PhonePerformance assembly - I hope you do, too! :)

  • Delay's Blog

    Announcing a free, open source Charting solution for Silverlight [Silverlight Toolkit released today at PDC!]

    • 21 Comments

    Today at the Microsoft Professional Developers Conference, we announced the immediate availability of the Silverlight Toolkit. The Silverlight Toolkit is a collection of controls for Silverlight that add new functionality and enable new scenarios. There are some great controls in the Toolkit: WPF favorites (ex: DockPanel, WrapPanel, TreeView), cool new things (ex: AutoCompleteBox, NumericUpDown), and some that are a little of both (ex: ImplicitStyleManager). All of these controls are worth looking at and blogging about - but that's not what I'm going to do. :)

    Instead, I'd like to direct your attention to the Microsoft.Windows.Controls.DataVisualization.dll assembly which contains a set of completely new classes that enable Silverlight developers to easily create professional-looking column, bar, pie, line, and scatter charts that follow the same XAML developer/designer metaphor as the rest of the Silverlight platform. We've tried to make Silverlight Charting as easy as possible to use - while also enabling some very powerful scenarios like automatic support for dynamic changes to a bound data collection as well as a powerful extensibility model that allows people to write their own custom chart types with minimal difficulty.

    Here's all it takes to create a simple column chart in XAML (note that there's no code required):

    <charting:Chart Title="My First Chart">
        <charting:Chart.Series>
            <charting:ColumnSeries>
                <charting:ColumnSeries.ItemsSource>
                    <controls:ObjectCollection>
                        <sys:Double>1</sys:Double>
                        <sys:Double>2</sys:Double>
                        <sys:Double>3</sys:Double>
                    </controls:ObjectCollection>
                </charting:ColumnSeries.ItemsSource>
            </charting:ColumnSeries>
        </charting:Chart.Series>
    </charting:Chart>
    
    Sample Chart

    Looking at the structure of that XAML, we start with a Chart control and set its Title. Then we add an instance of the ColumnSeries class to the Chart's Series collection and provide a simple collection of doubles as the ItemsSource of the series (think ItemsControl.ItemsSource). Silverlight Charting does all the rest and renders the chart you see above.

    Being able to display static XAML-only data like this is great for learning and experimenting, but more advanced scenarios will usually be working with pre-existing business objects. So let's create a pie chart to display some fictitious statistics about a project's source code. Here's the business object we'll be working with:

    public class CodeElement : INotifyPropertyChanged
    {
        public string Name { get; set; }
    
        public int Lines
        {
            get { return _lines; }
            set
            {
                _lines = value;
                var handler = PropertyChanged;
                if (null != handler)
                {
                    handler.Invoke(this, new PropertyChangedEventArgs("Lines"));
                }
            }
        }
        private int _lines;
    
        public event PropertyChangedEventHandler PropertyChanged;
    }
    

    And here's a collection of them that we'll use as the source of the data for the chart:

    public class CodeElementCollection : ObservableCollection<CodeElement>
    {
        public CodeElementCollection()
        {
            Add(new CodeElement { Name = "Code", Lines = 400 });
            Add(new CodeElement { Name = "Comments", Lines = 200 });
            Add(new CodeElement { Name = "Whitespace", Lines = 100 });
        }
    }
    

    All we need to do is create a CodeElementCollection (I'll put it in the Resources section for ease of access) and use it with our chart:

    <charting:Chart Title="Source Code Statistics">
        <charting:Chart.Series>
            <charting:PieSeries
                ItemsSource="{StaticResource CodeElementCollection}"
                DependentValueBinding="{Binding Lines}"
                IndependentValueBinding="{Binding Name}"/>
        </charting:Chart.Series>
    </charting:Chart>
    
    Sample Chart

    This time the ItemsSource property is being set to a collection of objects. (Note: We could have set the ItemsSource property in code instead.) Because these business objects have multiple properties, the series needs to know which of those properties correspond to the independent and dependent values. So we set the DependentValueBinding and IndependentValueBinding properties of PieSeries to Bindings that identify the relevant business object properties. The chart automatically extracts the data values, creates the properly-named legend items, and renders a pie chart. It's as easy as, uh, pie! :)

    Now let's say someone adds a new XAML file to our imaginary project - we want our chart to automatically update itself to show the new code type. And because we've used an ObservableCollection for our collection, it will! In the sample project for this post (you can download it from the attachment link at the bottom of the post), there's a button to add a new CodeElement to the collection; the implementation looks like this:

    private void AddXamlStatistics_Click(object sender, RoutedEventArgs e)
    {
        _codeElementCollection.Add(new CodeElement { Name = "XAML", Lines = 100 });
    }
    

    Pressing that button gives us the following chart:

    Sample Chart

    You can't tell from the still images here, but the new pie slice displays with a very smooth animation that's typical of Charting's support for dynamic data.

    My code for the click handler doesn't stop the user from hitting that button a bunch of times; doing so just for fun gives us the following chart which demonstrates how Charting's Legend control automatically scrolls to accommodate large lists and also demonstrates the complete palette of colors that Charting supports by default:

    Sample Chart

    Naturally, folks might want to customize the default palette - and it's easy to do so with the StylePalette property on Chart. Here, I've switched the palette to simple, monochromatic red, green, and blue:

    <charting:Chart Title="Statistics (Custom Palette)">
        <charting:Chart.StylePalette>
            <datavis:StylePalette>
                <Style TargetType="Control">
                    <Setter Property="Background" Value="Blue"/>
                </Style>
                <Style TargetType="Control">
                    <Setter Property="Background" Value="Green"/>
                </Style>
                <Style TargetType="Control">
                    <Setter Property="Background" Value="Red"/>
                </Style>
            </datavis:StylePalette>
        </charting:Chart.StylePalette>
        <charting:Chart.Series>
            <charting:PieSeries
                ItemsSource="{StaticResource CodeElementCollection}"
                DependentValueBinding="{Binding Lines}"
                IndependentValueBinding="{Binding Name}"/>
        </charting:Chart.Series>
    </charting:Chart>
    
    Sample Chart

    A designer could, of course, make the custom Background brushes much nicer by adding a gradient, an overlay, etc.. The StylePalette is simply the place the Chart goes to get the next Style it needs when it's creating a new DataPoint instance (DataPoint = the visual representation of a column or pie slice) for a Series. The custom palette above uses TargetType="Control" to indicate that each Style is applicable to all the different data point types (ColumnDataPoint, PieDataPoint, etc.), but it's also possible to provide a specific TargetType to customize the data point types individually.

    Getting back to the topic of dynamic data, suppose that instead of a new CodeElement instance being added to the collection, the value of one of the existing CodeElements changes as a result of an edit to an existing file of our imaginary project's source code. Again, we want our chart to automatically update - and because our CodeElement business object implements the INotifyPropertyChanged interface, it will! The sample project has a button to increase the number of comment lines by 50 each time it's clicked:

    private void ChangeCommentStatistics_Click(object sender, RoutedEventArgs e)
    {
        CodeElement commentCodeElement = _codeElementCollection.Where(element => element.Name == "Comments").First();
        commentCodeElement.Lines += 50;
    }
    

    After clicking this button a few times, the chart has smoothly animated itself to the following state (there's no XAML entry because I reloaded the sample before doing this):

    Sample Chart

    Note that in both of the dynamic data scenarios so far, the code that updates the business data doesn't ever need to know that a chart is actively presenting that data. All the code needs to concern itself with is changing the business objects - and the chart automatically handles the rest. It doesn't get much easier than that! :)

    Of course, there's lots more to Charting than I've shown off here: drill-down support, reveal/hide animations, customizability, and more. And much of this can only be fully appreciated in a live demonstration - so please have a look at the live sample page for Charting for lots more examples. And remember that the complete source code to Charting, the samples page, and the automated tests is all freely available under the Microsoft Public License (Ms-PL). Believe me when I say we've tried to make it as easy as possible to get started with Charting!

    And to make things even easier, I'll be posting the following application (with full source code) to my blog in a day or so. It's called ChartBuilder and is an interactive Charting sample/learning tool/test bed all rolled up in one happy bundle of joy. I hope you'll find it useful:

    ChartBuilder

    And that's Charting in a nutshell! I expect there will be lots more written about Charting in the coming weeks. Those of us on the Charting team have done our best to come up with an API and an implementation that everyone will love, but a big part of that process is YOU. We're eager to hear your feedback as you start playing around with Charting in your own projects. This release of Silverlight Charting is what's known as a "preview release", so nothing is set in stone and we're completely open to improvements anywhere. We'll do our best to minimize breaking changes, but if there are places where things are just plain broken, we'll do what we can to fix them! If you have any 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.

    Thank you for taking the time to learn about Silverlight Charting - it's my sincere hope that Charting helps Silverlight application developers deliver even more compelling applications with simplicity and ease!

     

    Aside: The development of Silverlight Charting has been a very interesting experience to be a part of. Throughout the entire process, we've been *extremely* resource-constrained (you wouldn't believe me if I told you), but when all is said and done, I'm really pleased with what we've accomplished!

    Further aside: I began leading the Charting effort a few months ago when my manager asked me to "think about developing a Charting story for Silverlight". I soon partnered with a few people on the SQL Data Visualization team and began working on the foundations of what you see today. We spent a lot of time trying to design a good, easy to use API for Charting that also made sense in the Silverlight world of templates, styling, etc.. (There are a lot of challenges here and I may blog more about these challenges in the future.) Near the end of August, Jafar Husain joined our team and the Charting effort. He's been responsible for large parts of the internal Charting infrastructure and there are examples of his influence throughout our final object model. He's going to be blogging about some of the internal details and decision-making process, so please subscribe to his blog if you want the gory details of how this stuff actually fits together.

  • Delay's Blog

    One more platform difference more-or-less tamed [SetterValueBindingHelper makes Silverlight Setters better!]

    • 21 Comments

    Earlier this week I wrote about the "app building" exercise my team conducted and posted my sample application, a simple organizational hierarchy viewer using many of the controls in the Silverlight Toolkit. One of the surprises I had during the process of building this application was that Silverlight (version 2 as well as the Beta for version 3) doesn't support the scenario of providing a Binding in the Value of a Setter. I bumped into this when I was trying to follow one of the "best practices" for TreeView manipulation - but I soon realized the problem has much broader reach.

    First, a bit about why this is interesting at all. :) Because of the way TreeView works on WPF and Silverlight, it turns out that the most elegant way of manipulating the nodes (for example, to expand all the nodes in a tree) is to do so by manipulating your own classes to which the TreeViewItem's IsExpanded property is bound. Josh Smith does a great job explaining why in this article, so I won't spend more time on that here. However, as Bea Stollnitz explains near the bottom of this post (and as I mention above), the XAML-based Setter/Value/Binding approach doesn't work on Silverlight.

    In her post, Beatriz outlines a very reasonable workaround for this problem which is to subclass TreeView and TreeViewItem in order to override GetContainerForItemOverride and hook up the desired Bindings there. However, there are two drawbacks with this approach that I hoped to be able to improve upon: 1. It's limited to ItemsControl subclasses (because other controls don't have GetContainerForItemOverride) and 2. it moves design concerns into code (where designers can't see or change them).

    As part of my app building work, I came up with a one-off way of avoiding the ItemsControl coupling, but it wasn't broadly useful in its original form. Fortunately, in the process of extracting that code out in generalizing it for this post, I realized how I could avoid the second drawback as well and accomplish the goal without needing any code at all - it's all XAML, all the time! [Yes, designers, I [heart] you. :) ]

    The trick is to use a Setter to set a special attached DependencyProperty with a Value that specifies a special object which identifies the DependencyProperty and Binding to set. It's that easy! Well, okay, I have to do a bit of work behind the scenes to make this all hang together, but it does work - and what's more it even works for attached properties!

     

    Here's an example of SetterValueBindingHelper in action:

    SetterValueBindingHelperDemo sample

    First, the relevant XAML for the TreeViewItem:

    <Style TargetType="controls:TreeViewItem">
        <!-- WPF syntax:
        <Setter Property="IsExpanded" Value="{Binding IsExpanded}"/> -->
        <Setter Property="local:SetterValueBindingHelper.PropertyBinding">
            <Setter.Value>
                <local:SetterValueBindingHelper
                    Property="IsExpanded"
                    Binding="{Binding IsExpanded}"/>
            </Setter.Value>
        </Setter>
    </Style>
    

    Yes, things end up being a little bit more verbose than they are on WPF, but if you squint hard enough the syntax is quite similar. Even better, it's something that someone who hasn't read this post should be able to figure out on their own fairly easily.

    Here's the XAML for the top Button:

    <Style TargetType="Button">
        <!-- WPF syntax:
        <Setter Property="Content" Value="{Binding}"/> -->
        <Setter Property="local:SetterValueBindingHelper.PropertyBinding">
            <Setter.Value>
                <local:SetterValueBindingHelper
                    Property="Content"
                    Binding="{Binding}"/>
            </Setter.Value>
        </Setter>
    </Style>
    

    There's really nothing new here, but I did want to show off that SetterValueBindingHelper works for non-ItemsControls as well.

    Finally, here's the XAML for the right Button where two properties are being set by SetterValueBindingHelper:

    <Style TargetType="Button">
        <!-- WPF syntax:
        <Setter Property="Grid.Column" Value="{Binding}"/>
        <Setter Property="Grid.Row" Value="{Binding}"/> -->
        <Setter Property="local:SetterValueBindingHelper.PropertyBinding">
            <Setter.Value>
                <local:SetterValueBindingHelper
                    Type="System.Windows.Controls.Grid, System.Windows, Version=2.0.5.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e"
                    Property="Column"
                    Binding="{Binding}"/>
            </Setter.Value>
        </Setter>
        <Setter Property="local:SetterValueBindingHelper.PropertyBinding">
            <Setter.Value>
                <local:SetterValueBindingHelper
                    Type="Grid"
                    Property="Row"
                    Binding="{Binding}"/>
            </Setter.Value>
        </Setter>
    </Style>
    

    As you can see, SetterValueBindingHelper also supports setting attached DependencyPropertys, so if you find yourself in a situation where you need to style such a creature, SetterValueBindingHelper's got your back. It's also worth pointing out that the Setter for "Column" is using the official assembly-qualified naming for the Type parameter of the SetterValueBindingHelper object. This form is completely unambiguous - and it's also big, ugly, and pretty much impossible to type from memory... :) So I added code to SetterValueBindingHelper that allows users to also specify the full name of a type (ex: System.Windows.Controls.Grid) or just its short name (ex: Grid, used by the Setter for "Row"). I expect pretty much everyone will use the short name - but sleep soundly knowing you can fall back on the other forms "just in case".

     

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

     

    Lastly, here's the code for SetterValueBindingHelper in case that's all you care about:

    Update (2010-10-31): At the suggestion of a reader, I've removed the implementation of SetterValueBindingHelper from this post because a newer version of the code (that works well on Silverlight 4, too) can be found in this more recent post. (FYI: The download link is the same for both posts and therefore always up-to-date.)

    /// <summary>
    /// Class that implements a workaround for a Silverlight XAML parser
    /// limitation that prevents the following syntax from working:
    ///    &lt;Setter Property="IsSelected" Value="{Binding IsSelected}"/&gt;
    /// </summary>
    public class SetterValueBindingHelper
    {
        /// <summary>
        /// Optional type parameter used to specify the type of an attached
        /// DependencyProperty as an assembly-qualified name, full name, or
        /// short name.
        /// </summary>
        [SuppressMessage("Microsoft.Naming", "CA1721:PropertyNamesShouldNotMatchGetMethods",
            Justification = "Unambiguous in XAML.")]
        public string Type { get; set; }
    
        /// <summary>
        /// Property name for the normal/attached DependencyProperty on which
        /// to set the Binding.
        /// </summary>
        public string Property { get; set; }
    
        /// <summary>
        /// Binding to set on the specified property.
        /// </summary>
        public Binding Binding { get; set; }
    
        /// <summary>
        /// Gets the value of the PropertyBinding attached DependencyProperty.
        /// </summary>
        /// <param name="element">Element for which to get the property.</param>
        /// <returns>Value of PropertyBinding attached DependencyProperty.</returns>
        [SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters",
            Justification = "SetBinding is only available on FrameworkElement.")]
        public static SetterValueBindingHelper GetPropertyBinding(FrameworkElement element)
        {
            if (null == element)
            {
                throw new ArgumentNullException("element");
            }
            return (SetterValueBindingHelper)element.GetValue(PropertyBindingProperty);
        }
    
        /// <summary>
        /// Sets the value of the PropertyBinding attached DependencyProperty.
        /// </summary>
        /// <param name="element">Element on which to set the property.</param>
        /// <param name="value">Value forPropertyBinding attached DependencyProperty.</param>
        [SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters",
            Justification = "SetBinding is only available on FrameworkElement.")]
        public static void SetPropertyBinding(FrameworkElement element, SetterValueBindingHelper value)
        {
            if (null == element)
            {
                throw new ArgumentNullException("element");
            }
            element.SetValue(PropertyBindingProperty, value);
        }
    
        /// <summary>
        /// PropertyBinding attached DependencyProperty.
        /// </summary>
        public static readonly DependencyProperty PropertyBindingProperty =
            DependencyProperty.RegisterAttached(
                "PropertyBinding",
                typeof(SetterValueBindingHelper),
                typeof(SetterValueBindingHelper),
                new PropertyMetadata(null, OnPropertyBindingPropertyChanged));
    
        /// <summary>
        /// Change handler for the PropertyBinding attached DependencyProperty.
        /// </summary>
        /// <param name="d">Object on which the property was changed.</param>
        /// <param name="e">Property change arguments.</param>
        private static void OnPropertyBindingPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            // Get/validate parameters
            var element = (FrameworkElement)d;
            var item = (SetterValueBindingHelper)(e.NewValue);
            if ((null == item.Property) || (null == item.Binding))
            {
                throw new ArgumentException(
                    "SetterValueBindingHelper's Property and Binding must both be set to non-null values.");
            }
    
            // Get the type on which to set the Binding
            Type type = null;
            if (null == item.Type)
            {
                // No type specified; setting for the specified element
                type = element.GetType();
            }
            else
            {
                // Try to get the type from the type system
                type = System.Type.GetType(item.Type);
                if (null == type)
                {
                    // Search for the type in the list of assemblies
                    foreach (var assembly in AssembliesToSearch)
                    {
                        // Match on short or full name
                        type = assembly.GetTypes()
                            .Where(t => (t.FullName == item.Type) || (t.Name == item.Type))
                            .FirstOrDefault();
                        if (null != type)
                        {
                            // Found; done searching
                            break;
                        }
                    }
                    if (null == type)
                    {
                        // Unable to find the requested type anywhere
                        throw new ArgumentException(string.Format(CultureInfo.CurrentCulture,
                            "Unable to access type \"{0}\". Try using an assembly qualified type name.",
                            item.Type));
                    }
                }
            }
    
            // Get the DependencyProperty for which to set the Binding
            DependencyProperty property = null;
            var field = type.GetField(item.Property + "Property",
                BindingFlags.FlattenHierarchy | BindingFlags.Public | BindingFlags.Static);
            if (null != field)
            {
                property = field.GetValue(null) as DependencyProperty;
            }
            if (null == property)
            {
                // Unable to find the requested property
                throw new ArgumentException(string.Format(CultureInfo.CurrentCulture,
                    "Unable to access DependencyProperty \"{0}\" on type \"{1}\".",
                    item.Property, type.Name));
            }
    
            // Set the specified Binding on the specified property
            element.SetBinding(property, item.Binding);
        }
    
        /// <summary>
        /// Returns a stream of assemblies to search for the provided type name.
        /// </summary>
        private static IEnumerable<Assembly> AssembliesToSearch
        {
            get
            {
                // Start with the System.Windows assembly (home of all core controls)
                yield return typeof(Control).Assembly;
    
                // Fall back by trying each of the assemblies in the Deployment's Parts list
                foreach (var part in Deployment.Current.Parts)
                {
                    var streamResourceInfo = Application.GetResourceStream(
                        new Uri(part.Source, UriKind.Relative));
                    using (var stream = streamResourceInfo.Stream)
                    {
                        yield return part.Load(stream);
                    }
                }
            }
        }
    }
    
  • Delay's Blog

    My new home page, expanded [Updated collection of great Silverlight Charting resources!]

    • 21 Comments

    It's been a couple of months since I shared my semi-comprehensive page of Charting resources on the web. During that time, the Silverlight Toolkit's December release came out with some great new Charting features (Woot!) and there have been a number of fantastic Charting posts that I'd like people to be aware of. So I've updated my previous list of links (FYI: old links are grayed-out) with all the new content that caught my eye:

    Overviews (100 level)

    Scenarios (200 level)

    Internals (300 level)

    My own Charting posts (Ego level)

    Many, many thanks to everyone who has spent time helping others learn how to use Silverlight Charting!

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

  • Delay's Blog

    Click your way to great Silverlight charts [Live ChartBuilder sample and source code!]

    • 20 Comments

    Yesterday in my Introduction to Charting with the Silverlight Toolkit post, I included a teaser for my ChartBuilder application. The tease ends today, because I've just posted a live ChartBuilder for everyone to play with and am also making the source code available for download.

    What is ChartBuilder?

    In addition to driving the Silverlight Charting effort and being one of the primary developers, I was also the entire test team for Charting. (I told you we were resource constrained.) I decided pretty quickly that I needed to do what I could to make it easy for anyone to exercise the Charting implementation, find bugs, and report them. Additionally, I wanted an easy way for the developers to exercise what they'd written in strange and unusual ways. My task was somewhat complicated by the fact that Charting supports fully dynamic data sources - static XAML Charts simply aren't enough to cover all the scenarios we care about.

    I tried to capture a hint of this dynamic behavior in the samples project (I wrote all the samples, too - except for the two fantastic Scenarios which were contributed by Ted Glaza (Custom Series) and Ruurd Boeke (Series Zoom)). But the sample project isn't the most debugging-friendly environment because of all the different Chart instances it loads. Last - but certainly not least - I knew there would be a lot of customers looking at Charting with very little idea how our API and object model work.

    I had a vision for an interactive chart-building application that would expose the most common Charting concepts with some simple UI, show how those settings combined to create a chart, AND show the corresponding XAML code to build that chart. What's more, I wanted changes to the chart's settings to be made in real-time to the running instance of the chart so that users could see how the dynamic data support actually works. (And so developers could fix it when it didn't!) And because it was so easy, I enabled a live XAML editing experience (think XAMLPad) for folks who demand absolute control over their charts. :)

    What does it look like? How do I play with it?

    Below is a static image of ChartBuilder as it existed yesterday to give an idea of what I'm talking about.

    Better yet, you can click this text or the image below to run the latest ChartBuilder in your browser!

    ChartBuilder

    [Click here to download the complete ChartBuilder source code.]

    The left side of the application is where you go to customize your chart, the upper-right side of the application shows the current chart, and the lower-right side shows the complete XAML for that chart (wrapped in a Grid to keep the XAML clean).

    Changes you make to the settings are automatically applied to the running instance of the chart as well as the XAML for that chart - so if you're curious how the API and object model look, just set things up how you want them and look at the XAML! As a convenience, the XAML area supports copying to the clipboard (use Ctrl+C or whatever your platform's standard "copy" keystroke is). That means you can build a chart you like and then paste the XAML right into your own source code to help get started.

    If you ever do something that's not allowed - or hit a bug - and an exception is thrown, the complete exception details will appear in a red text box over the chart - which can also be copied to the clipboard. ChartBuilder will let you continue to change the settings and keep going after an exception, but please bear in mind that some exceptions may leave the chart with inconsistent internal state. At any time you can hit the green "Recreate Chart" button to completely recreate the chart instance from the current settings and get everything back in sync.

    Anything else I should know?

    • ChartBuilder is a dual-edged sword: the same power that lets you explore strange and creative new scenarios also lets you do things that are not supported. We've tried to throw informative exceptions whenever we detect something unusual, so please read the exception message for details about what went wrong.
    • Of course, if an exception message makes no sense, or a broken scenario seems like it should be supported, or things just seem to be flat-out wrong, please post a question in the Silverlight Controls forum or report a bug with the Silverlight Toolkit Issue Tracker. When reporting issues, please include as much detail as possible; ChartBuilder makes that easy by letting you copy the entire exception text. What's more, if you can reproduce the problem reliably, then you can report it by giving us a simple list of steps to follow in ChartBuilder! Problems that are demonstrated by ChartBuilder scenarios are usually a pleasure to debug because it's so easy to tell what's going on and so easy to isolate the problematic behavior. [Well, most of the time! :) ]
    • One thing I did not do with ChartBuilder is spend a lot of time designing or implementing a comprehensive architecture for the application. More often than not, the little bits of time I found for adding features were spent in a frenzy of coding with me trying to enable new testing scenario as quickly as possible. When forced to choose between refactoring and getting a new feature implemented, the new feature always won. I've made a quick pass over the code since PDC and it's not as bad as I feared - but I guarantee it won't be winning any beauty contests. :)
    • Because ChartBuilder was written to validate the functionality of Charting, it would be ideal if ChartBuilder itself were bug-free. While I've done what I can to try to make that so, I'm sure a few things have slipped by here and there - in no small part because of the hurried conditions under which I added features to ChartBuilder. If you see a strange behavior and don't think it's a bug in Charting, please let me know because it may be ChartBuilder that's at fault!

    Are there any good usability tricks worth sharing?

    • Not all series types support all axis combinations. Specifically, it's not possible to put a column series and a bar series in the same chart because they put their independent/dependent axes in different places. Similarly, column and line don't go together because column only supports a category-based independent axis while line only supports a value-based (numeric or date) independent axis. Here's a quick cheat-sheet:
      Series Independent axis Works with
      Column Category Column, Pie
      Bar Vertical Category Bar, Pie
      Pie N/A Everything
      Line Number/Date Line, Scatter, Pie
      Scatter Number/Date Line, Scatter, Pie
    • The text input boxes validate their contents and update the chart state after every key press. This can make it tricky to make certain changes because the intermediate states are invalid. If you run into this situation, there are two tricks to try. First, see if you can select part of the text field and then type over it (ex: "10/28/2008" can be change to "10/29/2008" in one step by selecting the '8' and typing a '9'). If that doesn't help, another option is to open a Notepad window, type what you want there, copy it to the clipboard, select the entire text box in ChartBuilder and paste over it with the intended value.
    • The "Number of Axes" slider is disabled whenever there are one or more series present. This is because as soon as a series is rendered, it automatically creates whatever axes it needs to display itself - so if the user tried to add another axis, there would be multiple horizontal/vertical axes. At this time, Charting supports only a single horizontal and a single vertical axis - though please note that this restriction will be removed in an upcoming release to support multiple horizontal and vertical axes on the same chart. So if you want to customize the axes for your chart, first remove all the series, then add the axes you want, then add the series back - assuming the axis configuration that's set is supported by the series that are added, they will automatically use the provided axes and you will be able to customize them dynamically.
    • When "Allow XAML Editing" is checked, the XAML is completely re-parsed and the chart re-created after every keystroke. Similarly, when unchecking that option, the state stored in the input controls on the left is re-applied and the text in the XAML editing box is lost. Basically, once the option to edit XAML is enabled, ChartBuilder has no idea what the user has done and no way of mapping that back to the subset of changes it supports. So feel free to experiment with XAML mode (it's great for stuff like adding a colored background or extra data), but know that you're completely on your own when you do so.
    • ChartBuilder makes use of two classes it exposes via the "utility" XML namespace: ObservableObjectCollection and Pair. ObservableObjectCollection is simply an ObservableCollection<object> and Pair is just an object that exposes two properties "First" and "Second" of type object. These are purely convenience classes that make the XAML easy to deal with; you're welcome to make use of them in your own application if you care to.
    • ChartBuilder does its best to capture all exceptions that occur and display them in its user interface. This is quite handy when running outside of Visual Studio, but in order for it to have a chance to work when running under a debugger, be sure to configure the debugger to ignore handled exceptions (or just hit F5 when one occurs to allow ChartBuilder to catch and handle the exception).

    What are examples of some issues I might encounter?

    • Removing and re-adding a series over and over gives that series a different color each time. This is an artifact of how the Chart.StylePalette collection works: each time a new style is needed, it is fetched from the palette and the "next style" index incremented. So as long as the same chart instance is being used, recreating a series over and over gradually cycles through all the different colors of the default style palette. As soon as a new chart instance is created, the style palette is reset and the colors start from the beginning. In other words, the "random" behavior is completely deterministic and will always be the same for a particular application. If you want to guarantee that a particular series will always have the same color no matter what, there is a DataPointStyle property on Column/Bar/Line/ScatterSeries and a dedicated StylePalette property on PieSeries that can be set in code (but is not exposed by ChartBuilder at this time).
    • Removing the Chart (or Legend) Title does not automatically recover the space used by the text of that control (until something like a resize causes another layout pass). This is actually a Silverlight bug with ContentControl (which is what Title is) and can be reproduced outside Charting by placing a ContentControl in a StackPanel between two other elements and setting its Content property to null. This bug has been reported to the Silverlight team and should be fixed in a future release.
    • The default appearance of the line and scatter charts in "Automatic Doubles" mode is always a straight line (with a slope of 1) and the points just slide along it. "Manual Pairs" mode is the only mode that really makes sense for line and scatter because they need numeric independent values - but just so that "Automatic Doubles" and "Manual Doubles" modes show something, ChartBuilder automatically sets their IndependentValueBinding to "{Binding}". This provides an independent value, but it's always the same as the dependent value and so the points always lie on the same line. To really exercise line and scatter, switch to "Manual Pairs" mode, click the "All Doubles" button for both columns, enable a couple of points, and then have fun changing values.
    • Adding a point to the middle of the collection for line, bar, and pie charts adds that point to the end of the visual series. ChartBuilder's "Manual Doubles" and "Manual Pairs" modes allow you to individually enable and disable points - and specifically to "insert" points at the beginning, middle, or end of the collection. This is what ChartBuilder does internally (verify by looking at the XAML or clicking "Recreate Chart"), but the aforementioned series add that point to the end of their internal collections, so the new point always shows up after the existing points. This is a known issue to be addressed in a future release of Charting; it can be worked around for now by clicking "Recreate Chart".
    • Multiple occurrences of the same data point instance in the collection can get their visual representations mixed up. This can be seen by starting from the default ChartBuilder configuration, then increasing the "Starting Value" twice (from 1 to 3) - the columns of the resulting chart should increase in height from left to right, but do not. What happens is that the collection goes from [2, 3, ...] to [3, 3.6, ...] when the starting value goes from 2 to 3. As soon as the first value of the collection changes from 2 to 3, there are two instances of the value 3 in the collection: [3, 3, ...]. When the second value changes from 3 to 3.6, the chart incorrectly updates the column corresponding to the first point. This is unfortunate behavior, but is actually also present in the WPF and Silverlight ListBox implementations (though it manifests itself differently there). The basic problem is that the chart tracks things by object identity and the presence of the same object multiple times creates ambiguity for the tracking code. The good news is that this problem is only really an issue when the data set contains raw doubles or integers - the far more common scenario (in practice) of using unique business objects is unaffected by this because each business object is a unique instance. To verify this, switch to "Manual Pairs" mode for the column series, enable three points, set all their values to 1, then change any of them to a different value and observe that the correct column is always updated.
    • Changing the independent value of a pie series data point (in "Manual Pairs" mode) does not automatically update the label for that data point in the legend. This is a known issue to be addressed in a future release of Charting; it can be worked around for now by clicking "Recreate Chart".
    • There are certain circumstances under which toggling "Show GridLines" and "Should Include Zero" for a custom axis cause the axis to disappear (and the chart not to render). This is a known issue to be addressed in a future release of Charting; it can be worked around for now by clicking "Recreate Chart".

    Any last words?

    I wrote ChartBuilder to help you learn about Charting as much as to help us develop Charting. If it provides everyone with a common language that helps make it easy to talk about Charting features, suggestions, and issues, I'll be delighted!

    And now... Enjoy! :)

  • Delay's Blog

    Banana SplitButton [A WPF-specific fix for SplitButton and some code analysis improvements for the Silverlight version, too]

    • 20 Comments

    I've previously written that one of my ContextMenu test cases for the April '10 release of the Silverlight Toolkit was to implement a quick "split button" control for Silverlight using Button and ContextMenu. Though it wasn't my goal to build a general-purpose control for widespread use, there's been a lot of interest in SplitButton/MenuButton and I followed up with a few fixes for the Silverlight version and "official" support for WPF.

     

    SplitButton and MenuButton

     

    That might have been the end of the story - until Fabio Buscaroli contacted me to report an issue he was seeing with the WPF version (follow our exchange here). At first, I thought the problem was related to the DataContext not inheriting properly - and while that was true, it wasn't the whole story! When Fabio told me the tweak for DataContext I'd suggested didn't solve his RoutedCommand scenario, I had a look at a simple example he posted and realized the DataContext problem was a symptom of a larger issue: that the ContextMenu wasn't a logical child of the SplitButton. As soon as I solved that problem, it fixed Fabio's scenario and the related DataContext issue I'd discovered during my own investigation.

    Aside: None of this applies to Silverlight because that platform doesn't expose the notion of a logical tree the way WPF does.

     

    While I was working on this fix, I noticed full code analysis wasn't enabled for the SplitButton assemblies; I turned it on and addressed the handful of warnings it generated. Most of the changes won't matter to you, but one of them will: the namespace for SplitButton/MenuButton has changed. These classes now live in the Delay namespace - which is consistent with the rest of the sample code I post to my blog. Upgrading existing projects to the latest code will require a tweak of the XAML's "xmlns:" prefix and/or a tweak of the code's "using" statements. Otherwise, there have been no behavior changes to the functionality of SplitButton on Silverlight or WPF, so everything else should continue to work the same as before.

     

    [Click here to download the complete Silverlight/WPF source code for SplitButton/MenuButton and the sample application shown above.]

     

    It's been great to see the positive response SplitButton and MenuButton have generated. Thanks for everyone's help trying these controls out, finding issues, and reporting them!

Page 4 of 28 (277 items) «23456»