The blog of dlaa.me

Pineapple upside-down chart [How to: Invert the axis of a chart for "smaller is better" scenarios]

Let's imagine that we want to use Silverlight (or WPF!) to chart the performance of a book on one of those "bestsellers" lists... The book we care about has been doing very well lately; here's the corresponding data we want to display:

var items = new List<DataItem>
{
    new DataItem(new DateTime(2009, 4, 1), 10),
    new DataItem(new DateTime(2009, 4, 8),  5),
    new DataItem(new DateTime(2009, 4, 15), 2),
    new DataItem(new DateTime(2009, 4, 22), 1),
    new DataItem(new DateTime(2009, 4, 29), 1),
};

Naturally, we'll use the Charting controls that are part of the Silverlight Toolkit (and also available for WPF). :) Charting is easy to use and we quickly bang out the following XAML to create something suitable:

<charting:Chart
    FontSize="9">
    <charting:LineSeries
        ItemsSource="{Binding}"
        DependentValuePath="Place"
        IndependentValuePath="Date"
        Title="Book">
        <charting:LineSeries.DataPointStyle>
            <Style TargetType="charting:LineDataPoint">
                <Setter Property="Background" Value="Maroon"/>
            </Style>
        </charting:LineSeries.DataPointStyle>
        <charting:LineSeries.DependentRangeAxis>
            <charting:LinearAxis
                Orientation="Y"
                Minimum="0.5"
                Maximum="10.5"
                Interval="1"
                ShowGridLines="True"/>
        </charting:LineSeries.DependentRangeAxis>
    </charting:LineSeries>
</charting:Chart>

It looks like this:

Initial attempt

Hurm...

The chart is 100% correct, but there's a problem: it looks like the book is becoming less popular, not more popular. Most of us are used to assuming that "bigger/taller is better", but that's not the case for the data in this scenario and so the chart's meaning is not intuitively obvious. In the ideal world, there would be a bool Invert property on LinearAxis that you could toggle to "flip" the vertical axis and save the day. Unfortunately, we haven't yet implemented such a property (though it's on our list of things to do)...

Therefore, it looks like a clever solution is called for - and in this case the simple trick is to invert the values before charting them. After inversion, the "best" values (low numbers like 1) will be numerically greater than the "worst" values (high numbers like 10) and will therefore appear towards the top of the chart. This seems almost too easy, so let's see how it works out in practice by writing a simple IValueConverter to invert the values and then making the highlighted changes to the XAML:

public class InverterConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        if (value is int)
        {
            return -(int)value;
        }
        throw new NotImplementedException();
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}
<charting:Chart
    FontSize="9">
    <charting:LineSeries
        ItemsSource="{Binding}"
        DependentValueBinding="{Binding Place, Converter={StaticResource InverterConverter}}"
        IndependentValuePath="Date"
        Title="Book">
        <charting:LineSeries.DataPointStyle>
            <Style TargetType="charting:LineDataPoint">
                <Setter Property="Background" Value="Maroon"/>
            </Style>
        </charting:LineSeries.DataPointStyle>
        <charting:LineSeries.DependentRangeAxis>
            <charting:LinearAxis
                Orientation="Y"
                Minimum="-10.5"
                Maximum="-0.5"
                Interval="1"
                ShowGridLines="True"/>
        </charting:LineSeries.DependentRangeAxis>
    </charting:LineSeries>
</charting:Chart>

The resulting chart:

Inverted axis

Woot - the chart now clearly communicates the book's recent popularity! But the trick we played with negative numbers is plainly visible for everyone to see and they will probably mock us mercilessly. :( If only there were some way to customize the chart's visuals to hide what we've done and complete the illusion...

Wait - there is a way! All we need to do is take advantage of Charting's DataPoint.DependentValueStringFormat and LinearAxis.AxisLabelStyle properties and mix in a little of .NET's support for "Section Separators and Conditional Formatting".

[Type, type, type...]

<charting:Chart
    FontSize="9">
    <charting:LineSeries
        ItemsSource="{Binding}"
        DependentValueBinding="{Binding Place, Converter={StaticResource InverterConverter}}"
        IndependentValuePath="Date"
        Title="Book">
        <charting:LineSeries.DataPointStyle>
            <Style TargetType="charting:LineDataPoint">
                <Setter Property="Background" Value="Maroon"/>
                <Setter Property="DependentValueStringFormat" Value="{}{0:0.#;0.#}"/>
            </Style>
        </charting:LineSeries.DataPointStyle>
        <charting:LineSeries.DependentRangeAxis>
            <charting:LinearAxis
                Orientation="Y"
                Minimum="-10.5"
                Maximum="-0.5"
                Interval="1"
                ShowGridLines="True">
                <charting:LinearAxis.AxisLabelStyle>
                    <Style TargetType="charting:AxisLabel">
                        <Setter Property="StringFormat" Value="{}{0:0.#;0.#}"/>
                    </Style>
                </charting:LinearAxis.AxisLabelStyle>
            </charting:LinearAxis>
        </charting:LineSeries.DependentRangeAxis>
    </charting:LineSeries>
</charting:Chart>

Presto:

Complete success

Success - our chart looks exactly how we want it to and we barely even broke a sweat! You can go ahead and pat yourself on the back a few times - then stop spending time imagining Charting scenarios and get back to work! :)

[Click here to download the complete source code for the sample application used to create the charts shown above.]

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

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);
                }
            }
        }
    }
}

Going "extreme" with Silverlight 3 [Sharing the source code for a real-world sample application]

My teammates and I spent some time last week on an exercise known as "app building" to help identify issues with the latest build of Silverlight, the SDK, and the Silverlight Toolkit. The way app building works is that everyone comes up with an idea for a medium-sized application they think could be built with the bits at hand - then goes off and tries to build as much of that application as they can before time runs out!

The emphasis is on testing new scenarios, coming up with creative ways of integrating components, and basically just getting the same kind of experience with the framework that customers have every day. Coming up with a beautifully architected solution is nice if it happens, but not specifically a goal here. Rather, the point is to help people take a holistic look at how everything works together - because sometimes you'll find that two things which both look good in isolation are quite difficult to use together. App building is a great technique to use as part of the quality assurance process and the time we spent was definitely worthwhile. :)

For my application, I decided to write an organizational hierarchy viewer based loosely on an internal tool managers use at Microsoft. The application offers three main ways to visualize the data: a hierarchical tree of all employees at the left, a flattened summary pane of all employees at the bottom, and a detailed view of the selected employee at the right. I also added a search feature and a simple chart for visualizing the size of someone's "empire". (Because I love me some Charting...) I called my app "HeadTraxExtreme" (partly an inside joke) and here's what it looked like after the two days I spent banging it out:

HeadTraxExtreme sample application

[If you have the Silverlight 3 Beta installed, click here (or on the image above) to run HeadTraxExtreme in your browser.]

[If you want to have a look at the complete source code or build HeadTraxExtreme yourself, click here to download it.]

 

Notes:

  • HeadTraxExtreme incorporates the following controls/concepts:
    • DataGrid
    • DataForm
    • TreeView/TreeViewItem
    • AutoCompleteBox
    • ComboBox
    • Chart/PieSeries/PieDataPoint
    • Image
    • Data Binding
    • Model-View-ViewModel (MVVM)
    • Custom Style/Template
    • Asynchronous data access
    • Out-of-browser
    • IValueConverter
    • INotifyPropertyChanged
    • XLinq
    • Layout
  • The employee images are downloaded from the web site of origin, so if you download and build it yourself please be sure to run HeadTraxExtreme using the included web project (HeadTraxExtreme.Web) if you want to see the images.
  • Because we were testing the latest (private) builds of everything, I needed to back-port my code to the official Silverlight 3 Beta bits in order to post it here. That didn't take long, but it did draw my attention to a couple of very notable improvements that have been made since the Beta bits went out. I won't say more because I don't want to ruin any surprises, but I can say that a couple of the controls will be much more pleasant to use by RTM. :)
  • My original version displayed a 300+ person org, but I didn't want to publish everyone's personal data without permission. :) So I made up the completely fictional 14 person mini-org you see above. Any resemblance to an actual org is unintentional...
  • There are two aspects of HeadTraxExtreme that I plan to write more about in the next few days. In both cases, I approached something in a specific way and I'd like to highlight what I did and talk about why I thought that was a good way.

Building HeadTraxExtreme was a fun little diversion that turned up some good issues for everyone. It exposed me to a couple of controls I hadn't used yet and I'm glad to have broadened my knowledge. I think there's probably a little something for everyone here; I hope HeadTraxExtreme can be a good learning experience for others, too!

Trying to get the story straight [A brief summary of Storyboard differences between WPF and Silverlight]

I was investigating a Silverlight Toolkit bug this morning and got into a spirited discussion about Storyboard behavior with Jafar. We disagreed on a couple of points, so I [grudgingly :) ] coded up a quick test application to help sort things out. Unfortunately, it turns out that the situation is a little weirder than either of us realized... I knew I'd never be able to remember the specifics, so I'm documenting my findings here for everyone's benefit.

StoryboardBehavior on WPF

At the heart of the matter is the way Storyboards fit into the Silverlight/WPF property system. What's generally expected is that when a Storyboard is actively animating a property, the animation value takes precedence over the local value. It should still be possible to change the local value, but such changes shouldn't be visible until the Storyboard gives up control of the property. That said, there are a few ways in which WPF and Silverlight seem to disagree - as the following table illustrates. To help keep things clear, I've colored things green or red according to whether I believe the behavior to be correct or incorrect (respectively) based on my current understanding. (Note: All tests were performed with the default HoldEnd FillBehavior.)

Scenario WPF Silverlight 2 /
Silverlight 3 Beta
[DoubleAnimation.From specified]
  1. Start animation from value A to value B
  2. Set value C during animation
  3. Stop animation when complete
Property has value C Property has value A
[DoubleAnimation.From specified]
  1. Start animation from value A to value B
  2. Set value C after animation completes
Property has value B Property has value C
[DoubleAnimation.From not specified]
  1. Start animation from value A to value B
  2. Set value C during animation
Animation "jitters" when value C is set No "jitter"

You can experiment with these behaviors using the sample application I built for this purpose. It works quite simply: the width of the orange rectangle starts at 200 pixels and is changed by either animating it wider to 300 pixels or by directly setting it narrower to 50 pixels. The animation is performed by a 2 second Storyboard, so there's plenty of time to change things during the animation. (The relevant code is included below.)

StoryboardBehavior on Silverlight

As far as I can tell, the WPF behavior is the most correct. Granted, the animation "jitter" is visually jarring (and doesn't fit with my understanding of the property system hierarchy), but things quickly work themselves out and the final results on that platform make the most sense to me. Of course, I'll be passing all this information on to the folks who actually own this functionality and will help get bugs get opened for any behavior that turns out to be wrong. And with luck, these inconsistencies will be gone in a future release of Silverlight and/or WPF! :)

 

[Click here to download the StoryboardBehavior test application source code for WPF and Silverlight.]

 

public partial class DemoControl : UserControl
{
    public DemoControl()
    {
        InitializeComponent();
    }

    public bool SetFrom { get; set; }

    private Storyboard Storyboard
    {
        get
        {
            if (null == _storyboard)
            {
                _storyboard = new Storyboard();
                Storyboard.SetTarget(_storyboard, Indicator);
                Storyboard.SetTargetProperty(_storyboard, new PropertyPath("Width"));
                DoubleAnimation doubleAnimation = new DoubleAnimation();
                doubleAnimation.Duration = new Duration(TimeSpan.FromSeconds(2));
                if (SetFrom)
                {
                    doubleAnimation.From = 200;
                }
                doubleAnimation.To = 300;
                _storyboard.Children.Add(doubleAnimation);
            }
            return _storyboard;
        }
    }
    private Storyboard _storyboard;

    private void StartGrowingAnimation(object sender, RoutedEventArgs e)
    {
        Storyboard.Begin();
    }

    private void StopGrowingAnimation(object sender, RoutedEventArgs e)
    {
        Storyboard.Stop();
    }

    private void SetSmallerSize(object sender, RoutedEventArgs e)
    {
        Indicator.Width = 50;
    }
}

