The blog of dlaa.me

Posts tagged "WPF"

Preserving access to Silverlight 2's generic.xaml resources [SilverlightDefaultStyleBrowser updated for the Silverlight 2 RC!]

Last night the Silverlight team released Silverlight 2 Release Candidate 0! The new bits include a minor change with a big impact on SilverlightDefaultStyleBrowser (background reading available here, here, here, and here): the path for default styles has changed from generic.xaml to themes/generic.xaml. So SilverlightDefaultStyleBrowser ends up looking in the wrong place for the RC assemblies and doesn't find any styles. That's not very helpful, so I've just updated SilverlightDefaultStyleBrowser to extract styles from both of these paths (which will continue to work with Beta bits and will also work with the new RC bits).

The version number of SilverlightDefaultStyleBrowser appears in the window's title and the latest release number is 1.0.3191.14705. (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 app 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.

The RC bits of Silverlight introduce some neat new controls: ComboBox, PasswordBox, and ProgressBar. Go play with them now to see how they work - then use the new SilverlightDefaultStyleBrowser to see how they're styled!

Make life a little easier for designers [WPF Triggers with a little less XAML and a little less code]

When I was writing my video frame grabbing sample, I came up with a potentially useful technique for maintaining the developer/designer separation that's so sought after in the WPF (and Silverlight) world. I'm sure something similar has been discussed before, but I hadn't come across it (and couldn't find anything like it with a quick search), so I thought I'd describe it here for posterity.

Background

The high-level goal is to maintain a separation between how an application works (developer) and how it looks (designer). The conventional approach to this is to follow some kind of model/view pattern where the application exposes various properties that represent its state and the interface binds to those properties to convey that state to the user. In WPF, this is typically done in XAML via Binding or TemplateBinding. These two constructs are very powerful and work quite well. However, it seemed to me that there might be a slightly better way...

The Conventional Way

In my scenario, the application exposes a Boolean DependencyProperty "Processing" which is normally False, but flips to True whenever a video file is being processed. The UI exposes an "Open File" Button which is normally enabled, but should flip to disabled whenever a video file is being processed.

The first instinct is to use a Trigger to apply the style changes to the Button. However, there's a catch - the Trigger needs to be defined on Button itself because the Button properties are being changed, yet the actual Trigger property (the Processing property) is defined on the Window object. I didn't see an obvious way to point the Trigger at the Window from the Button, so I moved on to the next idea which was to use a Binding to attach the Button's IsEnabled property to the Window's Processing property. Depending on whether you prefer ElementName or RelativeSource, the XAML looks like this:

<Button
    IsEnabled="{Binding Processing, ElementName=Window}"
    Content="..."/>
<Button
    IsEnabled="{Binding Processing, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type l:Window1}}}"
    Content="..."/>

Both approaches work, but not perfectly: the IsEnabled state of the Button is backwards because we've got IsEnabled == Processing when what we really want is IsEnabled == !Processing. Fortunately, Bindings support Converters and I've already written about the power of IValueConverter. So the designer goes to the developer and asks for a Converter that will invert the state of a Boolean variable and the developer writes the following code:

/// <summary>
/// Simplest implementation of a Converter to invert the state of a bool.
/// </summary>
/// <remarks>
/// Needs parameter validation and error handling.
/// </remarks>
public class BooleanInversionConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return !((bool)value);
    }
    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

Now the designer adds an instance of this Converter to the resources of the application:

    <Application.Resources>
        <l:BooleanInversionConverter x:Key="BooleanInversionConverter"/>
    </Application.Resources>

And uses it to achieve the desired effect:

<Button
    IsEnabled="{Binding Processing, ElementName=Window, Converter={StaticResource BooleanInversionConverter}}"
    Content="..."/>
<Button
    IsEnabled="{Binding Processing, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type l:Window1}}, Converter={StaticResource BooleanInversionConverter}}"
    Content="..."/>

And the scenario works. But then the designer decides that the text of the Button should also be italic when it's disabled... And that requires the designer to go back to the developer who then needs to write a BooleanToFontStyleItalicConverter. Or maybe the designer attaches the Binding to the Button's Style property and gets the developer to write some kind of Style toggling Converter. Sheesh, what started out as a simple scenario has gotten annoyingly complicated and involves significant developer/designer coordination to accomplish something the designer should have been able to do independently in the first place.

A Different Way

So that's when I came up with a different idea that would let me use a Trigger after all: an attached DependencyProperty that inherits! Simply by changing the registration of the Processing property from Register to RegisterAttached and adding the FrameworkPropertyMetadataOptions.Inherits flag, we end up with a property on Window that looks to the Button just like a property defined on the Button itself. And suddenly the original idea to use a Trigger becomes possible - and even preferable! - because there's no longer a need for Bindings, Converters, or any of the complication above. Now the XAML looks just like it should and the designer can change things with complete autonomy:

<Button Content="...">
    <Button.Style>
        <Style TargetType="{x:Type Button}">
            <Style.Triggers>
                <Trigger Property="l:Window1.Processing" Value="True">
                    <Setter Property="IsEnabled" Value="False"/>
                    <Setter Property="FontStyle" Value="Italic"/>
                </Trigger>
            </Style.Triggers>
        </Style>
    </Button.Style>
</Button>

Much better.

Another Different Way

I discussed this scenario with my teammate Beatriz Costa briefly (she's one of the WPF data binding experts) and she independently suggested a very similar solution to what I came up with: DataTrigger. Using a DataTrigger instead of a Trigger has the nice benefit that the DependencyProperty no longer needs to be made attached/inheritable; any standard DependencyProperty will do. However, DataTrigger uses a Binding instead of a Property, so we end up back in the world of long-ish ElementName/RelativeSource markup syntax. Here's the XAML for the ElementName form:

<Button Content="...">
    <Button.Style>
        <Style TargetType="{x:Type Button}">
            <Style.Triggers>
                <DataTrigger Binding="{Binding Processing, ElementName=Window}" Value="True">
                    <Setter Property="IsEnabled" Value="False"/>
                    <Setter Property="FontStyle" Value="Italic"/>
                </DataTrigger>
            </Style.Triggers>
        </Style>
    </Button.Style>
</Button>

Just as better - pick whichever feels more comfortable. :)

Summary

Achieving a good developer/designer separation is good for a lot of reasons: flexibility, productivity, encapsulation, etc.. Unfortunately, it's not always as easy as we'd like... So if the approaches outlined here can help move us all just a little bit closer to that goal, then this post has served its purpose!

Tags: WPF

Video frame grabbing made easy [How to: Quickly capture multiple video frames with WPF]

When I transfer clips from my digital camera to my computer, I briefly check that the video transferred successfully. For images, this is usually a matter of opening a few of them - for video files I usually load them in Media Player and seek to a couple of different points. Mine is hardly a fail proof system, but it's fairly quick and it makes me feel a little safer about deleting the originals from the camera. :) One day I got to thinking that it would be pretty easy to automatically generate a set of video thumbnails instead of seeking around manually...

So I did some research into video frame capture with WPF and found that it boiled down to a few main ideas:

  1. All it takes to capture a frame of video is an instance of MediaPlayer and a call to RenderTargetBitmap
  2. Setting the ScrubbingEnabled property tells MediaPlayer to update its content dynamically when changes to the Position property are made
  3. The video updates are asynchronous, so it's necessary to delay the capture a short while after changing the Position property in order to capture the new frame
  4. Performing a capture in response to the MediaOpened event is a handy way to capture the first frame reliably
  5. However, nothing I found demonstrated a good way to capture additional frames reliably...

Because of the asynchronous nature of the video frame updates, attempting to capture multiple frames in rapid succession (ex: seek, capture, seek, capture, ...) resulted in duplicate frames every time the capture occurred too soon for the frame's content to update (which was most of the time!). This was clearly unsuitable for my scenario because I wanted my application to generate its thumbnails as quickly as possible. After a bit of experimentation and playing around, I arrived at a technique that seems to work quite reliably in practice.

I wrote a simple WPF application to show how it works - here's an image of VideoThumbnailer displaying a summary of a video of someone tubing in the snow:

VideoThumbnailer sample frame capture application

(The complete source code to VideoThumbnailer is available as an attachment to this post.)

The technique itself is fairly simple, if not entirely as straightforward as it would be if changes to the Position property were synchronous:

  1. Create a MediaPlayer instance and hook the MediaOpened and Changed events
  2. Set ScrubbingEnabled and open the media file
  3. When the MediaOpened handler fires, capture the initial frame and set Position to the first location of interest
  4. When the Changed handler fires, capture the current frame
  5. If that frame is new/different than before, seek to the next position of interest and repeat as necessary; otherwise wait for the Changed handler to fire again

There's nothing particularly clever going on here, but there are a few complicating circumstances worth mentioning:

  • The Changed event fires whenever the MediaPlayer instance gets a new frame from its underlying implementation. However, a single Position change results in multiple invocations of the Changed event (2-5 seems to be the usual range). Typically, the first Changed event doesn't actually correspond to new data, but rather the same frame as before - so simply listening for the first Changed event is not sufficient. What's most desirable is to listen for the last Changed event - but without knowing how many there will be in advance, it's difficult to know which one is the last frame...
  • Fortunately, in practice the first Changed event corresponding to different content (as determined by a pixel-by-pixel comparison of the captured frames) seems to also correspond to the final, desired content. So a very reasonable solution is to wait for the first changed content, call it "good enough", and move on by seeking to the next position in the file.
  • So now the application is quickly seeking to arbitrary positions and capturing the desired frame with a high degree of reliability. We're done, right? Well, not quite... What if a video file contains the same exact content at two different positions? (Note: This is uncommon in motion video, but VERY common at the beginning and ending of clips (ex: 100% black frame) or in generated content (ex: title frame).) The approach described so far will spend forever waiting for a new frame that will never arrive...
  • What's needed here is a separate watchdog timer that fires after a reasonable time has elapsed and aborts the infinite wait. The catch is that this timer should be long enough to give the new video content sufficient time to be made available, but also short enough that it's not onerous if it fires many times during the generation of thumbnails. I experimentally settled on 1 second for the watchdog timeout because it seems to strike a good balance between these competing goals. With the watchdog timer in place, the process of generating a video summary should never hang - and we're done!

Having implemented the technique above, we should have a fast, reliable solution for video file thumbnail generation that works with a wide variety of file types by virtue of the underlying operating system's support. And, indeed, VideoThumbnailer works fine with all the WMV, DVR-MS, MPEG, and AVI files I've tried (and probably supports some other types I didn't try). There are plenty of changes one could make to VideoThumbnailer to make it a more useful application, but I hope it serves pretty well as a concrete example of the concepts discussed here.

Happy frame grabbing!

[VideoThumbnailer.zip]

Tags: WPF

Maintaining access to Silverlight 2's generic.xaml resources [SilverlightDefaultStyleBrowser updated for build 2.0.30523.8 and beyond!]

A few days ago the Silverlight team released a minor update to Beta 2, changing the version number from 2.0.30523.6 to 2.0.30523.8 in the process. The version number is part of Silverlight's install path and SilverlightDefaultStyleBrowser (background reading available here, here, and here) didn't know to look in the new location when automatically importing Styles. As such, when run on machines with the updated Silverlight bits, the list was missing Styles for the core controls like Button, ListBox, etc.. (As I discovered for myself a short while ago when I tried to use SilverlightDefaultStyleBrowser!)

I've just updated SilverlightDefaultStyleBrowser to look in all subdirectories of %ProgramFiles%\Microsoft Silverlight when starting up. Not only does this fix the current problem, but it should save me from having to do anything next time there's an update, as well! :)

The version number of SilverlightDefaultStyleBrowser always appears in the window's title and the latest release number is 1.0.3123.31689. (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 once it detects the update (which typically happens after running the app 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.

Notes:

  • On most machines it seems that the upgrade to Silverlight version 2.0.30523.8 automatically uninstalled the bits for 2.0.30523.6. However, on some machines (like one of mine!), the uninstall of 2.0.30523.6 was skipped or incomplete. So it's possible that SilverlightDefaultStyleBrowser's new behavior of importing from all directories under %ProgramFiles%\Microsoft Silverlight will import Styles from more than one version of Silverlight. To help avoid confusion - and to support multiple side-by-side installations of Silverlight in the future - I've modified the ToolTip for the ListBox items to display the path of the assembly from which that Style came. So if you see multiple Button Styles in the list, just hover over each of them to see where they came from - then pick the one that matters to you!
  • I've tweaked the ListBox item ToolTip to pop up when the mouse is over any part of the item (instead of just the Style's name). This was a simple matter of adding HorizontalContentAlignment=Stretch to the ListBoxItems via the ListBox's ItemContainerStyle property.

I'm sorry for any trouble the recent Silverlight upgrade may have caused! The good news is that this particular problem shouldn't happen again. :) And if it does, you can always use the "Add Assembly" button to manually import Styles from any file(s) you want until I get around to fixing things.

The layout system lies have become a bit more elaborate [LayoutTransform functionality updated and enhanced for Silverlight 2 Beta 2!]

In the introductory post for LayoutTransformControl, I showed a trivial use case to demonstrate the need for LayoutTransform in some scenarios. 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 described how my LayoutTransformControl sample did just that, but implemented only RotateTransform functionality for its initial release. My impression then - as now - is that rotation is easily the most common use of LayoutTransform. However, there are plenty of scenarios where the other Transform subclasses (scale, skew, matrix, etc.) are required, and I wanted to have a solution for them as well.

So I've added full support for arbitrary transformations to LayoutTransformControl along with updating it for Beta 2! (The complete implementation - along with demo and test applications - can be found in LayoutTransformControl.cs in the attached ZIP.)

Where you might previously have written:

<local:LayoutTransformControl Angle="15">
    <TextBlock Text="I am rotated 15 degrees!"/>
</local:LayoutTransformControl>

You now write:

<local:LayoutTransformControl>
    <local:LayoutTransformControl.Transform>
        <RotateTransform Angle="15"/>
    </local:LayoutTransformControl.Transform>
    <TextBlock Text="I am rotated 15 degrees!"/>
</local:LayoutTransformControl>

Wait a second! That's more typing than before - why is this an improvement? Well, because you can also do this (just as you would on WPF):

<local:LayoutTransformControl>
    <local:LayoutTransformControl.Transform>
        <ScaleTransform ScaleX="1.5" ScaleY="2.5"/>
    </local:LayoutTransformControl.Transform>
    <TextBlock Text="I am a little wider and a lot taller!"/>
