Silverlight: Custom Design-Time Properties

|

Using themes in Silverlight—such as this really great one from these experts in awesomeness—at design-time, can be a real pain; especially then you’re working exclusively in Visual Studio. For example:

image

What does the UI for the Home.xaml look like? Reading the XAML there should be a couple TextBlock elements displaying some sample text. However, you cannot see this content properly because the background of the designer surface is white, but then so is the text in the view—Expression Blend does do a better job here, but it’s still not that great.

What typically happens in your development cycle is that you temporarily set the background colour to something visible in the designer, do your layout/design work, and then try to remember to remove hacked properties again before you deploy or test the view; toggle on, toggle off, rinse and repeat. This stops being funny very quickly.

I wanted to address this problem, but in a way that might already be familiar to both designers and developers. Expression Blend first introduced the notion of design-time properties by way of d:DesignWidth and d:DesignHeight, which have also made their way into Visual Studio. These properties enable you set the height and width of any element that only take effect from within the designer. This is a useful feature, however, today there is no way to extend these design-time properties, and today you only really have height and width to play with—along with support for design-time data. Which is nice and all that, but they are not versatile enough to help solve this problem.

Here’s what I’ve come up with:

public static class DesignTime
{
    private static readonly Brush DefaultBackgroundBrush =
        new SolidColorBrush(Colors.DarkGray);

    public static Brush GetBackground(DependencyObject obj)
    {
        return (Brush)obj.GetValue(BackgroundProperty);
    }

    public static void SetBackground(DependencyObject obj, Brush value)
    {
        obj.SetValue(BackgroundProperty, value);
    }

    public static readonly DependencyProperty BackgroundProperty = DependencyProperty.RegisterAttached(
        "Background",
        typeof(Brush),
        typeof(DesignTime),
        new PropertyMetadata(
            DefaultBackgroundBrush,
            OnBackgroundPropertyChanged));

    private static void OnBackgroundPropertyChanged(
        DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        if (!DesignerProperties.IsInDesignTool)
            return;

        var p = d as Panel;
        if (p != null)
            p.Background = e.NewValue as Brush;
    }
}

Here I have a single static class—called DesignTime—with an attached property—called Background. I have defined a property changed event handler that:

  1. Checks to see if we’re in the designer
  2. If we are, then try to cast to Panel—but this could be anything that has a Background property
  3. And then, set the background to the desired colour

This class only supports the Background property, but you would simply add a new dependency property for each design-time property you would like to support. If you apply the new property to the view I showed you earlier, you will get the following result:

image

As you can see from the screenshot above, I created a new XML namespace, so I can reference the DesignTime class, and therefore the Background property, with the prefix of pj. I then applied the property to the top level Grid element. Which renders on the designer nicely, assuming you like orange. However, when we launch the application, the original expert styling shines through, just as you would hope:

image

While I have not tested this approach in WPF I strongly suspect that it will Just Work™. Click here to download the sample code. As usual, if you have any comments, questions, flames, enhancements I would love to hear from you. In the meantime, think deeply and enjoy.

-PJ

No comments: