This article is compatible with the latest version of Silverlight.
Introduction
The reflection effect is widely used in different Silverlight demos and now, in this article, we will show you how to create a control that adds reflection to any class that inherits FrameworkElement:
System.Object
System.Windows.Threading.DispatcherObject
System.Windows.DependencyObject
System.Windows.Media.Visual
System.Windows.UIElement
System.Windows.FrameworkElement
Download source code
Overview
The idea is to have two identical FrameworkElement objects: the first one representing an image and the other one - a reflection of this image. We put four properties to define the reflection: coefficient of opacity; coefficient of fade away; angle of the reflection; and distance between the image and the reflection.
The only thing we do here is change the sign of the Y coordinate (M22 in the Transformation matrix). And for the angle of reflection we name a SkewTransform so that later we can change its value.
<Canvas x:Name="reflectionPlane">
<Canvas.RenderTransform>
<SkewTransform x:Name="skew"/>
</Canvas.RenderTransform>
<Canvas>
<Canvas x:Name="reflectionContainer">
<Canvas.RenderTransform>
<TransformGroup>
<MatrixTransform x:Name="reflectionMatrixTransform">
<MatrixTransform.Matrix >
<Matrix M11="1" M12="0"
M21="0" M22="-1"
OffsetX="0" OffsetY="0"/>
</MatrixTransform.Matrix>
</MatrixTransform>
</TransformGroup>
</Canvas.RenderTransform>
</Canvas>
</Canvas>
</Canvas>
. . .
public double ReflectionAngle
{
get { return skew.AngleX; }
set { skew.AngleX = value; }
}
The reflectionPlane and reflectionContainer canvases keep the settings of the transformations. When we set the element and its reflection we only add them to the children list and the transformations are done automatically.
private FrameworkElement reflectionElement;
public FrameworkElement ReflectionElement
{
get { return reflectionElement; }
set
{
this.reflectionContainer.Height = value.Height;
reflectionContainer.Children.Remove( reflectionElement );
reflectionElement = value;
reflectionContainer.Children.Add( reflectionElement );
}
}
Every time we set the FrameworkElement we change the offset of the matrix transformation to adjust the position of the reflection.
private void UpdateSizes()
{
Matrix newMatrix;
newMatrix= this.reflectionMatrixTransform.Matrix;
newMatrix.OffsetY = this.Height + this.distance / 2;
this.reflectionMatrixTransform.Matrix = newMatrix;
this.reflectionPlane.SetValue( Canvas.TopProperty, this.Height + this.distance / 2 );
}
And here is the brush that makes the reflection fade away.
transparent = new GradientStop();
transparent.Color = Color.FromArgb( 0, 0, 0, 0 );
LinearGradientBrush fade = new LinearGradientBrush();
fade.StartPoint = new Point( 0, 0 );
fade.EndPoint = new Point( 0, 1 );
GradientStopCollection fadeAlpha = new GradientStopCollection();
GradientStop opaque = new GradientStop();
opaque.Color = Color.FromArgb( 255, 0, 0, 0 );
opaque.Offset = 1;
fadeAlpha.Add( opaque );
fadeAlpha.Add( transparent );
fade.GradientStops = fadeAlpha;
reflectionContainer.OpacityMask = fade;
In the host project we create a couple of canvases to demonstrate the reflection. We even add an animation to one of them. And this is what we achieve: