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.
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.
Technorati tags:
C#,
Silverlight