The blog of dlaa.me

Posts tagged "WPF"

A rose by any other name... [LayoutTransformControl on track to ship in the Silverlight Toolkit under the name LayoutTransformer!]

I'm a believer in the power of LayoutTransform - so much so that I wrote a control to graft this capability onto Silverlight 2 (which natively supports only the slightly less practical RenderTransform). I've posted extensively about what my LayoutTransformControl project is and how it works - you can read more via the following links: 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. I've been happy with how this project has gone and have gotten lots of good feedback from customers who are successfully making use of LayoutTransformControl in their own applications. So, yeah, I'm a fan... :)

LayoutTransformer Sample Application

That said, I haven't pushed to include this control in the Silverlight Toolkit - I figured things would sort themselves out if and when the need for LayoutTransform functionality became sufficiently compelling. As it happens, we saw this need arise for Silverlight Charting a few months ago, and the December 08 release of Charting includes a private (internal) copy of this control (we weren't prepared to officially expose it at the time). More recently, the issue came up again in the context of a different Toolkit control and in this case using a private implementation isn't practical.

Nobody really had time on the schedule to "productize" LayoutTransformControl, but it was becoming clear that our inability to take advantage of LayoutTransform functionality was limiting our ability to develop flexible controls. So I dedicated some time to making LayoutTransform an official part of the Toolkit and am happy to say that we're tentatively planning to include it in the next release of the Silverlight Toolkit under the name LayoutTransformer.

For a variety of reasons, I did this work in a private area of our source control tree. At some point I realized that by starting from the same Visual Studio solution/projects I'd already shared on my blog, I'd made it easy to give everyone a sneak peak of LayoutTransformer and benefit from some pre-release feedback. :) That said, if LayoutTransformer were simply a renamed LayoutTransformControl, I probably wouldn't bother - but there's an internal change of some significance that I would love to get early feedback on from folks who are already making use of LayoutTransformControl. To the extent that I've tested things, LayoutTransformer behaves the same as LayoutTransformControl - but if anyone finds otherwise, I'd definitely like to understand why!

LayoutTransformer Silverlight Test Cases

And as long as I'm publishing new bits, I thought it would be nice to include two new projects in the solution: a Silverlight-based testing project and a WPF-based one. These two test projects share the same source code, run cross-platform (just like LayoutTransformer), and get nearly complete code coverage from various flavors of 26 test cases. Even better, when the tests are run on WPF, they validate all scenarios against WPF's built-in LayoutTransform, too - thereby ensuring that LayoutTransformer matches the real LayoutTransform (for all the test scenarios, at least)!

I hope you like the new bits and would love to hear from anyone who tries LayoutTransformer out in their own project(s). Good or bad, your feedback is very much appreciated!

 

[Click here to download the LayoutTransformer source code, samples, and test projects.]

 

Notes:

  • The most obvious difference with the new bits is the name change from LayoutTransformControl to LayoutTransformer. Along with this renaming of the control itself, we've gone ahead and renamed a couple of other things for increased clarity. So migrating to LayoutTransformer involves a bit of manual effort, but it's important to emphasize that the changes are just to the names of things - the core behavior remains the same as it was before.
    Aside: I'll be the first to admit that "LayoutTransformer" isn't exactly my favorite name in the whole world - mostly because it always makes me think of other things. But all the other proposed names were rejected for valid reasons (ex: inconsistency with the platform, potential confusion, etc.). LayoutTransformer simply outlasted its competition and got the most votes when I put the issue to the team. Fortunately, it happens to be the shortest of the proposed names - and that helps me feel a little better! :)
  • The difference that concerns me just a bit is that LayoutTransformer derives from ContentControl whereas LayoutTransformControl has always derived from Control. We made this base class change because LayoutTransformer seemed to make more sense as a ContentControl given that it follows the same "put your content in me" model as the rest of the ContentControl subclasses (like Button). Furthermore, by being a ContentControl, it's now possible to put non-UI business objects inside a LayoutTransformer, set its ContentTemplate to a suitable DataTemplate, and have the transform automatically apply to the resulting UI elements. Additionally, it's nice that this switch simplifies the code ever so slightly. As I note above, my testing suggests there should be no negative impact - but if you run into any difficulty, please let me know!
  • Here's the relevant portion of my check-in notes for a list of the other changes we decided to make:
    Base class Control -> ContentControl
    Removed Child property and handlers; supplanted by Content property
    Transform property -> LayoutTransform
    TransformUpdated method -> ApplyLayoutTransform
    Create MatrixTransform in code instead of Template
    Added TemplatePartAttributes ("TransformRoot"/Grid, "Presenter"/ContentPresenter)
    Moved template to generic.xaml
    Sealed class
    Removed unnecessary #if for template definition
    
  • The WPF test project may fail to load if you don't have the unit testing feature of Visual Studio installed (which isn't supported by the Express editions as far as I know). If so, don't worry because that project's absence doesn't affect anything else. The good news is that the Silverlight test project is self-contained and should work for everyone!

 

Ambiguous contract is ambiguous [Minor bug fix for CRC32 and MD5Managed HashAlgorithm implementations]

