Creating a "Vista" shadow or halo in Silverlight

If you have been working at all with Vista. You have notice a nice shadow effect around all of the windows. It isn't a drop shadow, more of a fuzzy fade all around the window. This does an excellent job of layering the windows for the user and all around nice effect. Now in Windows Presentation Foundation this is referred to as a halo effect or outer glow and can be done with a bitmap effect. Silverlight does not have the equivalent of this. To create this in Silverlight you need to roll your own version. I have created a user control that you can use to generate this effect.

First off, the way I would like to do this is to extend the border class, creating my own custom border that has extended properties to render the halo. However the border (and rectangle for that matter) are both sealed classes so we have to go another route. The most straight forward approach to me was to create a user control and go with the layered approach rather than the container route.

Here is an example with the halo:

SearchWithShadow

And without the halo:

SearchNoShadow

The XAML to define the user control shown above. This is the XAML for the container control and shadow only.

<AmazonAG:BorderShadow x:Name="SearchShadow"  Height="195" Width="330" Canvas.Left="0" Canvas.Top="0" Fill="#FFFFFFFF" Stroke="#FFCC4E06" StrokeThickness="1" FillOpacity="0.8" CornerRadius="5" ShadowWidth="7" />

In its simplest terms this is a rectangle defined by the Width, Height, Fill, Stroke, StrokeThickness, FillOpacity and CornerRadius properties. The rectangle is generated based on these properties and the shadow is added dynamically around it. The rectangle properties are the same as those for a standard rectangle, so the Fill, Stroke properties can be used to apply gradient brushes as well.

The ShadowWidth property is used to determine the thickness of the shadow itself. In a slight twist, I didn't like a linear gradient for the shadow. The effect just didn't look good to me, so I used a sine calculation for the curve of the alpha in the shadow. I think this creates a much better fade effect on the background. You can review this code in the SinAlpha method below.

A nice addition would be to add the ability to determine the darkness of the halo and possibly the color of the halo. Currently the halo effect is gray scale beginning at 48 calculating a sine wave to 0 opacity. I have included all of the code for the user control. Have fun, let me know what you think.

Here is the XAML for the user control:

<UserControl x:Class="AmazonAG.BorderShadow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Loaded="UserControl_Loaded" SizeChanged="UserControl_SizeChanged"
    >
    <Canvas x:Name="LayoutRoot" />
</UserControl>

As you can see the XAML is just a place holder for my control, all of the heavy lifting to generate the halo is in the code behind.

Here is the code behind the user control:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;

namespace AmazonAG
{
    public partial class BorderShadow : UserControl
    {
        #region Variables

        // list of border controls used to generate the halo effect
        private List<Border> _borderShadows = new List<Border>();

        // base rectangle control - the halo surrounds this control
        private Rectangle _rectangleFill = new Rectangle();

        #endregion

        #region Properties

        private int _shadowWidth = 7;

        public int ShadowWidth
        {
            get { return _shadowWidth; }
            set { _shadowWidth = value; }
        }

        private double _cornerRadius = 0;

        public double CornerRadius
        {
            get { return _cornerRadius; }
            set { _cornerRadius = value; }
        }

        public Brush Fill { get; set; }

        private double _fillOpacity = 1.0;

        public double FillOpacity
        {
            get { return _fillOpacity; }
            set { _fillOpacity = value; }
        }

        public Brush Stroke { get; set; }

        private double _strokeThickness = 1.0;

        public Double StrokeThickness
        {
            get { return _strokeThickness; }
            set { _strokeThickness = value; }
        }

        #endregion

        #region Control Events

        public BorderShadow()
        {
            InitializeComponent();
        }

        private void UserControl_Loaded(object sender, RoutedEventArgs e)
        {
            double width = this.Width;
            double height = this.Height;

            for (int i = 0; i < _shadowWidth; i++)
            {
                Border newBorder = new Border();

                width += 2;
                height += 2;

                newBorder.Width = width;
                newBorder.Height = height;

                newBorder.SetValue(Canvas.LeftProperty, (double)(-i - 1));
                newBorder.SetValue(Canvas.TopProperty, (double)(-i - 1));
                newBorder.BorderBrush = new SolidColorBrush(Color.FromArgb(SinAlpha(i), 0, 0, 0));
                newBorder.BorderThickness = new Thickness(1);

                newBorder.CornerRadius = new CornerRadius(_cornerRadius + (i + 1));
                _borderShadows.Add(newBorder);
                this.LayoutRoot.Children.Add(newBorder);
            }

            AddRectangleFill();

       }

        private void UserControl_SizeChanged(object sender, SizeChangedEventArgs e)
        {
            double width = this.Width;
            double height = this.Height;

            _rectangleFill.Width = width;
            _rectangleFill.Height = height;
            for (int i = 0; i < _shadowWidth; i++)
            {
                Border borderShadow = _borderShadows[i];

                width += 2;
                height += 2;

                borderShadow.Width = width;
                borderShadow.Height = height;
            }

        }

        #endregion

        #region Public Events

        #endregion

        #region Public Methods

        #endregion

        #region Private Methods

        private void AddRectangleFill()
        {
            if (Fill != null || Stroke != null)
            {
                _rectangleFill.Width = this.Width;
                _rectangleFill.Height = this.Height;

                if (Fill != null) _rectangleFill.Fill = Fill;

                if (Stroke != null)
                {
                    _rectangleFill.Stroke = Stroke;
                    _rectangleFill.StrokeThickness = StrokeThickness;
                }

                _rectangleFill.Opacity = FillOpacity;

                _rectangleFill.RadiusX = _cornerRadius;
                _rectangleFill.RadiusY = _cornerRadius;

                this.LayoutRoot.Children.Add(_rectangleFill);
            }
        }

        private byte SinAlpha(int i)
        {
            double maxAlpha = 48;
            double alphaSlice = (maxAlpha * i) / _shadowWidth;
            double alphaMultiplier = Math.Sin((Math.PI / 2) * (alphaSlice / maxAlpha));

            byte alpha = Convert.ToByte(maxAlpha - (maxAlpha * alphaMultiplier));

            return alpha;
        }

        #endregion

    }
}

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Posted by: Tim Miller
Posted on: 7/22/2008 at 9:32 PM
Tags: , , ,
Actions: E-mail | Kick it! | DZone it! | del.icio.us
Post Information: Permalink | Comments (0) | Post RSSRSS comment feed

Shop Amazon Demo

Ever since Silverlight 2.0 Beta1 was released I have been working on a Silverlight front end to Amazon services. Well I finally have version 0.5 ready for viewing! View Shop Amazon with Silverlight

If this were a game, I would refer to it as a "first playable". Which means about 50% of the functionality is there, and you can mess around with it. But there are definitely quirks, and I am sure you can break it at this point. When I have it completed, I will blog about some of the more interesting areas of the code.

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Posted by: Tim Miller
Posted on: 4/21/2008 at 6:56 PM
Actions: E-mail | Kick it! | DZone it! | del.icio.us
Post Information: Permalink | Comments (1) | Post RSSRSS comment feed

Fox Valley Day of .Net

Hard to believe it has already been nearly a month since we held the Fox Valley Day of .Net. John Ptacek of Skyline Technologies (on of my co-workers) was kind enough to present a great talk on Silverlight. We both worked on the material for the talk, but John did the hard part and actually got in front of everyone to present. He did a fantastic job, anyway here are the Power point slides from the presentation.

We also showed a demo of an application that I have been working on. I am building a fairly simple Silverlight front end application to Amazon. View Shop Amazon Demo 

I have been busy working with the Silverlight 2.0 Beta 1 since its release. I thought this would be a good showcase for what I have been working with. It combines a fairly unique interface with Amazon REST services to search Amazon for books by author or title.

Silverlight 2.0 is amazing. Every now and then, Microsoft really nails it, and they sure have with this. The further I dig the more impressed I am. It is part of why I stopped blogging for the last month, I have spent all of my free computer time (which isn't much) delving deeper into Silverlight. I will begin posting entries about the more interesting work that I have completed.

In the meantime take a look at the demo and let me know what you think. I have already started to change the way the images of books are displayed. I thought the ellipse was a cool effect at first, but I think I have come up with a more efficient method to display the books. I should have the new version completed over the next two weeks.

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Posted by: Tim Miller
Posted on: 4/13/2008 at 7:35 PM
Actions: E-mail | Kick it! | DZone it! | del.icio.us
Post Information: Permalink | Comments (0) | Post RSSRSS comment feed