The blog of dlaa.me

Posts tagged "WPF"

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

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

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

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

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!

Tags: WPF

Don't let the gotchas getcha [Adding the ScrollIntoViewCentered method to WPF's ListBox]

I recently found myself wanting a method I could call to bring one of the items in a WPF ListBox into view. In my scenario, the user would recognize the item when he/she saw it, so my problem wasn't about setting focus or rendering a highlight; it was just about bringing the item into view so it could be seen and manipulated. I started in the obvious place: the ListBox.ScrollIntoView method. Sure enough, a call to this method scrolled the item into view just like it was supposed to - and yet I wasn't completely satisfied... :)

You see, I didn't just want the item to be visible, I wanted it to be centered as well. I can't imagine I'm the first person to want to do this, but a quick web search didn't turn up anything relevant. I figured it'd be easy enough to write some code to implement the behavior myself, so I turned the problem over a bit in my head and was all giddy when I caught a gotcha prior to coding. :) Then, having decided on the implementation, I went ahead and typed up my ScrollIntoViewCentered extension method and gave it a try...

Darn... While my code worked as well as ScrollIntoView, it also wasn't any better. Specifically, the item wasn't centered like I wanted... :( A little poking around and some targeted experimentation turned up a second gotcha I'd missed. Once I'd addressed the second issue, the code worked fine and I continued with what I was originally doing.

ListBoxExtensionsDemo sample

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

 

You can find the complete code for ScrollIntoViewCentered below; here's a quick discussion of the two gotchas:

  • VirtualizingStackPanel.IsVirtualizing must be False. This is the gotcha I caught before coding. Basically, in order for the call to ItemContainerGenerator.ContainerFromItem to succeed, the container must have already been generated - but there may not be a container if virtualization is enabled! As you might expect, ScrollIntoView has this same problem - which it solves by calling the internal VirtualizingPanel.BringIndexIntoView method... :( Losing virtualization doesn't matter for my scenario, so ScrollIntoViewCentered simply requires that it be set. (FYI: I can think of a couple of ways to remove this restriction, but none of them is particularly elegant...)
  • ScrollViewer.CanContentScroll must be False. This is the gotcha I missed. It turns out that the same exact code I have that works fine when ScrollViewer.CanContentScroll is disabled fails when it's enabled. This highlights one of the differences between logical scrolling (item-based) and physical scrolling (pixel-based). I haven't thought about it enough to decide if I think it's a bug that the technique I use doesn't work properly with logical scrolling, but (like above) the difference is not relevant to my scenario. (FYI: Again, I can think of some ways to remove this restriction, but again they're kind of ugly.)

The ScrollIntoViewCentered method is hardly rocket science - but it is pretty handy. :) Thanks to the magic of extension methods, it looks just like the ListBox API it was inspired by and ends up being both easy to discover and easy to use.

I hope you like it!

 

/// <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.");
        Debug.Assert(!ScrollViewer.GetCanContentScroll(listBox),
            "ScrollViewer.GetCanContentScroll 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)
        {
            // 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

If they can build it, they will come... [Enabling anyone to compile WPF Charting from the Silverlight Charting sources!]

In yesterday's post I updated the unsupported public build of WPF Charting. The WPF version of the System.Windows.Controls.DataVisualization.Toolkit.dll assembly in that release is all that's needed to use Charting on WPF, so yesterday's update should to be enough to keep the WPF Charting early-adopters happy...

Well, except for the ones that want to be able to tinker with the code and build WPF Charting themselves. So today's post is for those of you who live life on the extreme edge - I'll enable you to build WPF Charting from the Silverlight Charting source code that ships in the March 09 release of the Silverlight Toolkit!

Okay, I've already said that we build WPF Charting from exactly the same sources as Silverlight Charting, so all that's really needed here is a new pair of CSPROJ/SLN files, right? Yes, for the most part, that's true - but there are a couple of other files involved... [There's always a catch, right? :) ]

The process of creating a WPF Charting "development environment" is pretty simple: We'll start with a clean copy of the Silverlight Toolkit source code for Silverlight 3. (Note: We could start from the Silverlight 2 Toolkit code just as easily, but if we did, then the Charting assembly would have the tiny, unnecessary limitation that it assumes Style properties are write-once. That's not the case on Silverlight 3 or WPF, so we'll create the WPF Charting environment from the Silverlight 3 sources.) If you haven't done so already, please download the Silverlight 3 Toolkit March 2009.msi from the Toolkit's CodePlex page and install it - making sure to leave the "Install source code" option enabled. Once the download is complete, extract the included Source code.zip file (you can find it in the Start Menu group for the Silverlight Toolkit) to an empty directory. Then extract the contents of Controls.DataVisualization.Toolkit.WPF.zip to the same directory (overwriting files when prompted).

Presto: You've got a Controls.DataVisualization.Toolkit.WPF directory containing a SLN you can open in Visual Studio 2008 and begin building immediately!

 

[Please click here to download Controls.DataVisualization.Toolkit.WPF.zip to start building WPF Charting.]

 

It's all very straightforward, but just in case I didn't explain the steps clearly, here's an example that goes through the whole process on the command line (you should be able to run the same basic commands on your machine):

C:\T>md WpfCharting

C:\T>cd WpfCharting

C:\T\WpfCharting>unzip "C:\Program Files\Microsoft SDKs\Silverlight\v3.0\Toolkit\March 2009\Source\Source code.zip"
Archive:  C:/Program Files/Microsoft SDKs/Silverlight/v3.0/Toolkit/March 2009/Source/Source code.zip
  ...
   creating: Controls.DataVisualization.Toolkit/
  inflating: Controls.DataVisualization.Toolkit/AggregatedObservableCollection.cs
   creating: Controls.DataVisualization.Toolkit/Charting/
  inflating: Controls.DataVisualization.Toolkit/Charting/AnimationSequence.cs
  ...

C:\T\WpfCharting>unzip ..\Controls.DataVisualization.Toolkit.WPF.zip
Archive:  ../Controls.DataVisualization.Toolkit.WPF.zip
replace Controls.DataVisualization.Toolkit/Charting/Series/DataPointSeries.cs? [y]es, [n]o, [A]ll, [N]one, [r]ename: y
  inflating: Controls.DataVisualization.Toolkit/Charting/Series/DataPointSeries.cs
replace Controls.DataVisualization.Toolkit/DependencyPropertyAnimationHelper.cs? [y]es, [n]o, [A]ll, [N]one, [r]ename: y
  inflating: Controls.DataVisualization.Toolkit/DependencyPropertyAnimationHelper.cs
replace Controls.DataVisualization.Toolkit/StoryboardQueue.cs? [y]es, [n]o, [A]ll, [N]one, [r]ename: y
  inflating: Controls.DataVisualization.Toolkit/StoryboardQueue.cs
   creating: Controls.DataVisualization.Toolkit.WPF/
  inflating: Controls.DataVisualization.Toolkit.WPF/Controls.DataVisualization.Toolkit.WPF.csproj
  inflating: Controls.DataVisualization.Toolkit.WPF/Controls.DataVisualization.Toolkit.WPF.sln
  inflating: Controls.DataVisualization.Toolkit.WPF/IEasingFunction.cs
   creating: Controls.DataVisualization.Toolkit.WPF/Properties/
  inflating: Controls.DataVisualization.Toolkit.WPF/Properties/AssemblyInfoWpf.cs
   creating: Controls.DataVisualization.Toolkit.WPF/Themes/
  inflating: Controls.DataVisualization.Toolkit.WPF/Themes/Generic.xaml
   creating: WpfToolkit/
  inflating: WpfToolkit/WPFToolkit.dll

C:\T\WpfCharting>Controls.DataVisualization.Toolkit.WPF\Controls.DataVisualization.Toolkit.WPF.sln

At this point, Visual Studio 2008 is open and you're ready to start WPF Charting development!

 

Okay, so what about those extra files - why are they necessary if WPF Charting is built from the same sources like I said it was? Well, first off, let's remember that Charting is made up of over 130 files - the extra files here are just a drop in the bucket! But I've got nothing to hide [ :) ], so we'll go through each of them to see why they're necessary:

Controls.DataVisualization.Toolkit.WPF.csproj
Controls.DataVisualization.Toolkit.WPF.sln
These two Visual Studio 2008 files create a solution/project that builds a WPF control assembly from the Charting source code. These are obviously necessary - and new for WPF Charting.
IEasingFunction.cs This file creates an empty definition of the IEasingFunction interface for code-level compatibility with Silverlight 3 where that interface is actually implemented. This empty interface definition avoids some #if/#endifs in the code, but otherwise does nothing. So there's nothing of interest here.
AssemblyInfoWpf.cs This file adds the WPF-specific, assembly-level ThemeInfoAttribute to the Charting assembly so that the styles in its Generic.xaml will be loaded properly. Nothing to see here, either.
Generic.xaml This file is an exact copy of the same file from the Silverlight 3 Charting code and is included here only because it's tricky to use file linking with Generic.xaml. Duplicating the file in this project is a nice, easy way to avoid the problem. Nope, no new code here!
DataPointSeries.cs
DependencyPropertyAnimationHelper.cs
StoryboardQueue.cs
These three files overwrite existing files from the Silverlight 3 Charting code and include the targeted fixes I made to Charting yesterday to work better on WPF. (Recall that my team has done absolutely no testing of WPF Charting.) There's actually nothing WPF-specific in any of these changes - and we would have included them in the Silverlight 3 Charting release if we'd known about them earlier. (In fact, they're already checked into our source control system and will be part of the next Charting release!) Yeah, there are some actual code changes here - but they're not specific to WPF!

And we're done! Yep, I really was serious when I said that WPF Charting builds from the same exact source code that Silverlight Charting builds from! :)

Thanks again for taking an interest in WPF Charting - I hope the ability to build it yourself makes it even more exciting!

Supporting the unsupported [Two fixes for the unofficial WPF Charting assembly!]

In my last post I described a few updates I made to my ChartBuilder sample/application/learning tool for the March 09 release of the Silverlight Toolkit. As usual, I posted the complete source code to ChartBuilder for interested parties to download and play around with. But this time around I also took the opportunity to showcase a WPF version of ChartBuilder that shows off an unofficial version of Silverlight Charting for WPF. Along with a WPF-based project file for ChartBuilder, I included a private build of the WPF Charting assembly that it compiles against.

I cautioned readers that WPF Charting was completely untested, but it was my not-so-secret hope that some of you would take the opportunity to play around with WPF Charting and give us some early feedback. And you did! :)

In addition to a known issue I describe in the previous post where dynamically added or removed DataPoints don't always complete their show/hide animations, kind reader abrien reported an additional problem causing a KeyNotFoundException during certain updates to a PieSeries's ItemsSource property. Then he/she went even further to create a CodePlex work item with a simple sample project demonstrating the problem! (Which is fantastic bug reporting, by the way!)

Now, before I go further, let me take this opportunity to reemphasize that neither of these problems is present in Silverlight Charting; they exist only in WPF Charting and are there because we've done absolutely no testing of WPF Charting due to a profound lack of time or resources. In fact, this is probably a good time to remind everyone of the warning I gave last time:

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

Okay, enough scare-mongering! :) The truth is that we'd love for people to be able to start experimenting with WPF Charting. So while the known show/hide problem was already bothering me, finding out about an exceptional condition arising from a fairly simple ChartBuilder scenario was downright unpleasant...

I spent some time over the weekend looking into both of these issues to see if I could identify the problem and perhaps fix it without too much fuss. And the good news is that I believe they're both fixed now! [After no small amount of frustration along the way... :) ] As it happens, both problems result from fairly subtle Storyboard behavior differences between Silverlight and WPF - though I'm happy to note the fixes I made are not platform specific. In other words, it's possible to write the exact same code on both platforms and have it work correctly on both platforms. It's just the code we started with wasn't that code... :(

I'm optimistic that both of these problems are behind us. And what's more, the fixes are already checked into our source control system for inclusion in the next release of Charting. But I don't want early adopters of WPF Charting to have to wait that long, so I've gone ahead and updated the WPF Charting assembly in the ChartBuilder download to include both of these fixes! 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 (file version 3.0.30324.1808) from the ChartBuilder download link below. And if you don't care about WPF Charting, then please don't worry about any of this - there are no other changes to the files in the download archive.

Our thanks go out to everyone who has given Silverlight Charting a try - and special thanks to our WPF Charting pioneers for their passion, feedback, and patience!

 

Please click here to download the complete ChartBuilder source code.

 

ChartBuilder on WPF

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

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

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

 

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

ChartBuilder on Silverlight

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

 

Release notes:

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

 

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

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

 

 

Oh, and one more thing... :)

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

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

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

 

ChartBuilder on WPF

 

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

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

 

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

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

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

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

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

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

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

WeakEventListener Sample Application

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

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

First, we'll load the SOS debugging extension:

0:012> .loadby sos coreclr

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Controls are like diapers: you don't want a leaky one [Implementing the WeakEvent pattern on Silverlight with the WeakEventListener class]

One of the nice things about developing on a platform that uses a garbage collecting memory manager (like Silverlight and WPF) is that the traditional concerns about memory leaks pretty much go away; most common types of memory leaks are impossible in a garbage collected environment. I say "most" and not "all" because there are still a few ways to leak memory - typically by creating a reference to an object and then "forgetting" about that reference. Sometimes this forgetfulness is simply an oversight on the part of the developer, but sometimes it is due to the reference being created in such a way that it's not obvious it even exists...

One of the easiest ways to create a "hidden" reference is by creating an event handler. Greg Schechter blogged in detail about the event handler situation back in 2004, and interested readers would do well to refer to his post for more details and some pretty diagrams. To summarize the issue briefly: when component A attaches an event handler to an event on component B, what happens behind the scenes is that component B creates a reference to component A - which it needs in order to provide a notification to A when the event is fired. This "backwards" reference is a bit subtle, but if you know what you're looking for, it's usually not too hard to spot.