</local:LayoutTransformControl>

Or this:

<local:LayoutTransformControl>
    <local:LayoutTransformControl.Transform>
        <MatrixTransform Matrix="-1,0,0,1,0,0"/>
    </local:LayoutTransformControl.Transform>
    <TextBlock Text="I am flipped horizontally!"/>
</local:LayoutTransformControl>

Or even this:

<local:LayoutTransformControl>
    <local:LayoutTransformControl.Transform>
        <TransformGroup>
            <ScaleTransform ScaleX="1.5" ScaleY="2.5"/>
            <SkewTransform AngleX="10"/>
            <MatrixTransform Matrix="-1,0,0,1,0,0"/>
            <RotateTransform Angle="15"/>
        </TransformGroup>
    </local:LayoutTransformControl.Transform>
    <TextBlock Text="I am all of the above - and tilted, too!!"/>
</local:LayoutTransformControl>

So maybe it's a bit of an improvement, after all... :)

Naturally, I updated my sample application to show off the new capabilities:

LayoutTransformControl Sample Application

And I updated my cross-platform test suite as well (here's the WPF version):

LayoutTransformControl Test Matrix on WPF

LayoutTransformControl should now be just as powerful as WPF's LayoutTransform is. Your application can take advantage of complete LayoutTransform functionality today - even though the Silverlight platform still doesn't officially support LayoutTransform!

Notes:

  • Improvements to Silverlight in Beta 2 (vs. Beta 1) meant that I was able to remove the lesser of the two Silverlight-specific workarounds present in the Beta 1 code.
  • I removed Button from the test matrix and added two new test cases to cover scenarios where LayoutTransformControl was previously behaving incorrectly. (Unfortunately, the default Button template in Beta 2 still causes the Button to measure/arrange itself incorrectly in some cases. The existing ContentControl test cases already cover correct Button behavior, so there's no loss of coverage.)
  • In WPF, changes to the members of a Transform property (be it an individual Transform or a TransformGroup) automatically cause the Transform property's property changed handler to be invoked (even though OldValue==NewValue). This is a wonderfully elegant way to handle the situation - and unfortunately this behavior isn't present on Silverlight. So LayoutTransformControl needs a way to know when one of its Transform members has been updated and it can't rely on Silverlight to tell it. One solution is to try to create a Binding to the relevant properties of each of the members. Unfortunately, Bindings don't work on Transform-derived classes in Silverlight (my theory: because Transform derives from DependencyObject instead of FrameworkElement where the SetBinding method is implemented). So what I've done is create a set of "Ltc"-prefixed Transform-derived classes that do provide automatic notifications (but still not Binding support - due to the same Silverlight limitation, I suspect). So instead of RotateTransform, you use LtcRotateTransform and get change notifications for free. The "Ltc" Transforms have the same transformation properties (ex: Angle, ScaleX) as their counterparts, so they're simple, plug-compatible replacements - with two caveats: LtcTranslateTransform doesn't exist (because TranslateTransform has no effect during LayoutTransform), and the CenterX/CenterY properties aren't implemented (for the same reason).
  • The "Ltc" Transforms are a pretty good workaround, but you don't have to use them. If the members of the LayoutTransformControl's Transform property never change, then change notification is unnecessary and the standard Transforms can be used without issue. The code in the "Motivation" project (part of the attached solution) demonstrates this approach. If the members of the Transform property do change and you're able to use the "Ltc" transforms, then the right thing should happen automatically. The code in the "Sample" project demonstrates this approach. But if your scenario is such that the members of the Transform property do change and you're unable to use the "Ltc" Transforms, all is not lost! LayoutTransformControl exposes a public TransformUpdated method that you can call to notify it of changes to the members of its Transform property. The code for the test matrix projects uses this last approach.
  • In any kind of project where the goal is to duplicate an existing behavior, it's common to do side-by-side comparisons to find bugs in the new implementation. This is exactly what my WPF test matrix does and it helped me find and fix a number of problems during the development of LayoutTransformControl. However, there typically comes a point at which this comparison process starts revealing differences that turn out to be bugs in the reference implementation. That's usually a pretty good sign that the new implementation is approaching the same level of quality as the old one - and it's a milestone I've hit with LayoutTransformControl. In addition to the "maximal area rectangle" improvement mentioned in the original blog post, LayoutTransformControl exhibits two new, deliberate behavior differences (improvements!) from WPF's LayoutTransform implementation. They're subtle, but they can both be demonstrated in the test matrix. Let me know if you think you've found one! :)
  • The examples above assume the "local" namespace prefix has been mapped to an assembly containing the LayoutTransformControl implementation:

    xmlns:local="clr-namespace:LayoutTransformControlSample;assembly=YourAssemblyName"

My goal with LayoutTransformControl has always been to address a perceived need. It began life as a bit of an experiment, but by now I've become rather fond of the solution and plan to incorporate it into projects of my own. I hope that if you have a need for LayoutTransform behavior in Silverlight, you'll consider LayoutTransformControl, too!

[LayoutTransformControl.zip]

Text; from a slightly different perspective [VerticalTextBlock Control sample for Silverlight 2]

During an informal discussion yesterday, the question of displaying text vertically (i.e., letters stacked on top of each other instead of side-by-side) came up and I offhandedly said it would be pretty easy to do. Later on, I got curious about just how accurate my claim was, so I spent a bit of time on the bus coding up a quick demonstration of what I had in mind. What I ended up with is a simple example, but it's enough that I feel like my flippant estimate wasn't so far off after all. :)

VerticalTextBlock is a simple Control subclass that runs on both Silverlight and WPF. The following XAML (in which the optional parts are italicized):

<local:VerticalTextBlock
    Text="Hello world"
    FontFamily="Arial"
    FontSize="12"
    FontWeight="Bold"
    Background="Orange"/>

Renders as follows (Silverlight on the left, WPF on the right):

VerticalTextBlock Demonstration

The implementation is quite simple: VerticalTextBlock exposes a Text property of type string, the VerticalTextBlock Template contains a TextBlock, and that TextBlock is populated with a series of LineBreak-separated Runs containing one character each.

Here's the complete code that compiles for both platforms:

using System.IO;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Markup;

namespace VerticalTextBlockSample
{
    public class VerticalTextBlock : Control
    {
        public VerticalTextBlock()
        {
            IsTabStop = false;
            var templateXaml =
                @"<ControlTemplate " +
#if SILVERLIGHT
                    "xmlns='http://schemas.microsoft.com/client/2007' " +
#else
                    "xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation' " +
#endif
                    "xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>" +
                    "<Grid Background=\"{TemplateBinding Background}\">" +
                        "<TextBlock x:Name=\"TextBlock\" TextAlignment=\"Center\"/>" +
                    "</Grid>" +
                "</ControlTemplate>";
#if SILVERLIGHT
            Template = (ControlTemplate)XamlReader.Load(templateXaml);
#else
            using(var stream = new MemoryStream(Encoding.UTF8.GetBytes(templateXaml)))
            {
                Template = (ControlTemplate)XamlReader.Load(stream);
            }
#endif
        }

        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();
            _textBlock = GetTemplateChild("TextBlock") as TextBlock;
            CreateVerticalText(_text);
        }

        private string _text { get; set; }
        private TextBlock _textBlock { get; set; }

        public string Text
        {
            get { return (string)GetValue(TextProperty); }
            set { SetValue(TextProperty, value); }
        }
        public static readonly DependencyProperty TextProperty = DependencyProperty.Register(
            "Text", typeof(string), typeof(VerticalTextBlock), new PropertyMetadata(OnTextChanged));
        private static void OnTextChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
        {
            ((VerticalTextBlock)o).OnTextChanged((string)(e.NewValue));
        }
        private void OnTextChanged(string newValue)
        {
            CreateVerticalText(newValue);
        }

        private void CreateVerticalText(string text)
        {
            _text = text;
            if (null != _textBlock)
            {
                bool first = true;
                foreach (var c in _text)
                {
                    if (!first)
                    {
                        _textBlock.Inlines.Add(new LineBreak());
                    }
                    _textBlock.Inlines.Add(new Run { Text = c.ToString() });
                    first = false;
                }
            }
        }
    }
}

At the end of the day, VerticalTextBlock is a simple control I wrote to keep myself honest. :) I figured it could be useful to others, so I'm posting about it and attaching a ZIP file with the complete solution+projects so anyone who's interested can try it out!

[VerticalTextBlock.zip]

Continuing access to Silverlight 2's generic.xaml resources [SilverlightDefaultStyleBrowser updated for Silverlight 2 Beta 2!]

Silverlight 2 Beta was released earlier today and one of the big changes is the switch to using Visual State Manager for control design/development. Another is that many of the core controls (Button, ListBox, etc.) have moved from the SDK into the Silverlight runtime itself (which nicely avoids the need to include them with every application download). I wanted to be sure that SilverlightDefaultStyleBrowser (background reading available here and here) worked seamlessly with Beta 2, so I made a few quick changes to help users explore the new stuff.

The version number of SilverlightDefaultStyleBrowser always appears in the window's title and the latest release number is 1.0.3079.23243. (Note: I haven't updated the original screen shot which shows the introductory version number.) If installed with ClickOnce, the application should automatically prompt you to upgrade once it detects the update (which typically happens after running the app once or twice). If you're using the standalone EXE then you'll need to update manually. For people who might not be able to upgrade to Beta 2 immediately, there's no need to worry because the new version works with the Beta 1 bits as well.

SilverlightDefaultStyleBrowser Sample page

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.

Notes:

  • Now that Blend 2.5 has rich support for Silverlight control design, some of the uniqueness of SilverlightDefaultStyleBrowser has been lost. However, it's still pretty handy for situations where Blend isn't available, so there may still be some residual value here. :)
  • The change I knew I wanted to make was the default search path for control assemblies. In Beta 1, nearly all the controls were part of the SDK, so that was the only place SilverlightDefaultStyleBrowser looked. But with Beta 2, the core controls are part of the Silverlight runtime and are located in the runtime install directory. SilverlightDefaultStyleBrowser now automatically imports assemblies from both locations when it loads.
  • The thing I didn't expect to change was special-casing the XML namespace for the Visual State Manager parts of the new control templates. SilverlightDefaultStyleBrowser uses LINQ to XML for parsing XAML and, by default, LINQ to XML emits namespace attributes at the time of first use (instead of on the top node). As far as I know, this is perfectly valid, but the resulting XAML causes a compile error when pasting such a Style/Template into Page.xaml: "Unexpected PROPERTYELEMENT in parse rule ...". This isn't ideal, so I added a bit of code to introduce the "xmlns:vsm" namespace on the Template node (i.e., before its first use) to avoid this error. (Note: The extra namespace is unnecessary - but harmless - for Beta 1 Templates.)
  • The header text changed from (for example) "System.Windows.Controls.ListBoxItem" to "System.Windows.Controls:ListBoxItem" to better communicate that ListBoxItem came from the System.Windows.Controls assembly (vs. being part of that namespace (which is not always be the case)).
  • If you compile the source code yourself, you may get a compiler warning from the ClickOnce settings in the project files: "Unable to find manifest signing certificate in the certificate store.". Despite turning off ClickOnce manifest signing, it seems to have gotten turned back on somehow. To make the warning go away, it should be enough to go to the Project's Properties page and uncheck the "Sign the ClickOnce manifests" box on the Signing tab. However, I found that unchecking that box didn't always do the trick - what worked for me was to check that box, use the "Create Test Certificate" button to create a dummy certificate, and then uncheck it.

Well, that's all the news about SilverlightDefaultStyleBrowser. Now go download Silverlight 2 Beta 2 and have fun with the new features! :)

Lying to the layout system for a good cause [Bringing LayoutTransform to Silverlight 2!]

People who want to rotate visual elements in Silverlight 2 are likely to use RotateTransform within RenderTransform - but they may not always get the results they expect! For example, using RenderTransform to achieve the following effect:

Sweet

Actually renders like this:

Whoops

But the problem isn't with RenderTransform - it's with using the wrong tool for the job! By design, RenderTransform applies its transformations (a rotation in this case) after the layout system has performed its measure/arrange pass. So when the elements in the example are being measured and arranged, the text is still horizontal. It's only after everything has been positioned that the text is finally rotated - and ends up in the "wrong" place. While it's possible to correct for this discrepancy by hard-coding all the relevant offsets in the XAML (very brittle and error-prone) or by adjusting all the offsets in code (only slightly more flexible - and a lot more work), these aren't great alternatives.

The right tool for the job is LayoutTransform which applies its transformations before the layout pass. With LayoutTransform, the text in the example is already rotated by the time the elements are measured and arranged, and the desired effect can be achieved quite simply.

But there's a catch: LayoutTransform doesn't exist in Silverlight 2 (Beta 1)...

However, there's no reason to let that stop us. Rotation is rotation whenever it happens, so maybe there's a way to get the already-optimized RenderTransform implementation to do the real work earlier in the layout pass. Unfortunately, we can't change when RenderTransform is applied.

But it turns out that we can tell a very carefully crafted set of lies to the layout system during the measure/arrange pass in order to convince it to lay things out as if it supported LayoutTransform - then we let RenderTransform do the work of actually rotating the content. The result is that we've got something that looks like LayoutTransform and behaves like LayoutTransform - so it might as well be LayoutTransform! :)

I've done just this and the result is something I've called LayoutTransformControl. The complete implementation can be found in LayoutTransformControl.cs in the attached ZIP. The XAML for LayoutTransformControl is quite simple and follows the well-known WPF Decorator model (ex: Border, Viewbox):

<local:LayoutTransformControl Angle="15">
    <TextBlock Text="I am rotated 15 degrees!"/>
</local:LayoutTransformControl>

Note: This assumes the "local" namespace prefix has been mapped to an assembly containing the LayoutTransformControl implementation:

xmlns:local="clr-namespace:LayoutTransformControlSample;assembly=YourAssemblyName"

In fact, the first picture of this post (the one that looked right!) was done with LayoutTransformControl. But it's easy to get simple scenarios right... So I also wrote a sample application that lets you interactively change the rotation angle and swap in different content:

LayoutTransformControl Sample Application

Attribution: The XAML example came from a post on designerslove.net; the image is from the set of stock Windows Vista wallpapers.

