Delay's Blog

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

April, 2009

Posts
  • Delay's Blog

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

    • 10 Comments

    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;
        }
    }
    
  • Delay's Blog

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

    • 25 Comments

    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! :)

  • Delay's Blog

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

    • 3 Comments

    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
  • Delay's Blog

    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);
                }
            }
        }
    }
    
  • Delay's Blog

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

    • 3 Comments

    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();
    }
    
  • Delay's Blog

    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!

  • Delay's Blog

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

    • 6 Comments

    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());
        }
    }
    
  • Delay's Blog

    A bit more than meets the eye [Easily animate LayoutTransformer with AnimationMediator!]

    • 3 Comments

    I came across a question on the Silverlight Toolkit support forum yesterday asking how to animate the values of a Transform used by the Silverlight Toolkit's LayoutTransformer. (Background on LayoutTransformer: Motivation and introduction for Beta 1, Significant enhancements and update for Beta 2, Update for RTW, Fixes for two edge case bugs, Usefulness on WPF, Rename for Silverlight Toolkit.) As the questioner discovered, this task isn't quite as easy as it should be due to a difference between Silverlight and WPF.

    Aside: I describe the Silverlight limitation in more detail under the third bullet point of the "Notes" in this post (though that workaround has since been invalidated per the first bullet point of the "Notes" of this post).
    AnimatingLayoutTransformer Demo

    I wanted something that was simple and easy to use - and managed to come up with a pretty reasonable XAML-only solution on my second try. [The first attempt would have been a bit simpler, but didn't work out. :( ] The basic idea is to insert a Mediator between the animation and the LayoutTransformer and let the Mediator make sure the LayoutTransformer is updated when necessary. This task is complicated slightly by the fact that Silverlight won't let you create a Binding on one of its Transforms - but the equivalent effect can be had by putting a TwoWay Binding on the Mediator instead (as discussed in more detail by Jeff Prosise here).

    This is what the XAML for the above sample looks like:

    <Grid.Resources>
        <!-- Storyboard to animate the Button; targets AnimationMediator -->
        <Storyboard x:Key="Animation">
            <DoubleAnimation
                Storyboard.TargetName="RotationMediator"
                Storyboard.TargetProperty="AnimationValue"
                To="180"
                AutoReverse="True"
                Duration="0:0:0.3"/>
        </Storyboard>
    </Grid.Resources>
    
    <!-- Target of animation; forwards changes to the RotateTransform -->
    <local:AnimationMediator
        x:Name="RotationMediator"
        LayoutTransformerName="ButtonTransformer"
        AnimationValue="{Binding Angle, ElementName=Rotation, Mode=TwoWay}"/>
    
    <!-- Applies the LayoutTransform for Button -->
    <layoutToolkit:LayoutTransformer
        x:Name="ButtonTransformer"
        Grid.Column="1"
        Grid.Row="1">
    
        <!-- A simple transformation -->
        <layoutToolkit:LayoutTransformer.LayoutTransform>
            <RotateTransform
                x:Name="Rotation"/>
        </layoutToolkit:LayoutTransformer.LayoutTransform>
    
        <!-- Button being animated -->
        <Button
            Content="Click to Animate!"
            Click="Button_Click"
            FontSize="30"/>
    
    </layoutToolkit:LayoutTransformer>
    

    The sample application is written for Silverlight 3 because I wanted to make use of the new ElementName feature to keep things simple - but the basic idea applies to Silverlight 2 just the same. (However, please note that it may be necessary to hook up the Binding with code on that platform.) And if you find that you need to support multiple Transforms, just add a few more AnimationMediators to the mix! :)

    There you have it - with just a little bit of extra typing to add an AnimationMediator, the original scenario works quite nicely!

     

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

     

    PS - Here's the implementation of AnimationMediator:

    Updated 2009-04-10: Please see this post for an update that makes AnimationMediator a little easier to use on Silverlight 3.

    /// <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; set; }
    
        /// <summary>
        /// Gets or sets the name of the LayoutTransformer to update.
        /// </summary>
        /// <remarks>
        /// This property is used iff the LayoutTransformer property is null.
        /// </remarks>
        public string LayoutTransformerName
        {
            get
            {
                return _layoutTransformerName;
            }
            set
            {
                _layoutTransformerName = value;
                // Force a new name lookup
                LayoutTransformer = null;
            }
        }
        private string _layoutTransformerName;
    
        /// <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)
            {
                // No LayoutTransformer set; try to find it by LayoutTransformerName
                LayoutTransformer = FindName(LayoutTransformerName) as LayoutTransformer;
                if (null == LayoutTransformer)
                {
                    throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture,
                        "AnimationMediator was unable to find a LayoutTransformer named \"{0}\".",
                        LayoutTransformerName));
                }
            }
            // The Transform hasn't been updated yet; schedule an update to run after it has
            Dispatcher.BeginInvoke(() => LayoutTransformer.ApplyLayoutTransform());
        }
    }
    
  • Delay's Blog

    Nobody likes seeing the hourglass... [Keep your application responsive with BackgroundTaskManager on WPF and Silverlight]

    • 9 Comments

    In my last post I talked about the source control repository browser project I've been playing around with and mentioned that one of my primary goals was for the application to be "Completely asynchronous so that one or more long-running network tasks wouldn't hang the user interface". Some of you asked me to go into more detail about how this application works, and I thought I'd begin by discussing the asynchronous aspect.

    The challenge here is that sometimes an application needs to perform a long-running task and it doesn't want to do so on the user interface thread because that causes noticeable delays and those make users grumpy. (Any time you see an application become completely unresponsive and stop updating its window, that's probably because the UI thread is busy doing something other than updating the UI...)

    Fortunately, there's usually an API available for potentially long-running tasks that is written according to one of the common asynchronous programming design patterns. When that's the case, making use of the provided construct is almost always the preferred option because the API's authors are in the best position to provide the right abstractions and interfaces. However, sometimes you'll find that the only API or technique for accomplishing what you need is synchronous. In these cases, the best choice is usually to move as much of the operation as possible to a separate thread in order to avoid bogging down the UI thread.

    BackgroundTaskManagerDemo on WPF

    .NET provides a number of ways to accomplish this, one of which is the BackgroundWorker class. BackgroundWorker tries to make asynchronous operations easy and - while it's not something I find myself using often - is well suited to the problem at hand! Specifically, all that's needed to avoid hanging the UI is to push the brunt of the work to the DoWork event and then add a bit of code to the RunWorkerCompleted event to take the result of that work and update the user interface accordingly.

    Aside: There are at least two high level approaches to updating the user interface after a long-running task: by updating the (bound) data and/or view model with the new values or by manipulating the UI directly to show those values. Both approaches work, but one of the things I find very elegant about the WPF and Silverlight architecture is how well suited it is to supporting rich data binding across an entire application. In particular, one of the great things that comes from an application which makes strong use of data binding is that there's typically very little need to directly manipulate the UI. Which means there's also a great deal less code that needs to be written to keep track of the exact state of the UI and make sure that overlapping updates don't conflict with each other.

    Because when you're managing all the UI yourself, you need to be sure that the results of a background task are still relevant by the time they're available (for example, that the user hasn't changed tasks, reconfigured the window, etc.). But when you're using data binding and updating the model the UI is bound to, the right thing "just happens" without any special effort on your part. If the new data is still relevant, it shows up exactly where it's supposed to - and if it's no longer applicable, it just doesn't show up. And if the user switches back to what they were doing, the new data is automatically used.

    Synchronization can be tricky to get right, so being able to avoid it entirely by taking advantage of WPF and Silverlight's rich data binding support is a big win!

     

    For the purposes of my source control repository viewer application, every call to the local source control client (ex: TF.exe or SVN.exe) is a synchronous call and is assumed to take a long time to complete. Granted, in the best case, a particular call might not need to contact the server and will complete quickly - but in the worst case, the call to the server ends up getting queued behind a backlog of other long-running commands and takes tens of seconds (or more!) to complete. I obviously don't want the interface of my application to block during this time - and furthermore I don't want to prevent the user from performing other operations just because a particular one is taking a long time to complete. For example, the user may have just requested a recursive history of the entire repository and is fully expecting for the command to take a while to complete. But there's no reason that should prevent him or her from simultaneously initiating a much simpler request to do something like view the list of pending changes. Therefore, every call to the source control client is run on a background thread and when the results come back they are used to update the model - at which point pervasive data binding automatically propagates those changes to the user interface!

    BackgroundTaskManagerDemo on Silverlight

    It's a fairly simple programming model, but a powerful one, and it leaves the application's UI snappy and responsive no matter what the user does, how fast the network is, or how overloaded the server may be. :)

     

    All long-running tasks are initiated via the BackgroundTaskManager class which is a thin wrapper around BackgroundWorker. The additional functionality this wrapper provides is tracking of the number of active background tasks (which is automatically reflected in the UI to help the user understand what's going on) and a slightly more convenient interface for specifying the code that runs in the background and upon completion. Here's what a typical call to BackgroundTaskManager from the UI thread looks like (from the sample application):

    BackgroundTaskManager.RunBackgroundTask(
        () =>
        {
            // Task function runs in the background
            return ComputeValue(TimeSpan.FromSeconds(durationInSeconds));
        },
        (result) =>
        {
            // Completion function runs on the UI thread
            UseValue(result);
            Log(string.Format(CultureInfo.CurrentCulture,
                "Finished asynchronous task taking {0} seconds", durationInSeconds));
        });
    

    The two methods that get passed to RunBackgroundTask can do anything you want - with one important restriction: the background task function must not do anything that directly manipulates the user interface. This is a platform limitation that anyone who's done much threading with WPF or Silverlight is probably familiar with: only the UI thread is allowed to manipulate the UI. So the pattern I follow is that the background task's only responsibility is to perform the time-consuming operation (ex: the call to the source control server). The result of this computation is handed directly to the completion function which has the responsibility of updating the UI either directly or through the model. It's an easy model to develop against and has proven pretty valuable so far.

    Here's the complete implementation of BackgroundTaskManager:

    /// <summary>
    /// Class that manages the execution of background tasks.
    /// </summary>
    public static class BackgroundTaskManager
    {
        /// <summary>
        /// Event invoked when a background task is started.
        /// </summary>
        [SuppressMessage("Microsoft.Usage", "CA2211:NonConstantFieldsShouldNotBeVisible",
            Justification = "Add/remove is thread-safe for events in .NET.")]
        public static EventHandler<EventArgs> BackgroundTaskStarted;
    
        /// <summary>
        /// Event invoked when a background task completes.
        /// </summary>
        [SuppressMessage("Microsoft.Usage", "CA2211:NonConstantFieldsShouldNotBeVisible",
            Justification = "Add/remove is thread-safe for events in .NET.")]
        public static EventHandler<EventArgs> BackgroundTaskCompleted;
    
        /// <summary>
        /// Runs a task function on a background thread; invokes a completion action on the main thread.
        /// </summary>
        /// <typeparam name="T">Type of task function result.</typeparam>
        /// <param name="taskFunc">Task function to be run on a background thread.</param>
        /// <param name="completionAction">Completion action to run on the primary thread.</param>
        public static void RunBackgroundTask<T>(Func<T> taskFunc, Action<T> completionAction)
        {
            // Create a BackgroundWorker instance
            var backgroundWorker = new BackgroundWorker();
    
            // Attach to its DoWork event to run the task function and capture the result
            backgroundWorker.DoWork += delegate(object sender, DoWorkEventArgs e)
            {
                e.Result = taskFunc();
            };
    
            // Attach to its RunWorkerCompleted event to run the completion action
            backgroundWorker.RunWorkerCompleted += delegate(object sender, RunWorkerCompletedEventArgs e)
            {
                // Invoke the BackgroundTaskCompleted event
                var backgroundTaskFinishedHandler = BackgroundTaskCompleted;
                if (null != backgroundTaskFinishedHandler)
                {
                    backgroundTaskFinishedHandler.Invoke(null, EventArgs.Empty);
                }
    
                // Call the completion action
                completionAction((T)e.Result);
            };
    
            // Invoke the BackgroundTaskStarted event
            var backgroundTaskStartedHandler = BackgroundTaskStarted;
            if (null != backgroundTaskStartedHandler)
            {
                backgroundTaskStartedHandler.Invoke(null, EventArgs.Empty);
            }
    
            // Run the BackgroundWorker asynchronously
            backgroundWorker.RunWorkerAsync();
        }
    }
    

     

    The sample application associated with this post lets you play around with synchronous and asynchronous operations of different durations. The red buttons on the left make synchronous calls that hang the entire application UI for the duration of the call (0, 2, or 10 seconds). (For fun, start a 10 second synchronous operation and then start clicking around the window - before long Windows will tag it as "Not responding" and gray the entire window out to reinforce the fact!) The green buttons on the right make asynchronous calls and leave the rest of the application completely responsive. You can queue up as many or as few simultaneous asynchronous calls as you'd like - they all run in parallel and without conflict. The status display shows the current number of pending asynchronous operations and helps to communicate what's going on in the background.

    [Click here to download the source code for BackgroundTaskManager and the WPF/Silverlight sample applications shown above.]

    One other thing that's worth pointing out is that while I originally wrote this code exclusively for use with WPF, it runs fine without changes on Silverlight as well (which you can see in the second screenshot above). In fact, the entire sample application is built from the exact same source code and XAML for both WPF and Silverlight (thanks to file linking)!

     

    Whatever platform you're on, if your application has things to do and those things take time, please do what you can to keep your application responsive by doing that work off the UI thread. BackgroundTaskManager makes this easy, but your users will appreciate your efforts no matter how you solve the problem! :)

  • Delay's Blog

    If you could have this... would you even *want* it? [Playing around with writing a flexible source control repository browser in WPF]

    • 8 Comments

    I've spent some of my time during the bus ride to/from the office experimenting with creating a flexible source control repository browser in WPF. I started this project as a learning exercise for myself and to address a particular need. As it stands today, I've managed to get far enough along that I've more-or-less accomplished my goals - and I'm not sure where I want to go with it next... :)

    RepositoryExplorer main view

    Thinking back a bit, this project started when I decided to see what it would be like to write my own standalone version control system (VCS) browser that worked with both Subversion and Team Foundation Server. You're probably wondering why (ignoring the educational benefits of such a thing) I would decide do this - well, the motivation actually came from two directions almost at once:

    1. I switched to Subversion for the source control provider I use for non-day-job projects. I'd picked Subversion for two main reasons: a great offline story and a dependency-free install. But my concern was that I couldn't seem to find a good, free UI-oriented repository viewer that wasn't implemented as a Windows shell extension. (Yes, I know about TortiseSVN and it looks like a fantastic tool - but I have this strong [and only semi-rational ;) ] objection to installing shell extensions.)
    2. I came to realize that although TFS's seamless integration with Visual Studio is beautiful when everything works, it gets kind of painful once there are any sort of problems with the server or network. I had the misfortune of hitting a pretty bad batch of such problems and grew quite tired of the entire Visual Studio application hanging whenever some little part of it decided to make a request to TFS and the response didn't come back immediately. :(

    So as long as I was going to set off down my own path, I outlined a set of goals to make my project the perfect source control browser (for me, at least!). Specifically, my tool would be:

    • Source control system agnostic (i.e., it should be easy to add support for ANY reasonable source control)
    • Completely asynchronous so that one or more long-running network tasks wouldn't hang the user interface
    • Network-friendly by making as few network calls as possible and caching results wherever possible
    • Simple and easy to use without sacrificing usefulness
    • Read-only (i.e., easily browsing change-logs with a friendly, point-and-click user interface is very much a goal; performing a check-in with multiple merge conflicts is not)
    • Small and self-contained (a single executable)
    • Require no installation
    • Have no external dependencies (once you have the tools to access your VCS of choice, you don't need anything else)
    • Harness the power of WPF
    RepositoryExplorer difference view

    Like I said, I've been working on this off-and-on for a while now and what I've got is starting to suit my needs pretty well. In fact, I've been using it nearly every day for a couple of weeks! Of course, I've got a big TODO list of enhancements and features I'd like to add, but from the point of view of an interesting learning exercise and a solution to my original problems, I've kind of accomplished what I set out to do. So I'd consider this project a success even if I kept it to myself and continued refining things gradually over the coming months...

    But I'm kind of curious if this is something of more general interest to the community:

    • Are there folks who might want to use something like this themselves?
    • Should I write a couple of blog posts on how I tackled some of the problems here? [In particular, I think the fully asynchronous model is kind of sweet - but then again, I'm a little biased... :) ]
    • Is there value in sharing the code for a small WPF application (as opposed to blogging small, one-off demonstrations of individual concepts)?
    • ...

    If you've got an opinion on the matter, please leave a comment below or send me feedback - I'd love to hear from people like you!

Page 1 of 1 (10 items)