My new home page, extended [Updated collection of great Silverlight and WPF Charting resources!]

It's been a while since the March 09 release of the Silverlight Toolkit - and even longer since I last posted a collection of Charting links. It's clearly time for an update, so I've added a bunch of new links to the collection below (FYI: previously published links are gray):

Overviews (100 level)

Scenarios (200 level)

Internals (300 level)

Jafar Husain's posts (Partner level)

My posts (Ego level)

Many, many thanks to everyone who has spent time helping others learn how to use Silverlight/WPF 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! :)

Another round of (un)support [Quick fix for the unofficial WPF Charting assembly!]

When I updated my ChartBuilder sample/application/learning tool for the March 09 release of the Silverlight Toolkit a few weeks ago, I included an unofficial build of the Silverlight Charting assembly for WPF. Despite my warning that WPF Charting was completely untested, I nevertheless hoped that some of you would give WPF Charting a try - and you didn't let me down. :) Two significant WPF-only issues came up pretty quickly, and I responded by fixing them and publishing an updated build of the WPF Charting assembly - along with sharing the files and steps for anyone to build the WPF Charting assembly themselves!

Things were pretty quiet on the WPF Charting front until recently when forum user giggs123 reported seeing an exception when handling the SelectionChanged event of a DataPointSeries class. I investigated and discovered that the WPF-only SelectionChangedEvent was handled incorrectly. :( While SelectionChanged is a normal .NET event on Silverlight (because that's all that's supported), it is a RoutedEvent on WPF. Except that it was hooked up wrong and caused an exception if it was raised when something was listening for it... (Fortunately, handling selection is a relatively uncommon scenario, so this probably wouldn't affect most of you. And, incidentally, the reason I didn't catch it with WPF ChartBuilder is that raising the event works just fine when nothing's listening for it!)

Long story short, I replied on the forum with a simple patch to the code - then went ahead and made the full fix and checked it into our version control system for inclusion with the next release of Charting. I've updated the WPF Charting assembly in the ChartBuilder download to include this fix (along with the previous two), so if you're already playing around with WPF Charting, please take a moment to upgrade to the new version of System.Windows.Controls.DataVisualization.Toolkit.dll from the ChartBuilder download link below. And if you're building WPF Charting for yourself, you'll be happy to know that I've also updated the code in the Controls.DataVisualization.Toolkit.WPF.zip archive to include the latest changes. :)

Again, I'd like to stress that none of these problems is present in Silverlight Charting; they exist only in the unofficially available WPF Charting bits and are there because we did no testing on WPF due to a lack of time or resources. If you don't care about WPF Charting, then none of this matters to you; there are no other changes to the files in the downloads.

Thanks again to everyone who has used WPF Charting and reported issues to us - we appreciate your help and patience!

 

[Please click here to download the complete ChartBuilder source code and the ready-to-use WPF Charting assembly (don't forget to reference WPFToolkit.dll, too).]

[Please click here to download Controls.DataVisualization.Toolkit.WPF.zip to build WPF Charting yourself.]

 

ChartBuilder on WPF

Fewer gotchas to getcha [Enhancing the ScrollIntoViewCentered method for WPF's ListBox]

Earlier this month I blogged about adding the ScrollIntoViewCentered method to WPF's ListBox control. At the time, I explained why it was necessary to set ScrollViewer.CanContentScroll to False for the code I'd written to function. The limitation didn't matter for my scenario, so I didn't spend too much time worrying about it then...

However, after putting the code into use for my RepositoryExplorer version control system browser side-project, I discovered a bug. And further investigation proved that it wasn't a bug in my code - it was a bug in WPF! Specifically, setting ScrollViewer.CanContentScroll to False for a ListBox with a horizontal ScrollBar breaks the Page Down key under most circumstances. (FYI, I've already reported this issue to the WPF team to fix in a future release.) Here's a complete demonstration of the problem:

<ListBox
    ItemsSource="{x:Static Fonts.SystemFontFamilies}"
    ScrollViewer.CanContentScroll="False"
    ScrollViewer.HorizontalScrollBarVisibility="Visible"/>

Just paste that XAML in a new application (or XamlPad), run it, and try to Page Down through the items in the list - you'll find that instead of advancing a page each time you press Page Down, the list advances only a single line. Specifically, the problem seems to occur if the bottom-most item in the ListBox can hide completely behind the horizontal ScrollBar - which it does for the default font size of a WPF application. :(

This bug is pretty bad and I didn't see a clean way of working around it, so I decided to revisit my ScrollIntoViewCentered implementation to see if there was some way I could get it working with ScrollViewer.CanContentScroll set to True...

And as luck would have it, I managed to do so! Scroll down to find an updated version of ScrollIntoViewCentered that works well with both settings. I've also updated the sample application to show ScrollIntoViewCentered working with a ListBox where ScrollViewer.CanContentScroll is False (left; same as before) and another where it is True (right; new scenario). I've forced the horizontal ScrollBar on for both scenarios so you can see the broken Page Down behavior in the first one.

ListBoxExtensionsDemo sample

There's just one thing to keep in mind: when using logical scrolling (item-based), WPF deals with item indices rather than pixel sizes for all of the items. The good news is that logical scrolling is noticeably faster than physical scrolling (pixel-based), so the new support for it by ScrollIntoViewCentered allows you to use the faster scrolling option. However, the bad news is that the calculations ScrollIntoViewCentered does for logical scrolling assume that all the items in the list are the same size. While this is nearly always the case with ListBox items, it doesn't need to be. So please consider the tradeoffs when choosing how you use ScrollIntoViewCentered.

[Click here to download the source code for ScrollIntoViewCentered and the ListBoxExtensionsDemo sample application.]

Happy (more flexible) scrolling!

 

/// <summary>
/// Class implementing helpful extensions to ListBox.
/// </summary>
public static class ListBoxExtensions
{
    /// <summary>
    /// Causes the object to scroll into view centered.
    /// </summary>
    /// <param name="listBox">ListBox instance.</param>
    /// <param name="item">Object to scroll.</param>
    [SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters",
        Justification = "Deliberately targeting ListBox.")]
    public static void ScrollIntoViewCentered(this ListBox listBox, object item)
    {
        Debug.Assert(!VirtualizingStackPanel.GetIsVirtualizing(listBox),
            "VirtualizingStackPanel.IsVirtualizing must be disabled for ScrollIntoViewCentered to work.");

        // Get the container for the specified item
        var container = listBox.ItemContainerGenerator.ContainerFromItem(item) as FrameworkElement;
        if (null != container)
        {
            if (ScrollViewer.GetCanContentScroll(listBox))
            {
                // Get the parent IScrollInfo
                var scrollInfo = VisualTreeHelper.GetParent(container) as IScrollInfo;
                if (null != scrollInfo)
                {
                    // Need to know orientation, so parent must be a known type
                    var stackPanel = scrollInfo as StackPanel;
                    var virtualizingStackPanel = scrollInfo as VirtualizingStackPanel;
                    Debug.Assert((null != stackPanel) || (null != virtualizingStackPanel),
                        "ItemsPanel must be a StackPanel or VirtualizingStackPanel for ScrollIntoViewCentered to work.");

                    // Get the container's index
                    var index = listBox.ItemContainerGenerator.IndexFromContainer(container);

                    // Center the item by splitting the extra space
                    if (((null != stackPanel) && (Orientation.Horizontal == stackPanel.Orientation)) ||
                        ((null != virtualizingStackPanel) && (Orientation.Horizontal == virtualizingStackPanel.Orientation)))
                    {
                        scrollInfo.SetHorizontalOffset(index - Math.Floor(scrollInfo.ViewportWidth / 2));
                    }
                    else
                    {
                        scrollInfo.SetVerticalOffset(index - Math.Floor(scrollInfo.ViewportHeight / 2));
                    }
                }
            }
            else
            {
                // Get the bounds of the item container
                var rect = new Rect(new Point(), container.RenderSize);

                // Find constraining parent (either the nearest ScrollContentPresenter or the ListBox itself)
                FrameworkElement constrainingParent = container;
                do
                {
                    constrainingParent = VisualTreeHelper.GetParent(constrainingParent) as FrameworkElement;
                } while ((null != constrainingParent) &&
                         (listBox != constrainingParent) &&
                         !(constrainingParent is ScrollContentPresenter));

                if (null != constrainingParent)
                {
                    // Inflate rect to fill the constraining parent
                    rect.Inflate(
                        Math.Max((constrainingParent.ActualWidth - rect.Width) / 2, 0),
                        Math.Max((constrainingParent.ActualHeight - rect.Height) / 2, 0));
                }

                // Bring the (inflated) bounds into view
                container.BringIntoView(rect);
            }
        }
    }
}
Tags: WPF

Comma quibbling a la Eric and Jafar [A slightly wacky approach to the problem in C#]

Jafar was teasing me about his F# solution to Eric Lippert's comma quibbling problem, and I decided to retaliate. :) One of the things I didn't like about the five-or-so solutions I'd seen [including Jafar's :P ] was that they were doing a bunch of work to detect special cases on every pass through the loop. That seemed silly to me because there are really only two special cases and they only come into play at the very end of the operation.

So I threw together the following method which has a fairly efficient inner loop which saves the special cases for the end. I also made it generic so it'll take an input stream of any type - then defer to StringBuilder (or the object itself!) for proper formatting. Oh, and the code is both compact and commented. :)

The downside - and it's a big one - is that the input stream is enumerated three times instead of just one. :(

Oh well, when you're out for a quick bit of revenge you can't accomplish everything... :)

/// <summary>
/// Solve comma quibbling posed by Eric Lippert at:
/// http://blogs.msdn.com/ericlippert/archive/2009/04/15/comma-quibbling.aspx.
/// </summary>
/// <typeparam name="T">Type of input.</typeparam>
/// <param name="input">Stream of input elements (ex: string).</param>
/// <returns>Quibbled string.</returns>
/// <remarks>
/// Good points:
/// * Generic type support (StringBuilder formats for output)
/// * Special-case logic is not run every time through the loop
/// Bad points:
/// * Input stream is traversed three times :(
/// </remarks>
private static string CommaQuibbling<T>(IEnumerable<T> input)
{
    // Capture stream
    var a = input.GetEnumerator();
    var b = input.Skip(1).GetEnumerator();
    var c = input.Skip(2).GetEnumerator();
    // Prefix the result
    var sb = new StringBuilder("{");
    // Process the "normal" leading elements
    while (c.MoveNext() && b.MoveNext() && a.MoveNext())
    {
        sb.Append(a.Current).Append(", ");
    }
    // Process the non-Oxford comma scenario
    if (b.MoveNext() && a.MoveNext())
    {
        sb.Append(a.Current).Append(" and ");
    }
    // Process the remaining element
    if (a.MoveNext())
    {
        sb.Append(a.Current);
    }
    // Postfix the result and return it
    return sb.Append("}").ToString();
}
Tags: Technical

If one proxy is good, two must be better [Socks5to4a gives your SOCKS 4a proxy a free upgrade!]

I occasionally find myself on a particular network that's separated from the rest of the Internet by a firewall. The administrators of this network are kind enough to provide both HTTP and SOCKS 4a proxy servers, so it's generally quite easy to get access to the Internet in spite of the firewall. Unfortunately, a couple of the programs I use from time to time support only the SOCKS 5 protocol which is not backwards compatible with the SOCKS 4a protocol...

Well, after bumping into this problem again recently, I checked the SOCKS specifications on Wikipedia and decided it would be pretty straightforward to write a SOCKS 5 proxy server that forwarded all its traffic to a SOCKS 4a proxy server to do the real work. There's just not that much to a proxy server - especially when you're only interested in supporting TCP/IP streams and aren't hoping for production-quality code. :)

So I dashed off a program called Socks5to4a that does exactly what I envisioned. I just run it on my machine, tell it how to connect to the network's SOCKS 4a server, and point my SOCKS 5 applications at localhost. The programs all think they're talking to a real SOCKS 5 server and the network's SOCKS 4a server thinks it's talking to a real SOCKS 4a client - and they're almost right!

With Socks5to4a, things couldn't be simpler - here's what it looks like in action:

D:\T>Socks5to4a myproxy 1080 fallback.example.com
0: Listening on localhost:1080 with fall-back to fallback.example.com...
1: Accepted connection.
1: Reading client request...
1: Client requests fallback.example.com:1234.
1: Connecting to proxy...
1: Connected.
1: Forwarding request...
1: Connected.
1: Sending response...
1: Proxying data...
2: Accepted connection.
2: Reading client request...
2: Client requests fallback.example.com:1234.
2: Connecting to proxy...
2: Connected.
2: Forwarding request...
2: Connected.
2: Sending response...
2: Proxying data...
3: Accepted connection.
3: Reading client request...
3: Client requests 192.168.1.6:2345.
3: Connecting to proxy...
3: Connected.
3: Forwarding request...
3: Connect failed.
3: Retrying with fallback.example.com:2345...
3: Connected.
3: Sending response...
3: Proxying data...
3: Connection closed.
1: Connection closed.
2: Connection closed.

If you paid close attention to the above trace, you probably noticed the "fall-back" behavior. What's going on is that one of the applications I use connects to a server that misreports its public IP address - so subsequent attempts to connect to that IP fail. What I did is add a bit of smarts to Socks5to4a so I can specify an optional fall-back server when I run it; and then any connection failures are automatically (and seamlessly) retried using the fall-back server. Because the fall-back server is used only when necessary, this kludge stays out of the way until it's needed. At which point it's elegant and icky at the same time... :)

Socks5to4a is something I wrote to solve a specific problem I was having - but if it's interesting to you, please go ahead and have a look or give it a try. However, please remember that I specifically did not set out to create the world's best or fastest SOCKS 5 server - the code I wrote works well enough for my scenarios, but may need a bit of tweaking for yours.

[Click here to download the Socks5to4a application along with its complete source code.]

Happy proxying!

A bit more(er) than meets the eye [Easily animate and update LayoutTransformer with AnimationMediator!]

Yesterday I posted the code for AnimationMediator, a simple Mediator class to make it easy to animate the Transitions of a LayoutTransformer. Today, Silverlight Toolkit teammate Ted Glaza asked why the LayoutTransformerName property was present and I said that while I'd really wanted to use Binding+ElementName with the LayoutTransformer property, it didn't work for me when I tried. Ted kindly pointed out that in order for that to succeed, the LayoutTransformer property needed to be a DependencyProperty - and I'd used a simple CLR property instead. :(

Aside: What happened is that I originally used a DependencyProperty, convinced myself it wasn't working, then simplified to a plain CLR property. But I obviously did something wrong along the way, because the scenario I wanted to enable works great once LayoutTransformer is a DependencyProperty.

 

So I've updated the implementation of AnimationMediator accordingly; you can get the new version by downloading the source code again - or from below. And with this update there's no need to use the LayoutTransformerName property on Silverlight 3 (in fact, I've removed it from the code!) - so the scenario from last time simplifies to the following:

<local:AnimationMediator
    x:Name="RotationMediator"
    LayoutTransformer="{Binding ElementName=ButtonTransformer}"
    AnimationValue="{Binding Angle, ElementName=Rotation, Mode=TwoWay}"/>
Aside: Binding's ElementName property isn't present on Silverlight 2, so the LayoutTransformerName property is still relevant on that platform. Therefore, I have not changed the AnimationMediator implementation I originally included in the text of yesterday's post.

 

AnimatingLayoutTransformer Demo

 

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

 

As long as I was updating the sample application, I wanted to take the opportunity to show off two other things as well. The first is that AnimationMediator is good for more than just Storyboards - you can also use it with XAML-only bindings to other UI elements! In this case, I've added a TextBlock and two Sliders and hooked those Sliders up to the ScaleTransform of a LayoutTransformer for the text. As you'll see if you run the demo, it works just like you'd expect and it's 100% XAML, no code. :) The second thing I wanted to show is a proof of my claim that you can use two AnimationMediators with the same LayoutTransformer - which I do here.

This is what the XAML for the new scaling scenario looks like:

<!-- Applies the LayoutTransform for TextBlock -->
<layoutToolkit:LayoutTransformer
    x:Name="TextTransformer">

    <!-- A scale transformation-->
    <layoutToolkit:LayoutTransformer.LayoutTransform>
        <ScaleTransform
            x:Name="Scale"/>
    </layoutToolkit:LayoutTransformer.LayoutTransform>

    <!-- Text being scaled -->
    <TextBlock
        Text="Scale Me!"
        FontSize="50"
        FontWeight="Bold"
        HorizontalAlignment="Center"
        VerticalAlignment="Center"/>

</layoutToolkit:LayoutTransformer>

<!-- An AnimationMediator for each Slider Binding -->
<local:AnimationMediator
    x:Name="ScaleXMediator"
    LayoutTransformer="{Binding ElementName=TextTransformer}"
    AnimationValue="{Binding ScaleX, ElementName=Scale, Mode=TwoWay}"/>
<local:AnimationMediator
    x:Name="ScaleYMediator"
    LayoutTransformer="{Binding ElementName=TextTransformer}"
    AnimationValue="{Binding ScaleY, ElementName=Scale, Mode=TwoWay}"/>

<!-- A Slider for X and Y -->
<Slider
    Maximum="5"
    Value="{Binding AnimationValue, ElementName=ScaleXMediator, Mode=TwoWay}"
    Grid.Row="1"/>
<Slider
    Maximum="5"
    Value="{Binding AnimationValue, ElementName=ScaleYMediator, Mode=TwoWay}"
    Grid.Row="2"/>

 

So thanks for pointing this out, Ted - AnimationMediator is now just about as simple as can be!

 

 

PS - Here's the updated implementation of AnimationMediator:

/// <summary>
/// Class that acts as a Mediator between a Storyboard animation and a
/// Transform used by the Silverlight Toolkit's LayoutTransformer.
/// </summary>
/// <remarks>
/// Works around an issue with the Silverlight platform where changes to
/// properties of child Transforms assigned to a Transform property do not
/// trigger the top-level property changed handler (as on WPF).
/// </remarks>
public class AnimationMediator : FrameworkElement
{
    /// <summary>
    /// Gets or sets a reference to the LayoutTransformer to update.
    /// </summary>
    public LayoutTransformer LayoutTransformer
    {
        get { return (LayoutTransformer)GetValue(LayoutTransformerProperty); }
        set { SetValue(LayoutTransformerProperty, value); }
    }
    public static readonly DependencyProperty LayoutTransformerProperty =
        DependencyProperty.Register(
            "LayoutTransformer",
            typeof(LayoutTransformer),
            typeof(AnimationMediator),
            new PropertyMetadata(LayoutTransformerPropertyChanged));
    private static void LayoutTransformerPropertyChanged(
        DependencyObject o,
        DependencyPropertyChangedEventArgs e)
    {
        var layoutTransformer = (LayoutTransformer)(e.NewValue);
        if (null != layoutTransformer)
        {
            // Update now to be safe
            layoutTransformer.ApplyLayoutTransform();
        }
    }

    /// <summary>
    /// Gets or sets the value being animated.
    /// </summary>
    public double AnimationValue
    {
        get { return (double)GetValue(AnimationValueProperty); }
        set { SetValue(AnimationValueProperty, value); }
    }
    public static readonly DependencyProperty AnimationValueProperty =
        DependencyProperty.Register(
            "AnimationValue",
            typeof(double),
            typeof(AnimationMediator),
            new PropertyMetadata(AnimationValuePropertyChanged));
    private static void AnimationValuePropertyChanged(
        DependencyObject o,
        DependencyPropertyChangedEventArgs e)
    {
        ((AnimationMediator)o).AnimationValuePropertyChanged();
    }
    private void AnimationValuePropertyChanged()
    {
        if (null == LayoutTransformer)
        {
            throw new InvalidOperationException(
                "AnimationMediator's LayoutTransformer property must not be null.");
        }
        // The Transform hasn't been updated yet; schedule an update to run after it has
        Dispatcher.BeginInvoke(() => LayoutTransformer.ApplyLayoutTransform());
    }
}