But it's easy to get a sample right... So I also wrote a test harness to exercise a handful of interesting elements in most of the interesting constraint scenarios and show RenderTransform along with LayoutTransformControl:

LayoutTransformControl Test Matrix on Silverlight

But the rules of layout are sufficiently complex and subtle that it's hard to tell if LayoutTransformControl is behaving properly without knowing how it's supposed to behave... So the test harness (and LayoutTransformControl!) also runs under WPF where LayoutTransform is supported and can be used to visually verify that LayoutTransformControl is doing what it should by comparing the bottom two rows:

LayoutTransformControl Test Matrix on WPF

Whew! :)

So - after all that posturing and seemingly comprehensive test coverage, you might expect me to be confident that LayoutTransformControl behaves correctly under all circumstances. Well... no. LayoutTransform is conceptually simple, but exhibits all kinds of weird and unexpected behaviors in practice. I've lost count of the number of times I had to stare at a bit of LayoutTransform output and figure out why it's correct - sometimes more than once for the same output! Add to that the fact that LayoutTransformControl is doing everything outside the core layout system, and I'm kind of surprised any of this works... :)

I do believe that LayoutTransformControl behaves correctly in all scenarios I've subjected it to, but I would not be surprised at all to hear about other scenarios where it breaks down. If you think you've found such a scenario, please let me know and I'll try to figure out what might be going on. (But before you do, please check the behavior on WPF - that's the first thing I'll do anyway!)

Notes:

  • This release of LayoutTransformControl supports only rotation because that's by far the most common scenario. However, I'm prepared to add support for scale, skew, and matrix if folks find this useful. (Party trivia: Translation has no effect during LayoutTransform.)
  • For the most part, using LayoutTransformControl is simply a matter of wrapping the desired content and setting the angle. But it's important to note that some properties will need to be moved from the child control to LayoutTransformControl. For example, when positioning things within a Grid, the Grid.Column and Grid.Row attached properties need to be set on LayoutTransformControl in order to be recognized by the parent Grid.
  • Examination of the test matrices above reveals two things that deserve more explanation:
    • Thing 1: The sizing of the Buttons in the Silverlight test matrix is different than in the WPF matrix. At first glance, the sizing seems to be wrong, but that's not actually the case. The default Button style in Silverlight Beta 1 has a bug that interferes with proper layout (and is completely unrelated to LayoutTransformControl). This can be seen with a simple example; adding the following to a new project should show a small, centered Button just like it does on WPF:
      <Button Content="Button" HorizontalAlignment="Center" VerticalAlignment="Center"/>
      However, on Silverlight Beta 1 the result is a very large Button instead. The problem is that the template in the default style includes the following:
      <Path Stretch="Fill" .../>
      The Stretch on that Path element causes the Button to measure much larger than it should (and a similar issue with the Line element affects the default ListBoxItem style). It's because the Button behaves as it does that sizing with LayoutTransformControl looks like it does. It's comforting to note that applying the Silverlight Button style to a WPF Button and using the real LayoutTransform gives the same wrong-looking behavior.
    • Thing 2: The rotated rectangle used by LayoutTransformControl for its child content is slightly different than that used by LayoutTransform under certain circumstances. This causes the child control to layout in a slightly longer, narrower rectangle and can be seen in the first column of the WPF test matrix. Perhaps surprisingly, this difference is deliberate. When determining the parameters of the rotated rectangle, the WPF algorithm finds the largest rectangle possible (as measured by area). It just so happens that the algorithm I came up with manages to find an even larger rectangle in certain cases. :) As far as I've seen, the algorithm in LayoutTransformControl is never worse - and sometimes better - so I decided it's a winner.
  • The example elements in the test matrix were chosen as follows:
    • Button - Represents the classic layout-friendly control which has a preferred size, but is willing to go smaller or larger as space permits
    • ContentControl - Containing a Border and a TextBlock, this simple Button-like layout acts like a Button should on Silverlight
    • TextBlock - Interesting for layout because it refuses to render smaller than the size it asked for
  • I've omitted any technical discussion of how LayoutTransformControl works from this blog post. For now, curious readers can refer to the commented source code. If there's interest, I'll write a follow-up post explaining what's really going on under the covers.

LayoutTransformControl was an interesting project that demonstrates once again the power and versatility of the WPF/Silverlight layout system. LayoutTransform and RenderTransform are like peanut butter and jelly - and I'm glad to help reunite them on Silverlight. If you've got a layout problem and RenderTransform isn't doing what you need, maybe LayoutTransformControl is the solution!

[LayoutTransformControl.zip]

IValueConverter: The Swiss Army Knife of Bindings [PropertyViewer sample is a WPF/Silverlight visualization and debugging aid!]

If you've made much use of data binding in WPF or Silverlight, you've probably come across the IValueConverter interface. IValueConverter sits between the data source and destination and gives the developer a chance to examine/alter/replace the data as it flows through the converter. It's been my experience that IValueConverter is a powerful and versatile tool for application developers.

As part of a recent project, I wanted to display some of an object's properties in a simple list. Specifically, I had an instance and I wanted to display a list of "Property Name: Property Value" entries for each property of that object. My background with ListBox led me to naturally think of using ItemsControl for the basis of my property viewer; the ItemsControl content model (with its support for DataTemplates) seemed like a natural fit.

Aside: One of the key things XAML enables is a distinct separation of implementation from representation. By explicitly separating most aspects of how an application looks (XAML) from how it works (code) as part of the developer/designer workflow, WPF and Silverlight help to enforce a level of encapsulation (in the object-oriented programming sense) that makes programs easier to write and maintain. In the ideal world, a program's functionality is entirely expressed in its code - and so it's possible for others to completely change that application's appearance without knowing or caring how it's implemented. Just like the web has CSS restyling contests, WPF has reskinning competitions!

So I knew I wanted to use ItemsControl and I knew I wanted to keep the UI aspects of the property viewer in XAML-space (what it looked like, what properties it displayed, etc.). I started looking at how IValueConverter might help and that led to the solution I describe here. PropertyViewer ends up being quite simple to use - and a good demonstration of the power of IValueConverter!

Here's the comment header from the code:

/// <summary>
/// IValueConverter implementation that expands some/all of an object's
/// public properties as a collection of bindable name/value instances.
/// </summary>
/// <remarks>
/// * Bindable instances are of type PropertyDetails, a contained class
///   that exposes a property's Name and Value as properties.
/// * All public properties are enumerated by default; ConverterParameter
///   can be used to specify which properties will be enumerated (and in
///   which order) by passing a space-delimited list of names.
/// * If DisplayNameAttribute is associated with a property, DisplayName
///   will be used instead of the property name.
/// * PropertyViewer can be used for simple data visualization, debugging
///   of Bindings, and more.
/// </remarks>
/// <example>
/// For ItemsControl/ListBox (accessing the object via DataContext):
///   ItemsSource="{Binding Converter={StaticResource PropertyViewer}}"
/// As above, but with a custom property list:
///   ItemsSource="{Binding Converter={StaticResource PropertyViewer},
///     ConverterParameter='PropertA PropertyB'}}"
/// For ItemsControl/ListBox, but specifying the object via Source:
///   ItemsSource="{Binding Source={StaticResource ObjectInstance}
///     Converter={StaticResource PropertyViewer}}"
/// </example>
public class PropertyViewer : IValueConverter
{ ... }

Like the comment says, it's easy to hook up a PropertyViewer:

<ItemsControl ItemsSource="{Binding Converter={StaticResource PropertyViewer}}"/>

Customizing the PropertyViewer is also easy:

<ItemsControl ItemsSource="{Binding Converter={StaticResource PropertyViewer},
    ConverterParameter='Species EatsBugs RelativeMass'}">

Because the end result is a collection of objects, all the existing ItemsControl knowledge and techniques can be used to completely customize the look and feel of the property viewer!

The examples above came from a simple application I wrote to demonstrate PropertyViewer in action. By design, the exact same XAML and code work on both WPF and Silverlight. To prove it, I attached the complete source code for the combined Visual Studio 2008 solution to this blog (download it from the link at the bottom of this post). The sample displays a list of animals, one default PropertyViewer, and one customized PropertyViewer. Select any animal to find out more about it (note that the PropertyViewer updates automatically when the data source changes).

Here's what it looks like on WPF:

WPF Sample

And on Silverlight:

Silverlight Sample

Thanks to the power of IValueConverter, it's easy to use PropertyViewer to display an object's properties in a customizable way. Additionally, PropertyViewer can help troubleshoot data bindings: just drop one in the target location and you can see the available properties along with their current values. I originally wrote this for my project - but now I hope you can use it in yours!

[PropertyViewer.zip]

Improved access to Silverlight 2's generic.xaml resources [SilverlightDefaultStyleBrowser available via ClickOnce]

In response to my previous post announcing the SilverlightDefaultStyleBrowser tool for working with default styles for Silverlight 2 controls, I got an email from Rob Relyea suggesting that I make SilverlightDefaultStyleBrowser available via ClickOnce as well. (For those who may not be familiar with it, here's a brief ClickOnce overview.) ClickOnce is simple to configure, adds a Start Menu entry automatically, supports seamlessly updating an application, and installs/uninstalls on Vista as standard user without requiring elevation - so this was easy to do!

SilverlightDefaultStyleBrowser sample image

[Click here or on the image above to install SilverlightDefaultStyleBrowser with ClickOnce.]

The functionality of SilverlightDefaultStyleBrowser is the same whether you use the executable in the ZIP or use the ClickOnce version - so pick whatever suits you best. And have fun styling those controls! :)

Note: The source code for SilverlightDefaultStyleBrowser is available only in the ZIP file download.