What's more challenging is when a component you're using creates one of these backwards references in response to an action that's (superficially) completely unrelated to the event handling. As an exercise, see if you can spot the event handler here:

control.ItemsSource = collection;

Not so obvious, huh? :) The context needed to understand what's going on here is that an the control variable is some type that derives from ItemsControl (such as ListBox) and the collection variable is some type that derives from ObservableCollection<T>. It so happens that when an ItemsControl sees an assignment to its ItemsSource property of an object that implements INotifyCollectionChanged, it automatically adds a handler for that object's CollectionChanged event. And there's the potentially troublesome backward reference...

All it takes to start leaking memory at this point is an application with a long-lived reference to an ObservableCollection that gets passed to a short-lived ItemsControl that gets discarded. What can end up happening is that all of the references to the ItemsControl go away when it is discarded except for the one the ItemsControl itself created to listen to CollectionChanged events. Unless something is done to avoid this problem, the long-lived reference to the ObservableCollection will inadvertently keep the ItemsControl and all of its references alive considerably longer than the application developer expects. Furthermore, if the application is creating and discarding these ItemsControls on a fairly regular basis, it will quickly build up a sizable memory leak due to the accumulation of all the "discarded" ItemsControls.

As luck would have it, this situation was well understood and by the WPF team and that platform exposes APIs to implement what they call the WeakEvent pattern. WPF makes use of the WeakEvent pattern for its own ItemsControl, so the scenario described above isn't a problem in practice.

However, the WeakEvent pattern APIs don't exist in Silverlight 2 and the scenario above actually is a problem for ItemsControl and its subclasses as well as the DataGrid, and Charting's Series classes (each of which has a non-ItemsControl-based ItemsSource property). The good news is that Silverlight intends to fix this problem for ItemsControl-derived classes as part of a future release. Unfortunately, that doesn't help Charting - and besides I'd like to help customers avoid this problem on the current release...

So I got in touch with Silverlight developer Ivan Naranjo to see if his team had anything we could make use of and he kindly responded with something very much like the WeakEventListener class you see below. (For my part, I just tweaked things to address a few code- and source-analysis warnings and made a small change to avoid a possible area of confusion Ivan and I independently agreed on.) If you read Greg Schechter's post, the technique and implementation should look pretty familiar - WeakEventListener is a small, intermediary class that can be attached to an event so the resulting backwards reference affects only the extremely lightweight WeakEventListener and not the heavyweight class that created it. This avoids the costly "hidden" reference and allows the owning class to be garbage collected as soon as it is no longer in use. It was easy to add a WeakEventListener in Charting's Series.ItemsSource handler - and now our users don't have to worry about inadvertently leaking Chart instances!

To show just how easy it is to use WeakEventListener, I created a sample project that you can download and experiment with as you read the rest of this post. Here's what it looks like:

WeakEventListener Sample Application

The sample application includes two custom controls: LeakyControl (which has a typical ItemsSource implementation) and FixedControl (which uses WeakEventListener). To see the problem and solution in action, start the application, click the "Check Status" button to verify both controls are present, then click "Remove From UI" to discard both controls. If you click "Check Status" again at this point, you'll see that both controls are still present - and that's expected because the garbage collector hasn't needed to run and so nothing has been done to clean up. Now click "Garbage Collect" and then "Check Status" again. You'll see that LeakyControl is still present, but FixedControl is gone. Yay, WeakEventListener works! :)

Now, take things just a bit further and click "Clear ItemsSource", then "Garbage Collect", then "Check Status". We see that LeakyControl is gone as well! Why? Because by setting ItemsSource property of LeakyControl to null, we've explicitly told it we were done with the old collection and it knew enough to remove its event handler from that collection - thereby breaking the backwards reference and making itself eligible for clean-up during the next garbage collection. And, in fact, this is the workaround for controls that don't implement some form of the WeakEvent pattern and suffer from leaky behavior because of it: null-out the relevant property and hope they're kind enough to detach their event handlers in response. Depending on your application scenario, implementing this work around may be quite simple - or it may be almost impossible if your application has no way of knowing when some subcomponent has gotten into this situation - or no explicit knowledge of when affected controls are removed from the user interface. That's why it's nice when controls behave properly on their own - they work no matter what you do! :)

For an idea of how an easy it is to make use of WeakEventListener in a control, here is the relevant (leaky) code from LeakyControl:

// Change handler for the ItemsControl.ItemsSource-like DependencyProperty
private void OnItemsSourceChanged(IEnumerable oldValue, IEnumerable newValue)
{
    // See if the old value implements INotifyCollectionChanged
    var oldNotifyCollectionChanged = oldValue as INotifyCollectionChanged;
    if (null != oldNotifyCollectionChanged)
    {
        // It does; detach from the CollectionChanged event
        oldNotifyCollectionChanged.CollectionChanged -= OnCollectionChanged;
    }
    // See if the new value implements INotifyCollectionChanged
    var newNotifyCollectionChanged = newValue as INotifyCollectionChanged;
    if (null != newNotifyCollectionChanged)
    {
        // It does; attach to the CollectionChanged event
        newNotifyCollectionChanged.CollectionChanged += OnCollectionChanged;
    }
}

And here's what that same code looks like in FixedControl where WeakEventListener is used to avoid the backward reference problem:

// Change handler for the ItemsControl.ItemsSource-like DependencyProperty
private void OnItemsSourceChanged(IEnumerable oldValue, IEnumerable newValue)
{
    // See if the old value implements INotifyCollectionChanged
    var oldNotifyCollectionChanged = oldValue as INotifyCollectionChanged;
    if (null != oldNotifyCollectionChanged)
    {
        // It does; detach our WeakEventListener and clear our reference
        _weakEventListener.Detach();
        _weakEventListener = null;
    }
    // See if the new value implements INotifyCollectionChanged
    var newNotifyCollectionChanged = newValue as INotifyCollectionChanged;
    if (null != newNotifyCollectionChanged)
    {
        // It does; create a WeakEventListener, attach us to it, and add it to the event
        _weakEventListener = new WeakEventListener<FixedControl, object, NotifyCollectionChangedEventArgs>(this);
        _weakEventListener.OnEventAction = (instance, source, eventArgs) =>
            instance.OnCollectionChanged(source, eventArgs);
        _weakEventListener.OnDetachAction = (weakEventListener) =>
            newNotifyCollectionChanged.CollectionChanged -= weakEventListener.OnEvent;
        newNotifyCollectionChanged.CollectionChanged += _weakEventListener.OnEvent;
    }
}

Note that the basic structure of the code is unchanged - there's just a bit more bookkeeping involved in creating and hooking up the WeakEventListener. The first thing to look at is the bottom half of the method where a new collection is handled - a WeakEventListener instance is created and a couple of properties are set. The OnEventAction property specifies a strongly-typed (thanks to WeakEventListener being generic!) function that gets called when the event is fired - this maps to whatever method would otherwise be used to handle the event. The OnDetachAction property specifies a strongly-typed function that gets called when the WeakEventListener is detached from the event it's listening to - it simply removes the WeakEventListener's handler for the event. (Note that this function takes a WeakEventListener parameter which should be used to unhook the event so as to prevent creating a closure that itself contains a hidden reference that could cause a leak. And if that last sentence makes no sense, don't worry about it - just follow the pattern shown here and you'll be fine.) Now that everything's in place, the WeakEventListener is attached to event of interest. Going back to the top half of the function, all that needs to be done is to manually detach the WeakEventListener from the event. The example also sets the WeakEventListener reference to null - but this is done for clarity and debugging convenience and not because it's necessary.

That's all there is to it - WeakEventListener adds only a couple of lines of code and saves gobs of aggravation!

Aside: People who prefer not to use anonymous methods like I've done here are welcome to write explicit methods to do the same thing. It's a few more lines of code and a tiny bit more work, but if you go that route, you can create static methods which almost guarantee you won't inadvertently create a leaky closure. Either way you do it, WeakEventListener works the same.

Finally, here's the complete implementation of WeakEventListener for anyone who's curious how it works:

/// <summary>
/// Implements a weak event listener that allows the owner to be garbage
/// collected if its only remaining link is an event handler.
/// </summary>
/// <typeparam name="TInstance">Type of instance listening for the event.</typeparam>
/// <typeparam name="TSource">Type of source for the event.</typeparam>
/// <typeparam name="TEventArgs">Type of event arguments for the event.</typeparam>
internal class WeakEventListener<TInstance, TSource, TEventArgs> where TInstance : class
{
    /// <summary>
    /// WeakReference to the instance listening for the event.
    /// </summary>
    private WeakReference _weakInstance;

    /// <summary>
    /// Gets or sets the method to call when the event fires.
    /// </summary>
    public Action<TInstance, TSource, TEventArgs> OnEventAction { get; set; }

    /// <summary>
    /// Gets or sets the method to call when detaching from the event.
    /// </summary>
    public Action<WeakEventListener<TInstance, TSource, TEventArgs>> OnDetachAction { get; set; }

    /// <summary>
    /// Initializes a new instances of the WeakEventListener class.
    /// </summary>
    /// <param name="instance">Instance subscribing to the event.</param>
    public WeakEventListener(TInstance instance)
    {
        if (null == instance)
        {
            throw new ArgumentNullException("instance");
        }
        _weakInstance = new WeakReference(instance);
    }

    /// <summary>
    /// Handler for the subscribed event calls OnEventAction to handle it.
    /// </summary>
    /// <param name="source">Event source.</param>
    /// <param name="eventArgs">Event arguments.</param>
    public void OnEvent(TSource source, TEventArgs eventArgs)
    {
        TInstance target = (TInstance)_weakInstance.Target;
        if (null != target)
        {
            // Call registered action
            if (null != OnEventAction)
            {
                OnEventAction(target, source, eventArgs);
            }
        }
        else
        {
            // Detach from event
            Detach();
        }
    }

    /// <summary>
    /// Detaches from the subscribed event.
    /// </summary>
    public void Detach()
    {
        if (null != OnDetachAction)
        {
            OnDetachAction(this);
            OnDetachAction = null;
        }
    }
}