RSS Feed
Download our iPhone app
Browse DevX
Sign up for e-mail newsletters from DevX


Designing Lookless Controls in Windows Presentation Foundation : Page 2

Learning to use and design lookless controls can free your WPF applications from the boring monotony of gray buttons forever.

Building the Template
The core of overriding the default look of a WPF component is to create a Template setter as shown in the snippet below. For a complete listing of the components look, see Common.xaml which you can find in both the downloadable source file and in Listing 1:

   <Style TargetType="Button">
     <Setter Property="Template">
         <ControlTemplate TargetType="Button">
It's important to note that you need to define the TargetType in your style property in order for the proper dependency properties to be available. Because different event triggers are available for different types of components, this property helps determine what events and options are available as setters. If the syntax of this style property looks strange to you, you might want to take a look at the sidebar WPF Oddities.

Once inside the ControlTemplate tag, you're free to put any kind of component or container. It expects only one component, so to do something more complex, your one component will need to be a layout component that can contain more sub-components, such as a Grid or StackPanel. The code example included with this article uses a Border component and a few sub-border components, one of which contains a StackPanel to hold multiple child components inside of a single Border.

Inside your template, one important tag is the ContentPresenter, which is responsible for displaying the sub-component provided to the usage of your template. For a button, this is typically the text label provided to the button via the Text attribute:

   <ContentPresenter />
The ContentPresenter itself supports a number of dependency properties that allow you to customize how you want sub-components to be handled. For most types of components, it's enough to just include an empty ContentPresenter tag, just to get the included text rendered.

Responding to Events
Figure 2. Mouseover Effect Applied: In this figure, the "Eyes" button has been clicked, and the mouseover effect has added a shadow at the top and the shine at the bottom.
Most components include some form of interactivity—and any form of interactivity requires that something happen with the component to let the user know that the component has accepted their input. The code example included provides support only for a mouseover effect. When the mouseover effect is applied it adds the shadow at the top of the button and the shine at the bottom to create the look of a slight depression in the button area. Figure 2 illustrates the mouse-over state of the "Eyes" button:

   <Trigger Property="IsMouseOver" Value="true">
     <Setter TargetName="Border" 
       Value="{StaticResource NormalBrush}" />
     <Setter TargetName="TopHighlight" 
       Value="{StaticResource ShadowInverted}"/>
     <Setter TargetName="BottomHighlight" 
       Value="{StaticResource ShinyInverted}"/>
You don't have to create the inverse state when working with events in XAML. For example, the included code provides setters that set the borders to use different gradients to invert the direction of the highlight and shadow on the PillButton if IsMouseOver=True. You don't have to define an event for IsMouseOver=False to return these to a normal state.

If your component contains a multiple components as the PillButton does, you can address each of these items to change different style properties on the elements by setting the TargetName. When defining the component's template, each component you define will need to have an x:Name attribute set if you want to be able to modify this element in your event triggers. In the setters of your event trigger, the TargetName correlates to the x:Name of the component defined in the template.

Each component type has a multitude of events—many are unique to that particular component type. For some help locating what events are supported by a given WPF component, take a look at the What Events Can I Respond To?

Achieving the Glass Look
The reflection is courtesy of a rectangle placed below the pill itself that applies a few filters. This rectangle is automatically sized to match the pill via the binding reference:

   Height="{Binding Path=ActualHeight,ElementName=Border}"
   Width="{Binding Path=ActualWidth, ElementName=Border}">
Within the rectangle, you can use a VisualBrush object that uses another component as a source. In this case, the code uses the pill button as the visual source. It then applies a group of filters chained together that invert the image (mirror), apply a gradient transparency to it (to make it fade), and apply a blur effect. The combined result of these three filters on the rendered result of the pill button is the coveted glass effect shown in Figure 1.

Because all this magic is done in real time using full binding, if you animate the pill the reflection will move and resize with the pill.

Lookless controls provide a powerful and flexible way to define application look and feel while cleanly separating visual style from behavior. For further exploration, look into styling other types of WPF controls. Each control has unique properties and approaches that you'll need to explore to figure out how to take each control to its limits—and beyond.

David Talbot is the vice president of development for Data Systems International, a company that develops case-management software for the social services industry. His experience ranges from license-plate recognition using neural networks to television set-top boxes to highly scalable Web applications. He is also the author of Applied ADO.NET.
Email AuthorEmail Author
Close Icon
Thanks for your registration, follow us on our social networks to keep up-to-date