SpinningCursor4-TestPage

Series History

 

Introduction

The goal of these posts is to build a spinning cursor similar to the Mac OS X wait cursor through programmatic means in Silverlight. The cursor is still very rough and will undergo improvements progrressively. One of the reasons to build the cursor programmatically is to have more control over the output such as changing the number of slices or rotation or other parameters.

For this post, I added an animation to the rotation angle of the canvas which causes the slices to spin. I also added a simple navigation to the previous examples.

Step 1: Adding the Animation

In the previous post, I had one canvas for the background image as well as the slices. This time I realized I needed two canvases: one for the background and one for the slices. I needed separate canvases so that I could apply a rotation transform animation to the slices without affecting the background image. I used Expression Blend 2.0 sp1 and selected the "SpinningCanvas" and added an Storyboard named "SpinStoryboard". I then changed the Angle of the RotateTransform from 0 - 360° for the time range 0.0 - 2.0 sec.

SpinningCursor4-BlendAnimation

This resulted in the following XAML:

<UserControl.Resources>
    <Storyboard x:Name="SpinStoryboard" RepeatBehavior="Forever">
        <DoubleAnimationUsingKeyFrames
            BeginTime="00:00:00"
            Storyboard.TargetName="SpinningCanvas"
            Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[2].(RotateTransform.Angle)">
            <SplineDoubleKeyFrame KeyTime="00:00:02" Value="360"/>
        </DoubleAnimationUsingKeyFrames>
    </Storyboard>
</UserControl.Resources>

Step 2: Starting the Animation

When the control is created or updated, the Update() method is called. I modified the method as follows:

void Update()
{
    SpinStoryboard.Stop();
    CursorCanvas.Children.Clear();
    SpinningCanvas.Children.Clear();
    CreateOutlineGuides(CursorCanvas);
    CreateBackground(CursorCanvas);
    CreateSlicePaths(SpinningCanvas);
    SpinStoryboard.Begin();
}

Step 3: Adding the Sample Navigation

I wanted to support future samples, so I used reflection to find all of the UserControls not including App or Page. I used a Dictionary<string, Type> to map the Type.Name with the Type in case I need it for future samples. I then dynamically created a button for each sample.

void Page_Loaded(object sender, RoutedEventArgs e)
{
    Assembly assembly = Assembly.GetExecutingAssembly();
    Type[] exportedTypes = assembly.GetExportedTypes();
    foreach (Type t in exportedTypes)
    {
        // Testing various inheritance detection methods
        bool isNotApp = (t != typeof(App));
        bool isNotSelf = (t != this.GetType());
        bool isUserControl = t.IsSubclassOf(typeof(UserControl));

        if (isNotApp && isNotSelf && isUserControl)
        {
            _implementedTypes.Add(t.Name, t);
        }
    }

    Button lastButton = null;

    // Sort the names of the UserControls to make more sense
    List<string> sortedKeys = new List<string>(_implementedTypes.Keys);
    sortedKeys.Sort();

    for (int index = 0; index < _implementedTypes.Count; ++index)
    {
        string key = sortedKeys[index];
        Type t = _implementedTypes[key];
        lastButton = AddButton(t, index);
    }

    // Use the last UserControl for the initial display
    if (lastButton != null)
    {
        Button_Click(lastButton, null);
    }
}

To make it simple, I used name of the UserControl for the Button.Content and in the Button_Click event I used System.Activator to create an instance of the class.

private void Button_Click(object sender, RoutedEventArgs e)
{
    UIElement control = null;

    try
    {
        Button button = sender as Button;
        string typeName = button.Content as string;
        Type type = _implementedTypes[typeName];
        object instance = System.Activator.CreateInstance(type);
        control = instance as UIElement;
    }
    catch (Exception ex)
    {
        System.Diagnostics.Debug.WriteLine(ex.ToString());
    }

    WorkArea.Children.Clear();
    if (control != null)
        WorkArea.Children.Add(control);
}

Conclusion

The animation looks nice and adds a lot to the overall effect. For the next part, I plan to dynamically create the animation instead of using Blend and perhaps improve the background shape to more closely resemble the Mac OS X spinning cursor.

kick it on DotNetKicks.com
Technorati tags: ,