WPF Reflection Control

One of the more popular eye-candy effects is reflection, and WPF can do really good reflections, this post on Xamlog shows a really nice technique to add reflection to just about anything.

When I was showing this technique to a co-worker he asked me if it can be encapsulated in a control, well, why not?

So I quickly wrote this control, the control is a "Decorator" like a Border, that means you can add one child to it and it draws itself around that child (obviously this child can be a panel with multiple children).

This control is divided into two halves the upper half displays the child control and the lower half the reflection.

Here is an example of a window with a reflected login box:

Notice that the reflection is "live", every change in the controls (including the blinking caret is immediately automatically updated in the reflection).

Here is the XAML for the window, the parts that add the reflection are marked in yellow:

<Window x:Class="ReflectionControlApp2.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:r="clr-namespace:ReflectionControlApp2"
    Title="ReflectionControlApp2" Height="300" Width="300"
    >
 <Window.Background>
    <LinearGradientBrush StartPoint="0,0.3" EndPoint="1,0">
      <GradientStop Color="#C4CBD8" Offset="0" />
      <GradientStop Color="#E0E4F0" Offset="0.3" />
      <GradientStop Color="#E6EAF5" Offset="0.5" />
      <GradientStop Color="#CFD7E2" Offset="0.9" />
      <GradientStop Color="#C4CBD8" Offset="1" />
    </LinearGradientBrush>
 </Window.Background>
 <r:ReflectionControl>
    <Border BorderBrush="DarkGray" BorderThickness="3"
            CornerRadius="5" Background="#CFD7E2">
      <Grid Margin="5">
        <Grid.RowDefinitions>
          <RowDefinition Height="Auto"/>
          <RowDefinition Height="Auto"/>
          <RowDefinition Height="Auto"/>
          <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
          <ColumnDefinition Width="Auto"/>
          <ColumnDefinition Width="150"/>
        </Grid.ColumnDefinitions>
        <TextBlock Text="Welcome" FontSize="18" Grid.ColumnSpan="2" HorizontalAlignment="Center" Margin="2"/>
        <Label Content="User Name:" Grid.Row="1" Margin="2"/>
        <TextBox Grid.Row="1" Grid.Column="1" Margin="2" />
        <Label Content="Password:" Grid.Row="2" Margin="2"/>
        <PasswordBox Grid.Row="2" Grid.Column="1" Margin="2"/>
        <Button Grid.Row="3" Grid.ColumnSpan="2" Content="Login" Margin="2" Width="80"/>
      </Grid>
    </Border>
 </r:ReflectionControl>
</Window>

And here is the complete code for the reflection control itself:

using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
 
namespace ReflectionControlApp2
{
    class ReflectionControl : Decorator
    {
        private VisualBrush _reflection;
        private LinearGradientBrush _opacityMask;
 
        public ReflectionControl()
        {
            // Set defaults for this control
            VerticalAlignment = VerticalAlignment.Center;
            HorizontalAlignment = HorizontalAlignment.Center;
 
            // Create brushes were going to use
            _opacityMask = new LinearGradientBrush();
            _opacityMask.StartPoint = new Point(0, 0);
            _opacityMask.EndPoint = new Point(0, 1);
            _opacityMask.GradientStops.Add(new GradientStop(Colors.Black, 0));
            _opacityMask.GradientStops.Add(new GradientStop(Colors.Black, 0.5));
            _opacityMask.GradientStops.Add(new GradientStop(Colors.Transparent, 0.8));
            _opacityMask.GradientStops.Add(new GradientStop(Colors.Transparent, 1));
            _reflection = new VisualBrush();
            _reflection.Stretch = Stretch.None;
            _reflection.TileMode = TileMode.None;
            _reflection.Transform = new ScaleTransform(1, -1);
        }
 
        protected override Size MeasureOverride(Size constraint)
        {
            // We need twice the space that our content needs
            if (Child == null)
            {
                return new Size(0, 0);
            }
            Child.Measure(constraint);
            return new Size(Child.DesiredSize.Width, Child.DesiredSize.Height * 2);
        }
 
        protected override Size ArrangeOverride(Size arrangeBounds)
        {
            // always put out content at the upper half of the control
            if (Child == null)
            {
                return new Size(0, 0);
            }
            Child.Arrange(new Rect(0, 0, arrangeBounds.Width, arrangeBounds.Height / 2));
            return arrangeBounds;
        }
 
        protected override void OnRender(DrawingContext drawingContext)
        {
            // draw everything except the reflection
            base.OnRender(drawingContext);
 
            // set opacity
            drawingContext.PushOpacityMask(_opacityMask);
            drawingContext.PushOpacity(0.3);
 
            // set reflection parameters based on content size
            _reflection.Visual = Child;
            ((ScaleTransform)_reflection.Transform).CenterY = 3 * ActualHeight / 4;
            ((ScaleTransform)_reflection.Transform).CenterX = ActualWidth / 2;
 
            // draw the reflection
            drawingContext.DrawRectangle(
                _reflection, null,
                new Rect(0, ActualHeight / 2, ActualWidth, ActualHeight / 2));
 
            // cleanup
            drawingContext.Pop();
            drawingContext.Pop();
        }
    }
}

posted @ Wednesday, November 21, 2007 11:36 AM

Comments on this entry:

# re: WPF Reflection Control

Left by Roland Rodriguez at 6/18/2008 11:33 AM

Hi Nir,

FYI, I linked to your blog post from mine where I offer an alternative version of your reflection control. Have a look and let me know what you think!

Regards,

Roland
http://wpfblog.info/2008/06/18/reflecting-on-reflections/

# re: WPF Reflection Control

Left by Bill at 10/11/2008 3:28 PM

Excellent component. Thanks for publishing it.

# re: WPF Reflection Control

Left by Huy at 1/21/2009 6:29 PM

Hi Nir,

I've used your excellent control, in addition with my own control to create is Apple ‘like’ dashboard fisheye effect.

Regards,
Huy

huydinhpham.blogspot.com/.../...ted-dashboard.html

# re: WPF Reflection Control

Left by Hari Prasad at 4/21/2009 11:15 AM

Hi Nir,
Great control , i wonder how to raise an event for the Button control.
It gave me an error while doing so. could you help me out?
Thanks,
Hari.

# re: WPF Reflection Control

Left by Nilav Ghosh at 9/16/2010 5:15 PM

Hi,
very neat control indeed. only problem is that it doesnt work with 3D controls. Will be very much appreciated if you could publish the 3D version as well or provide the " post on Xamlog" . We can try and add on to your control .. Amazing work done.. very appreciated.

# re: WPF Reflection Control

Left by DNN at 10/20/2010 3:32 PM

xmlns:r="clr-namespace:ReflectionControlApp2"

I am getting an error "Assembly 'ReflectionControlApp2' was not found. Verify that you are not missing an assembly reference."

# re: WPF Reflection Control

Left by DNN at 10/20/2010 3:47 PM

Found the error. Forgot to comment the partial class.

Thanks.

# re: WPF Reflection Control

Left by Manish at 6/13/2012 1:58 PM

Thanks!! Your solution actually solved my problem. :)

Your comment:



 (will not be displayed)


 
 
Please add 1 and 7 and type the answer here: