Kirk Evans Blog

.NET From a Markup Perspective

Creating a Silverlight Carousel and Gel Buttons Through Live Search

Creating a Silverlight Carousel and Gel Buttons Through Live Search

Rate This
  • Comments 4

One of my coworkers, Rob Wilson, did a fantastic interview with me that will be appearing on our new Channel9 show, "Communicating", as soon as I can get it edited, encoded, and uploaded.  In exchange, he asked for some Silverlight help.  I learned a few hard lessons along the way and thought I would share them (as well as the resources where I found the answers).

Let's start by looking at the final product.

What's cool about this is that the images are round and float seamlessly in front of the center image. I also like how the gel buttons turned out. This article is going to show how to achieve both effects.

Creating Gel Buttons in Silverlight

First, let's see how we did the gel buttons.  I have to completely give credit on this one to Jacob Sanford... he created a really great screencast that demonstrates how to do this, "Creating a Gel (Glass) Button in Silverlight, Part 1".  The real trick here is to create 2 rectangles with a LinearGradientBrush that overlap each other.  Both rectangles set the RadiusX and RadiusY properties to round their corners.  The part that I added to Jacob's demo code is setting the Name property of the containing Canvas as well as the darker color in the button.  I also added handlers for MouseEnter, MouseLeave, and MouseLeftButtonDown events.

    <!-- Gel button -->
    <Canvas Height="40" Width="100" Cursor="Hand" 
Name="button1" MouseEnter="buttonEnter" 
MouseLeave="buttonLeave" MouseLeftButtonDown="buttonDown">
      <Rectangle  Height="40" Width="100" RadiusX="15" RadiusY="15" Stroke="#FF0011FF" StrokeThickness="2">
        <Rectangle.Fill>
          <LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
            <GradientStop Color="#4444FF" Offset="0" Name="color1"/>
            <GradientStop Color="#6DACFF" Offset="1"/>
          </LinearGradientBrush>
        </Rectangle.Fill>
      </Rectangle>
      <Rectangle Canvas.Left="4" Width="92" Height="20" Canvas.Top="2" 
          RadiusX="13" RadiusY="13" Opacity="0.9">
        <Rectangle.Fill>
          <LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
            <GradientStop Color="White" Offset="0"/>
            <GradientStop Color="Transparent" Offset="1"/>
          </LinearGradientBrush>
        </Rectangle.Fill>
      </Rectangle>
      <TextBlock Text="AT&amp;T" FontFamily="Arial Black" 
          Canvas.Left="25" Canvas.Top="10" Foreground="White" />
    </Canvas>

We want to be able to respond to mouse events with our button in a way that users are familiar with.  When you hover over the button, you expect a visual cue that it is a button.  We can do that by handling the MouseEnter and MouseLeave events for the canvas.  I am using Silverlight 1.0 here, but you could do the same thing with Silverlight 2.0... just handle the mouse event and change the darker color in your button to the lighter color, then change it back when the mouse leaves.  When the mouse is pressed, you can do whatever you want (here, I just open up a new browser window that points to AT&T's web site).

function buttonEnter(s)
{   
    var idx = s.name.replace("button","");    
    s.findName("color" + idx).color = "#6DACFF";
    
}

function buttonLeave(s)
{
    var idx = s.name.replace("button","");    
    s.findName("color" + idx).color = "#4444FF";
}

function buttonDown(s)
{
    window.open("http://www.att.com");
}

You can see that the code is pretty simple... you handle the mouse events on the containing canvas, I just use that to locate and change the color in the LinearGradientBrush.

Creating Round Shapes With Transparent Areas

The next part took me awhile to figure out. The samples that I have seen using carousels in Silverlight all used square shapes. I wanted to use round shapes. I talked with my teammate and Silverlight guru, Michael Scherotter, and he suggested trying either a transparent GIF created by using Paint.NET (mad props, I haven't used this before... what an excellent program!), or using an ImageBrush with an elipse. Silverlight threw errors when I tried to use a transparent GIF, so back to the image brush. A JPG can't have a transparent area, so I needed to use Silverlight to only show part of the image.  For instance, look at the image against a black background:

Sure, I could just color the image background black, but I might want to use this on a different color canvas.

A quick search on Live.com yielded an article by my friend Dan Wahlin, "Silverlight XAML Primer 3: Working with Image Brushes" that was straightforward enough.  Create an elipse that is the same as the target area and set the Stretch property to None.

  <Canvas Name="imagesHolder" Width="300" Canvas.Top="0" 
    Canvas.Left="0">
    <!-- Center logo -->
    <Canvas Name="mainImageHolder" Canvas.ZIndex="-3" Opacity="1.0" 
         Canvas.Left="130" Canvas.Top="60">
      <Ellipse Width="125" Height="125" Canvas.ZIndex="-2">
        <Ellipse.Fill>
          <ImageBrush ImageSource="images/logo.jpg" Stretch="None"/>
        </Ellipse.Fill>
      </Ellipse>      
    </Canvas>
  </Canvas>

Notice that the Z-Index is set to -2.  I am doing this because I am going to rotate other images in front of the center logo.  Which is a nice segue to the next topic... dynamically adding the images.

Dynamically Adding Canvas Elements in Silverlight

I explained dynamically adding canvas elements in my article, "Microsoft Silverlight and Truveo Video Search, Part Two".  This bit of code uses the same technique, simply creating the Canvas elements and their containing elements within the JavaScript itself and using the CreateFromXaml API call.  You can see that I use the same Ellipse + ImageBrush technique here for rendering the circular images that will rotate around the statically placed center image.

function buildImages() 
{
    for(i=0;i<num_imgs;i++) {
    
        cur_img=i+".jpg"        
        img_url="images/"+cur_img         
        left_pos=0
        
        xaml_str='<Canvas Name="imageHolder_'+i+'" Canvas.Left="'+
left_pos+'" Canvas.Top="0"  MouseLeftButtonDown="buttonDown">'                                    
        xaml_str +=        '<Ellipse Width="'+img_width+'" Height="'+
img_height+'" Canvas.ZIndex="20">'
        xaml_str +=        '   <Ellipse.Fill>'
        xaml_str +=        '       <ImageBrush ImageSource="'+
img_url+'" Stretch="None"></ImageBrush>'          
        xaml_str +=        '   </Ellipse.Fill>'
        xaml_str +=        '</Ellipse>'                                      
        xaml_str +='  <Canvas.RenderTransform><ScaleTransform 
Name="st_'+i+'" ScaleX="1" ScaleY="1" CenterX="50" CenterY="50"/>
</Canvas.RenderTransform>'            
        xaml_str+='</Canvas>'

        xamlTags=plugin.content.createFromXaml(xaml_str)
        imagesHolder.children.add(xamlTags)        
        
        
        objsArr["image_"+i]=new Object()
        objsArr["image_"+i].angle=i*((Math.PI*2)/num_imgs)    
    }

    positionItems()
    moveItemsInt=setInterval("positionItems()",25)
}

Animating the Carousel Effect in Silverlight

The next part to look at is how to get the pictures to rotate around each other.  Again, I can't claim much credit here, I downloaded and modified an example from VectorForm's blog, "Image Carousel".  First, we'll set up some constants to control things like rotation radius, position on the X and Y axes, speed of rotation, and the number of images to rotate.

var plugin
var main
var objsArr=new Array()

//Constants to control number of images,
//screen realestate, radius of the spin,
//and speed of spin
var img_width=75
var img_height=75
var num_imgs=5

var radiusX=150
var radiusY=75
var centerX=150
var centerY=75

var speed=-0.01

The rest of the code boils down to moving the images left and right according to a sin wave and moving up and down based on a cos wave. It also uses a ScaleTransform to grow and shrink the image according to the current sin and cos calculations.  There was also handling in the original source for controlling the spin speed based on clicking on the left or right of the canvas.  To simplify for my needs, I wanted it to continually spin at a constant rate.  Here's the result.

function positionItems() 
{
    for(i=0;i<num_imgs;i++) 
    {    
        var my_x=Math.cos(objsArr["image_"+i].angle)*radiusX + centerX
        var my_y=Math.sin(objsArr["image_"+i].angle)*radiusY + centerY

        main.findName("imageHolder_"+i)["Canvas.Left"]=my_x
        main.findName("imageHolder_"+i)["Canvas.Top"]=my_y

        var stRef=main.findName("st_"+i)

        var sc = (my_y  - stRef.scaleY) / (centerY+radiusY-stRef.scaleY)

        stRef.scaleX=sc
        stRef.scaleY=sc

        objsArr["image_"+i].angle+=speed
        main.findName("imageHolder_"+i)["Canvas.ZIndex"]=parseInt(my_y)
    }
}

This function is called from the buildImages() method with a setInterval() call that fires once every 25 milliseconds.  To the end user, it looks like the ball is continuously moving in orbit around the center logo.

As you can see, I was simply modifying the work of others to achieve an effect. All the props to the original authors for sharing their knowledge and source.

Page 1 of 1 (4 items)
Leave a Comment
  • Please add 8 and 6 and type the answer here:
  • Post
Translate This Page
Search
Archive
Archives