Casting reflections

All FrameworkElement-derived classes have a RenderTransform property that we can utilize to transform their rendered output in a variety of ways. A ScaleTransform element enables us to scale each object in both horizontal and vertical directions. One useful facet about the ScaleTransform object is that we can also scale negatively, and therefore reverse the visual output.

One visually pleasing effect that we can create with this particular facet is a mirror image, or reflection, of the object. In order to enhance this effect, we can use an opacity mask to fade out the reflection as it recedes from the object. This can give the visual impression of an object being reflected on a shiny surface, as shown in the following image:

Let's see how we can achieve this result:

<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center"  
  Width="348"> 
  <TextBlock Name="TextBlock" FontFamily="Candara" 
    Text="APPLICATION NAME" FontSize="40" FontWeight="Bold"> 
    <TextBlock.Foreground> 
      <LinearGradientBrush StartPoint="0,0" EndPoint="1,0"> 
        <GradientStop Color="Orange" /> 
        <GradientStop Color="Red" Offset="0.5" /> 
        <GradientStop Color="Orange" Offset="1" /> 
      </LinearGradientBrush> 
    </TextBlock.Foreground> 
  </TextBlock> 
  <Rectangle Height="31" Margin="0,-11.6,0,0"> 
    <Rectangle.Fill> 
      <VisualBrush Visual="{Binding ElementName=TextBlock}"> 
        <VisualBrush.RelativeTransform> 
          <ScaleTransform ScaleY="-1.0" CenterX="0.5" CenterY="0.5" /> 
        </VisualBrush.RelativeTransform> 
      </VisualBrush> 
    </Rectangle.Fill> 
    <Rectangle.OpacityMask> 
      <LinearGradientBrush StartPoint="0,0" EndPoint="0,1"> 
        <GradientStop Color="#DF000000" /> 
        <GradientStop Color="Transparent" Offset="0.8" /> 
      </LinearGradientBrush> 
    </Rectangle.OpacityMask> 
  </Rectangle> 
</StackPanel> 

In this example, we use a StackPanel object to position a TextBlock element above a Rectangle element. The text will be the object to reflect and the reflection will be generated in the rectangle. The panel's width is constrained to ensure that the reflection fits the text element exactly. We start by naming the TextBlock element and setting some typeface properties, along with the text to output.

We've set a LinearGradientBrush object as the color for the text to make it more interesting, although this plays no part in creating the reflection effect. Next, note that the Rectangle element is sized and positioned exactly to fit the size of the text from the TextBlock element. We can of course use this technique to reflect anything and are not restricted to just reflecting text elements.

The background of the rectangle is painted with a VisualBrush object, where the Visual property is data bound to the visual output of the TextBlock element, using the ElementName property. Note the RelativeTransform property of the VisualBrush object, enables us to transform the visual in some way and is set to an instance of the ScaleTransform class.

This is one of the most important constituents for creating this effect, as this element is what inverts the related visual in the vertical plane. Setting the ScaleY property to -1 will invert the visual vertically for us, while setting the ScaleX property to -1 would invert the visual horizontally. Note that we omit the ScaleX property here because we want it set at its default value of 1.

Next, we see the OpacityMask property, which lets us set a gradient brush to be mapped to the opacity of the rectangle. When the alpha channel of the brush is 1, the rectangle will be opaque, when it is 0, the rectangle will be transparent and when it is in between, the rectangle will be semi-transparent. This is the other essential part of this effect and creates the fade of the reflected image.

In our example, we have a vertical gradient that is almost solid black at the top and gets increasingly transparent until it reaches four fifths of the way down, where it becomes fully transparent. When set as the rectangle's OpacityMask, only the alpha channel values are used and this results in it being totally visible at the top and then fading to invisibility four fifths of the way down, as shown in the preceding image.