Kind reader Gregor Zurowski contacted me over the weekend to let me know that he was using my free CRC-32 HashAlgorithm implementation in his project and he'd found that omitting the call to the Initialize method lead to incorrect hash values being returned. My first thought was, "Well, sure, calling the Initialize method before using the class is a necessary part of the HashAlgorithm contract - if you don't satisfy the contract then problems like this are entirely possible.". But Gregor went on to say that he got perfectly good results from the .NET Framework's MD5/SHA1 implementations without needing to call the Initialize method. I wondered if maybe the Framework classes had anticipated this scenario and allowed callers to skip the initialize call, but I was beginning to suspect that my understanding of the contract was wrong and that my implementation had a bug...

Sure enough, when I consulted the documentation for the Initialize method there was no mention of a contractual need to call it prior to doing anything else: "Initializes an implementation of the HashAlgorithm class.". I poked around a bit more trying to find some kind of justification for my beliefs, but I couldn't and was soon forced to conclude that I'd simply been mistaken by assuming that the .NET Framework followed the same pattern as COM or Win32. What's worse, I'd made the mistake twice: once for my CRC32 class and again with my MD5Managed class. :(

Well, as long as I was taking some time to validate my assumptions, I decided to check up on my assumption that a final call to TransformFinalBlock was required prior to fetching the hash value. Fortunately, I was right about this one and the TransformFinalBlock documentation notes that "You must call the TransformFinalBlock method after calling the TransformBlock method but before you retrieve the final hash value.". So while I'd clearly started out on the wrong foot, at least I didn't botch the landing, too. :)

The good news here is that this mistake is trivial to detect during application development (failing to call the Initialize method always yields the wrong hash value) and it's also trivial to work around: simply call the Initialize method after constructing a new instance of one of these HashAlgorithm subclasses. In fact, if your application already does this (as my ComputeFileHashes suite does), then you are completely unaffected by this issue.

Of course, I didn't want to perpetuate this error any further than I already had, so I corrected the code for the CRC32 and MD5Managed classes by performing the initialization as part of the object construction process. I then updated the following resources:

As I note above, there was no need to update the ComputeFileHashes application binaries because they were written under the assumption that a call to Initialize was required - and therefore are correct whether or not a call to Initialize actually is required.

Again, my thanks go out to Gregor for bringing this issue to my attention - I sincerely hope that no one else was affected by this mistake. While I do try to strive for perfection, I obviously need all the help I can get along the way! :)

Sometimes all it takes is a little encouragement [How to: Automatically update the widths of ListView columns]

I was working on a WPF project the other day and wanted an easy way to display data in a simple tabular format: a few columns (with headers) that would automatically size to fit their contents. The obvious choice was the ListView control and its GridView View which do exactly this. As you might expect, using this control was straightforward and it worked just like I wanted. Well, almost... There was one small catch: my ListView was hooked up to a data source that changed dynamically (via a Binding on its ItemsSource property) and I noticed that when the data source was updated, the widths of the columns were not automatically adjusted to fit the new content.

Here's a sample application I wrote for this post - notice how the text in the first ListView's "Value" column is truncated because the columns widths have not been updated:

ListViewColumnWidthAutoUpdate sample

This behavior was kind of annoying - and a brief web search showed that I'm hardly the first person to want to change it. There are a few different ways to tell the ListView to update its columns - the one I prefer looks something like this:

// Technique for updating column widths of a ListView's GridView manually
public static void UpdateColumnWidths(GridView gridView)
{
    // For each column...
    foreach (var column in gridView.Columns)
    {
        // If this is an "auto width" column...
        if (double.IsNaN(column.Width))
        {
            // Set its Width back to NaN to auto-size again
            column.Width = 0;
            column.Width = double.NaN;
        }
    }
}

Calling this method after a ListView's ItemsSource property changes is simple and does exactly what we want. So if your scenario is such that you always know exactly when your data changes, you can add a call to this method after that happens and stop reading now because your problem is already solved. :)

Okay, so if you're still reading, then your scenario is probably like mine: changes to the data source can occur without the application explicitly knowing about it. That last bit may not make a lot of sense until you realize that it's possible to implement a great deal of an application's functionality entirely in XAML. Specifically, it's quite easy to connect a ListView to the SelectedItem property of a ListBox so that changes to the selected item of the ListBox automatically re-populate the data in the ListView. Because this can be done entirely in XAML, these updates aren't automatically visible to the application.

The solution for the slightly more complicated scenario begins by realizing that it's necessary to know when the ItemsSource Binding updates. Fortunately, this is quite easy in WPF! :) By setting the NotifyOnTargetUpdated property of the ItemsSource Binding to true and handling the Binding.TargetUpdated attached event on the ListView, we have a fairly simple way of generating an event that can run a bit of code that calls the above method to update the column widths. What's more, this technique is fairly designer-friendly because it gives the designer complete freedom to set up such cross-control Bindings in their XAML without having to be intimately involved with the developer responsible for the application's code. Granted, the developer needs to implement the handler for the generated event, but that code is completely general and can be reused across multiple different ListViews.

The second ListView of the sample application uses this approach; notice how the column widths are correct in the image above. The XAML looks like this:

<ListView
    ItemsSource="{Binding Details, NotifyOnTargetUpdated=True}"
    Binding.TargetUpdated="ListViewTargetUpdated"
    ...

And the code for the event handler looks like this:

// Handler for the ListView's TargetUpdated event
private void ListViewTargetUpdated(object sender, DataTransferEventArgs e)
{
    // Get a reference to the ListView's GridView...
    var listView = sender as ListView;
    if (null != listView)
    {
        var gridView = listView.View as GridView;
        if (null != gridView)
        {
            // ... and update its column widths
            ListViewBehaviors.UpdateColumnWidths(gridView);
        }
    }
}

I'd arrived at the above solution and was going to consider the problem solved - and that's when Dr. WPF suggested I could use an attached behavior to encapsulate what I'd done into something that would be even simpler to use from XAML and wouldn't require the developer's involvement at all (aside from referencing the code that implements the attached behavior, of course). Attached behaviors are a powerful technique that allow the introduction of changes to the functionality of a control simply by setting an attached property on it. (If you're not familiar with attached behaviors, you can read more about them in this post by John Gossman or this article by Josh Smith.)

In the case of the attached behavior solution to this problem, we make use of the DependencyPropertyDescriptor class to attach a change handler to the ItemsSource property of the ListView - and then call the method above to actually update the widths of the columns. There end up being a few more lines of code with this solution because of what it takes to create an attached DependencyProperty and attach/remove a handler for it, but that code is completely self-contained and can live entirely in its own dedicated class (whereas the method used by the previous solution needs to be part of one of the application's classes). More importantly, the number of XAML edits drops to just one and it's no longer even necessary that a Binding changes the data source - even direct assignments to the ItemsSource property will do!

The third ListView of the simple application uses this approach; the XAML looks like this:

<ListView
    ItemsSource="{Binding Details}"
    local:ListViewBehaviors.IsAutoUpdatingColumnWidths="true"
    ...

And here's the complete implementation of the attached DependencyProperty:

// Class implementing handy behaviors for the ListView control
public static class ListViewBehaviors
{
    // Technique for updating column widths of a ListView's GridView manually
    public static void UpdateColumnWidths(GridView gridView)
    {
        // For each column...
        foreach (var column in gridView.Columns)
        {
            // If this is an "auto width" column...
            if (double.IsNaN(column.Width))
            {
                // Set its Width back to NaN to auto-size again
                column.Width = 0;
                column.Width = double.NaN;
            }
        }
    }

    // Definition of the IsAutoUpdatingColumnWidthsProperty attached DependencyProperty
    public static readonly DependencyProperty IsAutoUpdatingColumnWidthsProperty =
        DependencyProperty.RegisterAttached(
            "IsAutoUpdatingColumnWidths",
            typeof(bool),
            typeof(ListViewBehaviors),
            new UIPropertyMetadata(false, OnIsAutoUpdatingColumnWidthsChanged));

    // Get/set methods for the attached DependencyProperty
    [SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters",
        Justification = "Only applies to ListView instances.")]
    public static bool GetIsAutoUpdatingColumnWidths(ListView listView)
    {
        return (bool)listView.GetValue(IsAutoUpdatingColumnWidthsProperty);
    }
    [SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters",
        Justification = "Only applies to ListView instances.")]
    public static void SetIsAutoUpdatingColumnWidths(ListView listView, bool value)
    {
        listView.SetValue(IsAutoUpdatingColumnWidthsProperty, value);
    }

    // Change handler for the attached DependencyProperty
    private static void OnIsAutoUpdatingColumnWidthsChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
    {
        // Get the ListView instance and new bool value
        var listView = o as ListView;
        if ((null != listView) && (e.NewValue is bool))
        {
            // Get a descriptor for the ListView's ItemsSource property
            var descriptor = DependencyPropertyDescriptor.FromProperty(ListView.ItemsSourceProperty, typeof(ListView));
            if ((bool)e.NewValue)
            {
                // Enabling the feature, so add the change handler
                descriptor.AddValueChanged(listView, OnListViewItemsSourceValueChanged);
            }
            else
            {
                // Disabling the feature, so remove the change handler
                descriptor.RemoveValueChanged(listView, OnListViewItemsSourceValueChanged);
            }
        }
    }

    // Handler for changes to the ListView's ItemsSource updates the column widths
    private static void OnListViewItemsSourceValueChanged(object sender, EventArgs e)
    {
        // Get a reference to the ListView's GridView...
        var listView = sender as ListView;
        if (null != listView)
        {
            var gridView = listView.View as GridView;
            if (null != gridView)
            {
                // And update its column widths
                UpdateColumnWidths(gridView);
            }
        }
    }
}

 

[Click here to download the sample application demonstrating everything described here.]

 

What's neat is how something that started out as a minor annoyance turned into a great learning opportunity! I haven't needed to use DependencyPropertyDescriptor before now, but it's definitely something I'll keep in mind next time something like this comes up. And while I've made use of attached behaviors in the past, I didn't initially think to use one here - my thanks go out to Marlon Grech and Dr. WPF for encouraging me to do so. As it turns out, I like the attached behavior solution best of all for its simplicity, clarity, and separation of concerns. I've incorporated this change into my project and now my ListViews are behaving exactly how I want them to!

Tags: WPF

The proverbial "one line fix" [ComputeFileHashes works around a troublesome Silverlight-on-Mac issue]

When I achieved cross-platform parity by adding MD5 support to the Silverlight version of ComputeFileHashes, I thought I was done for a while. But then I got an email from a coworker reporting that the Silverlight version of ComputeFileHashes running on a Mac under Safari presented an "Add Files" dialog that did not actually let the user select any files. Ouch, that's no good...

I started investigating with a quick web search; the top hit for "OpenFileDialog Mac" showed that others had experienced similar problems and the Silverlight team confirmed a bug. So at least my application wasn't totally broken. :) I wanted to understand the scenario better, but I don't own a Mac (which is why this problem escaped my notice in the first place). Fortunately, I found one at work that I could borrow some cycles on and I wrote a simple test application to invoke the OpenFileDialog with a few different values for the Filter property. ComputeFileHashes was initially passing the value "All Files (*)|*" - effectively just "*" - which was intended to match all files. And, indeed, it does so in WPF and Silverlight/PC. However, on Silverlight/Mac that value seems to match no files. Someone suggested "*.*", but to me that matches all files with a '.' in their name and I didn't want to exclude files that don't happen to have an extension. So I tried "" instead, and that did exactly what I wanted on Silverlight/Mac and Silverlight/PC. I thought I'd found the solution - until I tried the new value on WPF and it caused an exception...

At this point I was tired of cross-platform trial-and-error, and I decided I was inviting trouble by passing any filter string at all! The default behavior of OpenFileDialog is to allow the selection of all files, so I wasn't really adding much value by passing a custom filter that did the same thing. Well, I was providing more explicit filter text in the drop-down of the dialog, but it wasn't worth the compatibility problems I was dealing with. So I removed the line of code that set the Filter property, recompiled, republished, and called it done. :)

The latest version of ComputeFileHashes is now 2009-01-30. I've updated all the binaries in order to avoid version number confusion, but the only real change here is the filter string and the improvement is only visible on Silverlight/Mac. (Note: I did not update the screenshots below, so the versions shown there are out of date.)

  • If you're using Silverlight to run ComputeFileHashes, you'll automatically get the new version next time you run ComputeFileHashes.
  • If you're using ClickOnce to run ComputeFileHashes, the application will automatically update itself after you run it a couple of times.
  • If you're using the WPF or command-line versions, you'll need to download the new binaries and update manually.

Please refer to the original release announcement for more information about supported platforms, source code, implementation, etc..

 

ClickOnce ComputeFileHashes

Click here or on the image below to run the Silverlight version of ComputeFileHashes in your browser.

Silverlight ComputeFileHashes

Click here or on the image below to download the command-line and WPF versions of ComputeFileHashes - along with the ClickOnce and Silverlight versions AND the complete source code for everything!

Command-line ComputeFileHashes

 

Seamless cross-platform support is a tricky matter that's usually best left to others who have the time and resources to do it right. I didn't realize I was introducing a platform dependency by specifying a filter string, but I was... and I got burned by it. That's why it's important to test an application on all the supported configurations: you never know what problem might show up where you least expect it! That said, I'm probably not going to run out and buy myself a Mac just because of this incident - so please accept my apologies in advance should I fall victim to a similar problem in the future. :)

Cross-platform feature parity: achieved [Silverlight version of ComputeFileHashes now includes MD5!]

I was very happy with last week's release of ComputeFileHashes supporting the command-line, WPF, Silverlight, *and* ClickOnce. Only one thing bothered me: the Silverlight version didn't do MD5 due to the lack of support for that type of checksum by Silverlight 2. Recall that I'd fairly happily implemented my own CRC-32 class because none of the platforms supported it. [Also, it was relatively simple and had a good reference implementation. :) ] But because MD5 is a more complex algorithm and was only missing on Silverlight, I was reluctant to do the same thing for MD5...

What I really wanted was a freely available, Silverlight compatible HashAlgorithm-based MD5 implementation that I could trivially drop into my code and use on Silverlight. So I was excited when kind reader (and teammate!) Jeff Wilcox left a comment pointing to something that sounded perfect for my needs. I told Jeff I'd add MD5 for Silverlight and mentally breathed a sigh of relief that all four of ComputeFileHashes's supported platforms would provide the same set of checksums.

As it turns out, after a bit of research I decided not to use that MD5 implementation. (I'll explain why in my next post.) However, now that I'd fully bought in to the idea of MD5 on Silverlight, I was reluctant to let it go... So I spent some time working on an alternate solution and developed something I'm quite happy with. So I'm able to release an update to ComputeFileHashes that offers MD5 support on Silverlight!

The latest version of ComputeFileHashes is now 2009-01-26. I've updated all the binaries in order to avoid version number confusion - but the only real change here is the addition of MD5 for Silverlight. (FYI, I only updated the screenshot of the Silverlight version below.)

  • If you're using Silverlight to run ComputeFileHashes, you'll automatically get the new version next time you run ComputeFileHashes.
  • If you're using ClickOnce to run ComputeFileHashes, the application will automatically update itself after you run it a couple of times.
  • If you're using the WPF or command-line versions, you'll need to download the new binaries and update manually.

Please refer to the original release announcement for more information about supported platforms, source code, implementation, etc..

 

ClickOnce ComputeFileHashes

Click here or on the image below to run the Silverlight version of ComputeFileHashes in your browser.

Silverlight ComputeFileHashes

Click here or on the image below to download the command-line and WPF versions of ComputeFileHashes - along with the ClickOnce and Silverlight versions AND the complete source code for everything!

Command-line ComputeFileHashes

 

I've said that "ComputeFileHashes is a simple tool intended to make verifying checksums easy for anyone.". And in some ways, I think the Silverlight version is the easiest option of all because there's no need to install it on your machine and it runs everywhere Silverlight 2 does (PC, Mac, (Linux soon!), Internet Explorer, Firefox, Safari, ...). So I'm really glad to add MD5 support to ComputeFileHashes for Silverlight - I hope you enjoy the new functionality!

Gratuitous platform support [ComputeFileHashes works on the command-line, on WPF, on Silverlight, and via ClickOnce!]

Last week, I released the ComputeFileHashes tool for calculating file checksums. (To read more about what checksums are and why they're useful, please refer to that post.) ComputeFileHashes is a fairly simple .NET command-line application for calculating the MD5, SHA-1, and CRC-32 hashes of one or more files. It takes advantage of the multi-processing capabilities of today's hardware to complete that task quickly - roughly on par with native-code implementations. ComputeFileHashes works quite well and I happily used it to verify the recently released Windows 7 Beta ISO images I'd downloaded.

Because not everybody is a fan of command-line tools, I thought it would be nice to use WPF to create a more user-friendly version of ComputeFileHashes. Once I'd done that, I knew it would be a trivial matter to publish the WPF version via ClickOnce to enable an absurdly easy install scenario. From there, porting to Silverlight would be straightforward and would offer an install-free, completely web-based solution with cross-platform (ex: PC/Mac), cross-browser (ex: IE/Firefox/Safari) appeal. What's more, because all of these platforms are built on .NET, so I expected to be able to take significant advantage of code sharing!

 

ClickOnce ComputeFileHashes

Click here or on the image below to run the Silverlight version of ComputeFileHashes in your browser.

Silverlight ComputeFileHashes

Click here or on the image below to download the command-line and WPF versions of ComputeFileHashes - along with the ClickOnce and Silverlight versions AND the complete source code for everything!

Command-line ComputeFileHashes

 

Implementation notes:

  • The command-line version of ComputeFileHashes is a standard .NET 2.0 application and should work pretty much everywhere. The Silverlight version requires Silverlight 2 which is tiny and can be completely installed in less than a minute start-to-finish. The WPF/ClickOnce versions are a little more advanced and require .NET 3.5 SP1 (conveniently pre-installed on all Windows 7 machines!). If you don't already have .NET 3.5 SP1 (and you may not because Windows Update still doesn't seem to offer it), you can get the .NET 3.5 SP1 installer from here. Unfortunately, the only indication of .NET 3.5 SP1 not being installed seems to be an application crash immediately after starting the stand-alone WPF version. :( Fortunately, the ClickOnce version knows about the .NET 3.5 SP1 prerequisite and should offer to install it automatically if it's not already present.
  • None of the computation or file processing is performed on the main user interface thread under WPF or Silverlight, so ComputeFileHashes remains responsive even when working on a large file. Additional files can be queued for processing or the application/browser can be closed without the user having to wait.
  • As I hoped, I was able to achieve a very high degree of code sharing. By refactoring the original ComputeFileHashes code slightly, I pulled the core implementation out into a common class/file that everything shares. Then I put nearly all of the user interface functionality into another class/file that the WPF and Silverlight implementations both share. The XAML for the WPF and Silverlight versions is separate, but very similar. (There are enough slight differences between the two versions that I deliberately did not attempt to share the same XAML file.)
  • The source code structure looks like this:
    ComputeFileHashesCore.cs Core implementation of the file hashing code shared by all implementations. Makes use of multiple threads to perform hash calculations in parallel.
    ComputeFileHashesUI.cs User interface code shared by the WPF and Silverlight implementations. Makes use of a worker thread to push all computation off of the user interface thread and keep the application responsive. Defers to ComputeFileHashesCore for hashing functionality.
    CRC32.cs
    WaitingRoom.cs
    Custom CRC-32 HashAlgorithm implementation and synchronization object shared by all implementations.
    HashFileInfo.cs
    BlockingQueue.cs
    Data object for tracking state and custom Queue subclass that are shared by the WPF and Silverlight implementations.
    ComputeFileHashesCL\
       ComputeFileHashesCL.cs
    Command-line interface for handling arguments and displaying progress. Defers to ComputeFileHashesCore for hashing functionality.
    ComputeFileHashesWPF\
       Window1.xaml
       Window1.xaml.cs
    WPF definition of the application window. Defers to ComputeFileHashesUI for nearly all functionality.
    ComputeFileHashesSL\
       Page.xaml
       Page.xaml.cs
    Silverlight definition of the application window. Defers to ComputeFileHashesUI for nearly all functionality.
  • A look at the screenshots above reveals a few differences between the WPF and Silverlight implementations:
    • The DataGrids look different. On WPF, ComputeFileHashes makes use of the WPFToolkit's DataGrid; on Silverlight it uses the DataGrid in the SDK. The two are very similar to use from a developer perspective, but they draw themselves differently and have some slightly user-level functionality changes due to platform differences. This was actually my first experience with either DataGrid and I was happy to find that they both worked the same - and pretty much the way I expected them to!
    • The Silverlight version does not calculate the MD5 hash. This is because Silverlight's .NET doesn't implement the MD5 HashAlgorithm subclass while the desktop's .NET does. MD5 is not trivial to write, so I wasn't too interested in developing my own implementation like I did for CRC-32 (which isn't supported on any .NET platform).
    • The Silverlight version includes a "Details" column that's not present on the WPF version. On WPF, it's trivial to create a ToolTip for a DataGrid column, but on Silverlight the ToolTipService class must be used and my attempts to set its attached property with a Style were met with ... resistance. So if an exception is thrown when processing a file, the WPF version will show the exception text in a ToolTip while the Silverlight version shows it in the Details column.
    • The Silverlight version does not support drag-and-drop from the Windows Explorer. Running within a browser imposes certain limitations on Silverlight; an inability to integrate quite as richly with the operating system is one of them.
    • The hyperlinks aren't quite the same. Silverlight ships with HyperlinkButton which is exactly the right control for the job here. WPF doesn't have that control, so the similar Hyperlink control is made to behave as desired with a small bit of code.

 

In the original release announcement, I wrote that "ComputeFileHashes is a simple tool intended to make verifying checksums easy for anyone.". Well, that's still the case - and adding support for WPF, ClickOnce, and Silverlight should make it even easier for everyone to use. Just decide what kind of user interface you prefer, and start using that version of ComputeFileHashes for all your checksumming needs! :)

Expanded access to Silverlight 2's generic.xaml resources [SilverlightDefaultStyleBrowser updated for better compatibility!]

Kind reader (and fellow Silverlight Charting blogger!) Pete Brown contacted me recently to report that my SilverlightDefaultStyleBrowser application (background reading available here, here, here, here, and here) didn't seem to be working for assemblies from the Silverlight Toolkit or Telerik's RadControls for Silverlight. Specifically, he found that SilverlightDefaultStyleBrowser would work successfully with the controls in the Silverlight runtime and in the Silverlight SDK - but when he used the "Add Assembly" button to add assemblies from the Toolkit or RadControls, no new controls appeared in the list. This was unexpected and I started investigating...

Silverlight Toolkit: This scenario was particularly troubling because I'm on the Toolkit team and I know it's not doing anything weird that should break SilverlightDefaultStyleBrowser. So I stepped through the code for parsing generic.xaml and discovered that the root ResourceDictionary wasn't getting loaded. A bit more debugging revealed that this was because the Silverlight Toolkit's generic.xaml uses a different XAML namespace than SilverlightDefaultStyleBrowser was looking for (and than nearly every other Silverlight assembly I've seen). But the Toolkit is not at fault here - it turns out that Silverlight supports two different XAML namespaces! So I added support for the second namespace to SilverlightDefaultStyleBrowser, and now it happily parses assemblies from the Silverlight Toolkit. :)

RadControls: I'd assumed the issue here was the same and expected the RadControls to "just work" now that I'd added support for the second XAML namespace - but I was wrong. :( So I stepped through SilverlightDefaultStyleBrowser's generic.xaml parsing code again (recall that generic.xaml is a public entry point for Silverlight assemblies) and discovered that SilverlightDefaultStyleBrowser was finding and parsing the RadControls generic.xaml just fine all along - except that there weren't any Style elements in it. So SilverlightDefaultStyleBrowser's behavior was "correct" in the first place! What's different here is that the Telerik assemblies expose a generic.xaml containing references to custom elements that work at run time (when generic.xaml is parsed and executed by Silverlight), but which do not work with the simple XML-level parsing that SilverlightDefaultStyleBrowser performs. I'm guessing it would be fairly straightforward to modify SilverlightDefaultStyleBrowser to successfully expose the Telerik Styles, but I'm not going to add that custom code until/unless I hear from someone at Telerik that this is something they're okay with. So (for now, at least), SilverlightDefaultStyleBrowser doesn't find any Styles in the RadControls assemblies; that behavior is correct and "by design".

Having investigated the problem report and fixed what I could, I updated the public SilverlightDefaultStyleBrowser application and the downloadable source code. And started writing this post! :)

 

The version number of SilverlightDefaultStyleBrowser appears in the window's title and the latest release number is 1.0.3268.34946. (Note: I haven't updated the screen shot below which shows the introductory version number.) If installed via ClickOnce, the application should automatically prompt you to upgrade when it detects the update (which typically happens after running the application once or twice). If you're using the standalone EXE, you'll need to update manually.

SilverlightDefaultStyleBrowser Application

Click here or on the image above to install SilverlightDefaultStyleBrowser via ClickOnce with automatic updating.

Click here to download the standalone SilverlightDefaultStyleBrowser executable and source code in a ZIP file.

 

SilverlightDefaultStyleBrowser was written to do one thing and to do it simply. As is often the case when trying to duplicate existing behavior, there tend to be a few surprises along the way where things turn out not to work quite as expected. This was one of those surprises and I'm glad for the opportunity to fix this and make SilverlightDefaultStyleBrowser just a little more useful for everyone!

Having problems with layout? Switch to Plan B! [LayoutTransformControl scenarios for WPF]

When I first wrote about adding full LayoutTransform fidelity to Silverlight with my LayoutTransformControl project, I mentioned that I'd found two scenarios where LayoutTransformControl's behavior differed from WPF's LayoutTransform. (Background reading for LayoutTransformControl: Motivation and introduction for Beta 1, Significant enhancements and update for Beta 2, Update for RTW, Fixes for two edge case bugs) These two differences were intentional because it seemed to me that the corresponding WPF behavior was incorrect. Now you can judge for yourself. :)

The sample WPF application shown here (note: complete source code is attached to this post) demonstrates two variants of the problematic scenarios; one rendered by WPF's LayoutTransform and the other by LayoutTransformControl. The top four cells demonstrate the first scenario and the bottom four cells demonstrate the second. Cells on the left show a variant that both implementations agree on while cells on the right show a small modification that demonstrates conflicting behavior. I've shaded the problematic sections red to help identify them. Here's how it looks:

WPF LayoutTransform issue demonstration

The XAML for top scenario's red quadrant is this:

<Grid Background="Orange">
    <Grid.LayoutTransform>
        <SkewTransform AngleX="-20"/>
    </Grid.LayoutTransform>
    <Grid MinHeight="75" MinWidth="75"/>
</Grid>

It's a simple skew on a free-sized Grid with arbitrary content (for simplicity, the content is another Grid with MinWidth/MinHeight, but this could be a Button or any other typical content). The behavior for positive skew angles makes sense to me, but the behavior for negative angles seems inconsistent and causes undesirable clipping of the content (which is really obvious when applied to a Button). The corresponding XAML for LayoutTransformControl's "correct" rendering is:

<l:LayoutTransformControl>
    <l:LayoutTransformControl.Transform>
        <SkewTransform AngleX="-20"/>
    </l:LayoutTransformControl.Transform>
    <Grid Background="Orange">
        <Grid MinHeight="75" MinWidth="75"/>
    </Grid>
</l:LayoutTransformControl>

The XAML for bottom scenario's red quadrant is this:

<Grid Width="75">
    <Grid Background="Orange">
        <Grid.LayoutTransform>
            <RotateTransform Angle="90"/>
        </Grid.LayoutTransform>
        <Grid MinHeight="125" MinWidth="125"/>
    </Grid>
</Grid>

It's a width-constrained, free-sized Grid with arbitrary content. The behavior for Angle=0 (or 180) makes sense to me, but the behavior for Angle=90 (or 270) does not; at this angle the content should be vertically unbounded and should stretch to the full 125 pixels of the inner Grid. Changing the nature of the outer constraint from width to height exhibits the same "doesn't stretch" behavior at Angle=90 (or 270). The corresponding XAML for LayoutTransformControl's "correct" rendering is:

<Grid Width="75">
    <l:LayoutTransformControl>
        <l:LayoutTransformControl.Transform>
            <RotateTransform Angle="90"/>
        </l:LayoutTransformControl.Transform>
        <Grid Background="Orange">
            <Grid MinHeight="125" MinWidth="125"/>
        </Grid>
    </l:LayoutTransformControl>
</Grid>

I reported both of these issues to the WPF team when I found them, but unfortunately they weren't able to address them for the .NET 3.5 SP1 release. Although it might put LayoutTransformControl out of a job, I'm optimistic that both will be fixed in a future update of WPF. :) For the time being, though, I humbly suggest giving LayoutTransformControl a try if you encounter unexpected behavior like this. It's probably the quickest, easiest patch you'll find!

[WpfLayoutTransformIssues.zip]

An unexceptional layout improvement [Two LayoutTransformControl fixes for Silverlight 2!]

I'd almost finished patting myself on the back for managing to implement WPF's LayoutTransform on Silverlight using just the RenderTransform available on that platform. (Background reading for LayoutTransformControl: Motivation and introduction for Beta 1, Significant enhancements and update for Beta 2, Update for RTW) Yes, everything was peachy - until I was contacted by kind reader Matthew Serbinski with a report of an InvalidOperationException being thrown by Silverlight when using LayoutTransformControl with a ScaleTransform with ScaleY=0... :(

You've probably already recognized ScaleY=0 as something of an edge case for layout: a value which collapses everything into nothingness. I looked into how WPF's LayoutTransform code handled this situation and discovered that it specifically detected circumstances corresponding to a transformation matrix without an inverse - and skipped performing the usual layout computations. My LayoutTransformControl implementation didn't look for this special case, ended up violating one of the rules of the layout system, and triggered the reported exception.

So I added a little bit of code to handle such input the same way WPF does. And while I was at it, I checked to see if maybe there were other special cases that LayoutTransformControl wasn't handling properly... Sure enough, I found one other scenario: that of needing to layout within an container having no width or height. In this case, LayoutTransformControl's behavior wasn't wrong enough to cause an exception (or any visible problem I noticed), but I made a similar tweak for consistency with WPF.

Changes in place, I modified the LayoutTransformControl sample application, its Silverlight test framework, and its WPF test framework to allow setting both ScaleX and ScaleY to 0. (I'd formerly limited ScaleX/ScaleY to positive values which is why I didn't realize there was a problem myself.) Then I spent some time playing around with the test apps: trying all kinds of things and looking for any other anomalous behavior. Nothing turned up, so I updated the LayoutTransformControl source code download and started writing this post... :)

LayoutTransformControl Sample Application

Changes to code always involve a certain amount of risk that a regression will be introduced. Fortunately, the changes here are small, self-contained, and easy to test - so I'm optimistic they won't cause problems for those of you already using LayoutTransformControl in your projects. Of course, if there are any new problems - or existing ones I don't know about yet! - please let me know and I'll look into them as quickly as I can.

Thank you for your help - happy LayoutTransform-ing!

Maintaining pretenses with the layout system [LayoutTransform functionality updated for Silverlight 2!]

In the introductory post for LayoutTransformControl and the feature-enhancing follow-up, I gave a variety of examples to convey the need for LayoutTransform. I also noted that Silverlight 2 supports only RenderTransform, but went on to demonstrate how it was possible to use RenderTransform to get LayoutTransform behavior. I'm biased, but I happen think LayoutTransform is a pretty fundamental part of application design - and the feedback I've gotten about LayoutTransformControl suggests that at least some of you agree with me. :)

Soon after the Silverlight 2 release candidate became available, a customer contacted me to ask about updating LayoutTransformControl. I've spent some time doing just that and am happy to share a version that works well on the latest Silverlight 2 bits. The complete implementation - along with demo and test applications - can be found in LayoutTransformControl.cs in the ZIP file attached to this post.

The sample application works the same as it did before (with the updated look-and-feel of the new bits):

LayoutTransformControl Sample Application

It remains my belief that LayoutTransformControl for Silverlight 2 is just as powerful as WPF's LayoutTransform. Your application can take advantage of complete LayoutTransform functionality today - even though the Silverlight platform doesn't support LayoutTransform!

Notes:

  • Unfortunately, Silverlight does not yet behave the same as WPF with regard to automatically calling the property changed handler of a Transform-typed DependencyProperty when any of that Transform's members change. I explain how clean this makes things in my previous post, and outline how I worked around the issue by introducing a set of custom "Ltc" transforms that automatically notify their LayoutTransformControl parent of any changes. But with the release of the Silverlight 2 RC, the Transform class can no longer be derived from - which forces the removal of the "Ltc" Transforms and completely invalidates my workaround. :(
  • There are now just two scenarios for the Transforms provided to a LayoutTransformControl:
    1. The Transforms are static - so change notifications are unnecessary and there's no need to worry about this. (As before, the Motivation project demonstrates this scenario.)
    2. The Transforms are dynamic and it is now necessary to make a call to LayoutTransformControl.TransformUpdated() whenever they change so that LayoutTransformControl can find out and update its rendering. (The Sample project has been changed to demonstrate this scenario.)
  • Silverlight 2 has introduced a feature known as "layout rounding" - it's conceptually very similar to WPF's SnapsToDevicePixels property. (I can't find documentation of the new UIElement.UseLayoutRounding property quite yet, but it's discussed briefly in the Breaking Changes document.) Unlike WPF's SnapsToDevicePixels which takes effect silently (as I understand it), Silverlight's UseLayoutRounding (which defaults to True) does not. Specifically, UseLayoutRounding causes some of the intermediate values used during the measure/arrange pass to change while LayoutTransformControl is busy working its magic. Left alone, these unexpected changes appear to LayoutTransformControl as various special-cases of the layout system and cause incorrect layout for certain special angles/scales/etc. based on whether things get rounded up or down. So LayoutTransformControl disables UseLayoutRounding for itself (which avoids bad layout during scaling) and that automatically inherits to its templated Grid (which avoids bad layout during rotation). Consequently, LayoutTransformControl's rendering behavior remains correct at all times.
  • If UseLayoutRounding is necessary for some element within a LayoutTransformControl, simply set it back to True on that element and things should continue to work as desired. But note that LayoutTransformControl does look at the outermost child element (the value of its .Child property) for some of its calculations, so UseLayoutRounding should not be toggled on that element. If necessary, wrap that element in a container (ex: Grid) and it should then be fine to set UseLayoutRounding on it.

My thanks to everyone who has expressed interest in LayoutTransformControl or is actively using it in their projects! I hope this update continues to serve you well as Silverlight 2 approaches final release.

[LayoutTransformControl.zip]