Spinning Wait Symbol in Silverlight, Part4


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: ,

author: Ryan Van Slooten | posted @ Thursday, November 06, 2008 2:03 AM | Feedback (0)

Spinning Wait Symbol in Silverlight, Part3


SpinningCursor3-TestPage

Series History

In this post, I add curvature to the slices and refactor the code to support upcoming features. The goal of these posts is to build a spinning cursor similar to the Mac OS X wait cursor through programmatic means in Silverlight. One of the reasons to build the cursor programmatically is to create it will different number of slices or rotation or other parameters. At this point, the cursor is still very rough and just beginning to resemble the final result. In upcoming posts, I will animate the cursor and adjust the appearance to more closely resemble the desired cursor.

Step 1: Adjust the cursor properties

I added AlternateSlices and Origin properties, and also DefaultSliceCount and DefaultRotationAngle to the class.

public static readonly int DefaultSliceCount = 10;
public static readonly double DefaultSliceRotationAngle = 360.0 / DefaultSliceCount;

/// <summary>
/// Show alternating slices (true) or all slices (false)        
/// </summary> 
public bool AlternateSlices { get; set; }

/// <summary>        
/// The origin of the ellipse for the spinning cursor 
/// </summary> 
public Point Origin { get; private set; }

Step 2: Set the default parameter values

I specified the default slice count and the radius and origin of the cursor.

void Page_Loaded(object sender, RoutedEventArgs e)
{
    txtVersion.Text = this.GetType().Name;
    AlternateSlices = true;
    SliceCount = DefaultSliceCount;
    txtSliceCount.Text = Convert.ToString(SliceCount);

    SliceRotationAngle = DefaultSliceRotationAngle;
    txtRotation.Text = Convert.ToString(SliceRotationAngle);

    RadiusX = SpinningCanvas.Width / 2.0;
    RadiusY = SpinningCanvas.Height / 2.0;
    Origin = new Point(RadiusX, RadiusY);

    Update();
}

Step 3: Create a background circle (ellipse)

I used a simple linear gradient for now because the final gradient looks much more complicated and will take more time.

void CreateBackground(Canvas cursorCanvas)
{
    GradientBrush brush = new LinearGradientBrush();
    GradientStop stop1 = new GradientStop();
    stop1.Color = Color.FromArgb(255, 255, 0, 0);
    stop1.Offset = 0.25;
    brush.GradientStops.Add(stop1);

    GradientStop stop2 = new GradientStop();
    stop2.Color = Color.FromArgb(255, 0, 255, 0);
    stop2.Offset = 0.5; // cursorCanvas.Width / 2.0;
    brush.GradientStops.Add(stop2);

    GradientStop stop3 = new GradientStop();
    stop3.Color = Color.FromArgb(255, 0, 0, 255);
    stop3.Offset = 0.75; // cursorCanvas.Width;
    brush.GradientStops.Add(stop3);

    Ellipse ellipse = new Ellipse();
    ellipse.Height = cursorCanvas.Height;
    ellipse.Width = cursorCanvas.Width;
    ellipse.Fill = brush;
    cursorCanvas.Children.Add(ellipse);
}

Step 4: Implement the alternating slices

Implementing the alternating slices was simple:

/// <summary>
/// Create an ellipse using rotated slices to build the ellipse
/// </summary>
void CreateSlicePaths(Canvas cursorCanvas)
{
    // Create Slices
    for (int index = 0; index < SliceCount; ++index)
    {
        PathFigure pathFigure = CreateSliceFigure();

        PathGeometry pathGeometry = new PathGeometry();
        pathGeometry.Figures.Add(pathFigure);

        Path path = new Path();
        path.Stroke = new SolidColorBrush(Color.FromArgb(128, 0, 0, 0));
        path.StrokeThickness = 1.0;
        path.Fill = new SolidColorBrush(Color.FromArgb(192, 128, 128, 128));
        path.Data = pathGeometry;

        // Rotate the slice for all slices after the first slice
        if (index > 0)
        {
            RotateTransform t1 = new RotateTransform();
            t1.CenterX = RadiusX;
            t1.CenterY = RadiusY;
            t1.Angle = SliceCenterAngle * index;

            TransformGroup transformGroup = new TransformGroup();
            transformGroup.Children.Add(t1);
            path.RenderTransform = transformGroup;
        }

        cursorCanvas.Children.Add(path);

        if (AlternateSlices)
            ++index;
    }
}

Step 5: Add the curvature to the slices

The curvature isn't perfect, but it looks fine with the default settings (10 slices and 36° rotation). I used a Bezier curve, but perhaps the standard arc would work better in the future.

// Curve control weighting
double curveWeight = 0.75;

// Create the first line
BezierSegment seg1 = new BezierSegment();
seg1.Point1 = point0;
seg1.Point2 = CalculatePointOnEllipse(0.0, RadiusX * curveWeight, RadiusY * curveWeight, Origin);
seg1.Point3 = point1;
pathFigure.Segments.Add(seg1);

// Use an arc for the circular side
ArcSegment seg2 = new ArcSegment();
seg2.Point = point2;
seg2.Size = new Size(RadiusX, RadiusY);
seg2.RotationAngle = SliceCenterAngle;
seg2.IsLargeArc = (SliceCenterAngle > 180.0);
seg2.SweepDirection = SweepDirection.Counterclockwise;
pathFigure.Segments.Add(seg2);

// Close shape by going back to the starting point
BezierSegment seg3 = new BezierSegment();
seg3.Point1 = point2;
seg3.Point2 = CalculatePointOnEllipse(SliceCenterAngle, RadiusX * curveWeight, RadiusY * curveWeight, Origin);
seg3.Point3 = point0;
pathFigure.Segments.Add(seg3);

Conclusion

The spinning cursor now has a dynamic number of slices and rotation angle, and it is starting to look more like the desired result. In the next post, I will animate the cursor to give it the spinning effect.

Technorati tags: ,
kick it on DotNetKicks.com

author: Ryan Van Slooten | posted @ Wednesday, October 29, 2008 11:16 AM | Feedback (2)

Spinning Wait Symbol in Silverlight, Part 2


After my previous spinning wait symbol, I decided to see how difficult it would be to create a Silverlight version of the Mac OSX wait cursor that I referenced in the previous post. The Mac OSX cursor is commonly referred to as the "Spinning Pizza of Death" or the "Marble of Doom" and in fact there is a Marble of Doom web site dedicated to the amount of time spent waiting while watching the spinning cursor. The Marble of Doom web site has a very nice and large version of the cursor using Flash although it doesn't have any vector information but is using video frames (they probably just published the final product and did not include the vector/animation information). The purpose of this post is to programmatically build the cursor and then in later posts to animate it.

Step 1: Decide on the initial interface properties

I realized quickly that I would need a little geometry to programmatically build the cursor, but the first step was to build the interface requirements. The essential properties were:

public int SliceCount { get; set; }
public double SliceCenterAngle { get; private set; }
public double SliceRotationAngle { get; set; }
public double RadiusX { get; set; }
public double RadiusY { get; set; }

The SliceCount determines how many slices or divisions to create, and the SliceCenterAngle is simply 360° / SliceCount. The SliceRotationAngle is the angle to twist or bend the slice. I decided to have a RadiusX and RadiusY to support ellipses in the future as well.

Step 2: Manually create a slice

Manual slice in Blend Before I could programmatically create a slice, I needed to find out how to create a slice using XAML and Blend. The points on the slice would be in the center of the circle, and then two points on the circle determined by the SliceCenterAngle. The biggest question was how to create the arc and maintain the circular appearance. Fortunately, the Geometry Overview on MSDN was very helpful and got me started on the right track with the PathGeometry. I was able to create the simplest scenario with a single slice from a circle with four slices:

<Path Stroke="Black" StrokeThickness="1">
    <Path.Data>
        <PathGeometry>
            <PathGeometry.Figures>
                <PathFigure StartPoint="50,50">
                    <PathFigure.Segments>
                        <LineSegment Point="0,50" />
                        <ArcSegment Size="50,50" IsLargeArc="False"
                            RotationAngle="90" SweepDirection="CounterClockwise" Point="50,100" />
                        <LineSegment Point="50,50" />
                    </PathFigure.Segments>
                </PathFigure>
            </PathGeometry.Figures>
        </PathGeometry>
    </Path.Data>
</Path>

The next step was to create the same quarter-circle except with two slices:

<Path Stroke="Black" StrokeThickness="1">
    <Path.Data>
        <PathGeometry>
            <PathGeometry.Figures>
                <PathFigure StartPoint="50,50">
                    <PathFigure.Segments>
                        <LineSegment Point="0,50" />
                        <ArcSegment
                            Size="50,50"
                            Point="14.645,85.355" 
                            RotationAngle="45" 
                            IsLargeArc="False"
                            SweepDirection="CounterClockwise" 
                        />
                        <LineSegment Point="50,50" />
                    </PathFigure.Segments>
                </PathFigure>
                
                <PathFigure StartPoint="50,50">
                    <PathFigure.Segments>
                        <LineSegment Point="14.645,85.355" />
                        <ArcSegment
                            Size="50,50"
                            Point="50, 100" 
                            RotationAngle="45" 
                            IsLargeArc="False"
                            SweepDirection="CounterClockwise" 
                        />
                        <LineSegment Point="50,50" />
                    </PathFigure.Segments>
                </PathFigure>
            </PathGeometry.Figures>
        </PathGeometry>
    </Path.Data>
</Path>

Step 3: Create a slice programmatically

The general idea is to create one slice and then rotate the slice around the circle to create the complete circle.

/// <summary>
/// Create an ellipse using rotated slices to build the ellipse
/// </summary>
void CreateSlicePaths(Canvas cursorCanvas)
{
    SliceCenterAngle = 360.0 / SliceCount;

    // Create Slices
    for (int index = 0; index < SliceCount; ++index)
    {
        PathFigure pathFigure = CreateSliceFigure();

        PathGeometry pathGeometry = new PathGeometry();
        pathGeometry.Figures.Add(pathFigure);

        Path path = new Path();
        path.Stroke = new SolidColorBrush(Color.FromArgb(255, 0, 0, 0));
        path.StrokeThickness = 1.0;
        path.Data = pathGeometry;

        // Rotate the slice for all slices after the first slice
        if (index > 0)
        {
            RotateTransform t1 = new RotateTransform();
            t1.CenterX = RadiusX;
            t1.CenterY = RadiusY;
            t1.Angle = SliceCenterAngle * index;

            TransformGroup transformGroup = new TransformGroup();
            transformGroup.Children.Add(t1);
            path.RenderTransform = transformGroup;
        }

        cursorCanvas.Children.Add(path);
    }
}

/// <summary>
/// Create the base shape for the slice
/// </summary>
private PathFigure CreateSliceFigure()
{
    // Start at the center of the ellipse
    Point point0 = new Point(RadiusX, RadiusY);

    // Next point is the left side of the ellipse
    //Point point1 = new Point(0.0, RadiusY); // if no rotation
    Point point1 = CalculatePointOnEllipse(SliceRotationAngle);

    // Calculate the bottom point on the ellipse
    Point point2 = CalculatePointOnEllipse(SliceRotationAngle + SliceCenterAngle);

    // Starting point
    PathFigure pathFigure = new PathFigure();
    pathFigure.StartPoint = point0;

    // Create the first line
    LineSegment seg1 = new LineSegment();
    seg1.Point = point1;
    pathFigure.Segments.Add(seg1);

    // Use an arc for the circular side
    ArcSegment seg2 = new ArcSegment();
    seg2.Point = point2;
    seg2.Size = new Size(RadiusX, RadiusY);
    seg2.RotationAngle = SliceCenterAngle;
    seg2.IsLargeArc = (SliceCenterAngle > 180.0);
    seg2.SweepDirection = SweepDirection.Counterclockwise;
    pathFigure.Segments.Add(seg2);

    // Close shape by going back to the starting point
    LineSegment seg3 = new LineSegment();
    seg3.Point = new Point(point0.X, point0.Y);
    pathFigure.Segments.Add(seg3);

    pathFigure.IsClosed = true;
    pathFigure.IsFilled = true;

    return pathFigure;
}

/// <summary>
/// Returns a point on the ellipse based on the rotationAngle, using
/// RadiusX/Y as the center (0, 0).
/// </summary>
private Point CalculatePointOnEllipse(double rotationAngle)
{
    double angleRadians = rotationAngle * Math.PI / 180.0;
    double x = Math.Cos(angleRadians);
    x = RadiusX * x;
    double y = Math.Sin(angleRadians);
    y = RadiusY * y;

    Point result = new Point(x, y);
    result.X = RadiusX - x;
    result.Y = RadiusY + y;
    return result;
}

Step 4: Going Forward

SpinningCursor1-TestPage Obviously this still needs a lot of improvement before it approaches the appeal of the Marble of Doom, which I will work on in the coming posts. However, the initial effort to create the "pizza" slices has been achieved and it is easier to build upon a base.

I added a grid in the background when I had some difficulty with the path geometry, but it is useful to I added two text boxes for the number of slices and rotation angle so that I could see the shape update dynamically.

Since I just used LineSegments to connect the slice points, the shape does not have the swirl or twist effect yet. Next time I will add the twist as well as rotation.

Technorati tags: ,

kick it on DotNetKicks.com

author: Ryan Van Slooten | posted @ Monday, October 20, 2008 3:08 PM | Feedback (0)

Spinning Wait Symbol in Silverlight


My wife has a Treo with Windows Mobile and I when I was using it I noticed it had a cool rotating wait symbol, so I wondered how difficult it would be to build the symbol in Silverlight. The symbol is similar to the old BeOS wait cursor and has as well as the Mac OS X wait cursor which I've always thought looks nice. At one point in time I created a Windows cursor that duplicated the look of the BeOS cursor but I don't use it anymore. If I found a really nice looking 24-bit cursor then I might use it again.

Quadrant1The first step is to create the four quadrants. I used the path geometry to create each quadrant, for example the first quadrant is:

Data="M0,0 C0,0 100,0 100,100 L0,100 z"

For reference , the path markup syntax is documented at MSDN. The shape starts at (0,0) and creates a curve point with a control point at (100,0) and end point at (100,100). Then there is a LineTo (0,100) and finally the close ("z") marker to complete the shape. While this is fairly easy through a graphical editor such as Expression Blend (blog), I created this manually so that I could have more control over the exact coordinates rather than relying on the graphical editor.

I repeated the shape for each quadrant by adjusting the coordinates as necessary. It is also possible to simply apply a rotation to the shape so you have rotations of 90, 180, and 270 degrees, but remember to set the RenderTransformOrigin to the correct corner such as (0,1) to rotate by the bottom left corner.

Once the quadrants are built, then I placed a rotating quadrant named 'Spinner' over the combined shape. I used an alpha mask on the spinner to differentiate it from the other quadrants. Finally, I added a Storyboard to rotate the 'Spinner' 360 degrees every two seconds:

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

The end result is a nice looking animation that is fairly simple. I'm tempted to try to duplicate the Mac OS X wait cursor, but that will have to wait until I have more time.

Spinning Cursor in Blend

kick it on DotNetKicks.com
Technorati tags:

author: Ryan Van Slooten | posted @ Wednesday, October 08, 2008 8:03 AM | Feedback (0)

Bouncing Balls in Silverlight Part 1


BounceTest

Recently I wanted to make a very simple sample in Silverlight that used a little code to animate bouncing balls. The overall effect is fairly simple, but getting the sample down to the basics took a little time. As part of my research, I looked at several old bouncing ball demos using JavaScript and it was an eye-opening reminder of the dark ages of browsers and JavaScript.

For the sample, I wanted to keep everything very simple. I started with a circle (ellipse with the same height and width) in a canvas. In order to move the ball, I chose to position the ball at (0, 0) and use a TranslateTransform to adjust the position. You could easily move the ball around with Left and Top, but I also plan to use RotateTransform in later samples so it makes sense to use a TransformGroup to manipulate the object.

The XAML for the base page is just a Canvas and a rectangle that acts as a border. The ball is placed on the canvas using the common TransformGroup that is created by Expression Blend when you add a transform to an object. Expression Blend also uses the same order for the transforms (ScaleTransform, SkewTransform, RotateTransform, TranslateTransform) and will reorder the transforms back to this order if you adjust any transform using Blend (at least as of Blend 2.5 June 2008 Preview). Here is the XAML for the page:

<UserControl x:Class="BounceTest.Page"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    Width="400" Height="300">
    <Canvas x:Name="LayoutRoot">
        <Rectangle x:Name="Boundary" Height="300" Width="400" 
            Canvas.Left="0" Canvas.Top="0"
            Fill="#FFFFFFFF" Stroke="#FF000000" />
        <Ellipse x:Name="Ball01" Height="25" Width="25"
            Canvas.Left="0" Canvas.Top="0"
            Fill="#FFEE3131" Stroke="#FF000000"
            RenderTransformOrigin="0.5,0.5">
            <Ellipse.RenderTransform>
                <TransformGroup>
                    <ScaleTransform/>
                    <SkewTransform/>
                    <RotateTransform/>
                    <TranslateTransform X="50" Y="50"/>
                </TransformGroup>
            </Ellipse.RenderTransform>
        </Ellipse>
    </Canvas>
</UserControl>

I created a small class to manage the position and velocity of the ball. The constructor takes the source shape (the ball), the velocity, and the boundary shape. For this scenario, I assume that the ball already has the RotateTransform otherwise it throws an exception. The code could easily create a RotateTransform if it isn't present. The Update method takes the elapsed time since the previous method call. The boundary checking is extremely simple and just changes the direction of the motion when the ball hits a boundary edge. This obviously needs a lot of work for more complex scenarios, but it will do for this simple case.

public class ShapeVelocity
{
	public Shape shape;
	public Vector velocity;
	public TranslateTransform translate;
	public Size bounds;
	public Size container;

	public ShapeVelocity(Shape AShape, Vector AVelocity, Shape BoundsContainer)
	{
		this.shape = AShape;
		this.velocity = AVelocity;

		var renderTransform = this.shape.RenderTransform;
		if (renderTransform is TransformGroup)
		{
			TransformGroup transformGroup = (TransformGroup)renderTransform;
			foreach (Transform transform in transformGroup.Children)
				if (transform is TranslateTransform)
					this.translate = (TranslateTransform)transform;
		}
		if (this.translate == null)
			throw new ArgumentException("Shape must have a TranslateTransform in it");

		container = new Size(BoundsContainer.Width, BoundsContainer.Height);
		bounds = new Size(
			this.shape.ActualWidth + this.shape.StrokeThickness,
			this.shape.ActualHeight + this.shape.StrokeThickness);
	}

	public void Update(TimeSpan Interval)
	{
		Rect pos = new Rect(
			translate.X,
			translate.Y,
			bounds.Width,
			bounds.Height);

		if ((velocity.X < 0.0) && (pos.Left < 0.0))
			velocity.X = -velocity.X;
		else if ((velocity.X > 0.0) && (pos.Right > container.Width))
			velocity.X = -velocity.X;
		if ((velocity.Y < 0.0) && (pos.Top < 0.0))
			velocity.Y = -velocity.Y;
		else if ((velocity.Y > 0.0) && (pos.Bottom > container.Height))
			velocity.Y = -velocity.Y;

		translate.X += velocity.X * (double) Interval.Milliseconds / 1000.0;
		translate.Y += velocity.Y * (double) Interval.Milliseconds / 1000.0;
	}
}

In anticipation of future examples, I used a List<Shape> collection to store the ball shape to updae the position. For the motion, I used the StoryBoard instead of a DispatcherTimer based on the recommendation of Adam Kinney.

public partial class Page : UserControl
{
    private DateTime _lastTime = DateTime.MinValue;
    private double _initialSpeed = 50.0;
    private Storyboard _storyboard;
    private List<ShapeVelocity> _shapes;

    public Page()
    {
        InitializeComponent();
        _shapes = new List<ShapeVelocity>();
        _storyboard = new Storyboard();
        _storyboard.Duration = TimeSpan.FromMilliseconds(10);
        _storyboard.Completed += new EventHandler(storyboard_Tick);
        this.Loaded += new RoutedEventHandler(Page_Loaded);
    }

    void Page_Loaded(object sender, RoutedEventArgs e)
    {
        _shapes.Add(new ShapeVelocity(Ball01, new Vector(_initialSpeed, _initialSpeed), Boundary));
        _lastTime = System.DateTime.Now;
        _storyboard.Begin();
    }

    void storyboard_Tick(object sender, EventArgs e)
    {
        DateTime now = System.DateTime.Now;
        TimeSpan interval = now - _lastTime;
        foreach (ShapeVelocity s in _shapes)
        {
            s.Update(interval);
        }
        _lastTime = now;
        _storyboard.Begin();
    }
}

So that is the very simple bouncing ball sample.

kick it on DotNetKicks.com

Technorati tags: ,

author: Ryan Van Slooten | posted @ Sunday, September 14, 2008 11:28 PM | Feedback (0)

Constant Prototyping and Design


BalletA co-worker gave a presentation on a project we worked on a long time ago and he asked me to update an feature in the web application. The web app was originally programmed in ASP and I had almost forgotten how painful it was to work in that environment. The new request was to enable live presence information in a web part using the new RTC libraries. Fortunately, another developer had a component to perform all of the calls using the RTC libraries, but I needed to retrieve the group members for the selected group and cross-reference the group members with online presence.

The actual details involved opening "MTSAdmin.Catalog.1" object and looking for a specific COM+ package, enumerating the "RolesInPackage", and then getting the "UsersInRole". There were several layers of filtering going on and I wanted an elegant solution. Unfortunately I kept trying to be too elegant and I was getting hung up on how to make it more elegant. I wanted to have a "before", "item", and "after" callback functions at each of the function depth levels where the "item" function would occur in the iteration loop. This would allow for scenarios where items might need to be collected after iteration, particularly to avoid making multiple unnecessary calls to the lower filtering methods.

Small example from jQuery

ajax: function( s ) {
  // Extend the settings, but re-extend 's' so that it can be
  // checked again later (in the test suite, specifically)
  s = jQuery.extend(true, s, jQuery.extend(true, {}, jQuery.ajaxSettings, s));

  // ... snip, snip, snip ...

  // Allow custom headers/mimetypes
  if ( s.beforeSend && s.beforeSend(xhr, s) === false ) {
    // cleanup active request counter
    s.global && jQuery.active--;
    // close opended socket
    xhr.abort();
    return false;
  }

The example above from jQuery only uses the "before" concept that I mentioned above, but it shows a very nice implementation where the 's' object is based on jQuery.ajaxSettings. Just before the AJAX call is about to be made, jQuery checks for any custom headers and calls the 'beforeSend' function if it exists.

Start Simple and Add Progressive Elegance

BaseballHomerun I did manage to get the function working correctly, but I had to take a step back and start simple and progressively build additional functionality into the component. Sometimes you have inspiration and you can just design and code and everything is great. Other times it can take a while before the inspiration comes. It is better to start coding and have something you can refine rather than wait and lose valuable time.

To use a baseball analogy: Don't try to hit a homerun every time; sometimes you need to play "small ball" and get people in scoring position and just try to make contact. A similar soccer (futból!) analogy would be don't just try to dribble down the field and score every time, but you need to pass and set up the players around you to maximize the scoring opportunity.

author: Ryan Van Slooten | posted @ Thursday, July 31, 2008 11:28 AM | Feedback (0)

On Sample Simplicity


I was working through a Communications sample of connecting SilverLight to POX, then Web Services, and finally WCF and I came across the following instructions:

(Part 1: POX) The Generic handler will process an incoming request using the code declared in the ProcessRequest function. This function should create some new instances of CityData and add them to the myCities list. Here are some examples of cities with longitude and latitude { (London, 51.5, 0), (Stratford-upon-Avon, 52.3, -1.71), (Edinburgh, 55.95, -3.16) }. See if you can write the code to do this.

(And later in the lab...)

In this example your ASHX served up hard-coded data for 1 city. Can you build it so that it can accept parameters on the URI string (i.e. http://localhost:8001/Sample1Web/GetData.ashx?city=whatever)?

(And even further in the lab...)

Write a function that takes in a country and builds a List<CityData> of several cities for that country. Here are some cities and their latitudes and longitudes:

("Paris", 48.87, 2.33);
("Lourdes", 43.1, 0.05);
("Toulouse", 43.6, 1.38);
("London", 51.5, 0);
("Stratford-Upon-Avon", 52.3, -1.71);
("Edinburgh", 55.95, -3.16);
("Berlin", 52.52, 13.42);
("Munich", 48.13, 11.57);
("Hamburg", 53.58, 9.98);

Provided Solution

The sample provided "solution" code at the end of the instructions. The code is basic and unimaginative, but perhaps that is all the is needed of sample code (I would offer that samples are the perfect to push the boundaries -- lead by example).

// File: CityData.cs
public class CityData
{
    public string CityName { get; set; }
    public double Latitude { get; set; }
    public double Longitude { get; set; }

    public CityData(string strCityName, double nLatitude, double nLongitude)
    {
        CityName = strCityName;
        Latitude = nLatitude;
        Longitude = nLongitude;
    }

    public CityData()
    {
    }
}

// File: GetData.ashx
public class GetData : IHttpHandler
{
    private List<CityData> getCities(string strCountry)
    {
        List<CityData> ret = new List<CityData>();

        switch (strCountry)
        {
        case "france":
            ret.Add(new CityData("Paris", 48.87, 2.33));
            ret.Add(new CityData("Lourdes", 43.1, 0.05));
            ret.Add(new CityData("Toulouse", 43.6, 1.38));
            break;

        case "uk":
            ret.Add(new CityData("London", 51.5, 0));
            ret.Add(new CityData("Stratford-Upon-Avon", 52.3, -1.71));
            ret.Add(new CityData("Edinburgh", 55.95, -3.16));
            break;

        case "germany":
            ret.Add(new CityData("Berlin", 52.52, 13.42));
            ret.Add(new CityData("Munich", 48.13, 11.57));
            ret.Add(new CityData("Hamburg", 53.58, 9.98));
            break;

        default:
            ret.Add(new CityData("London", 51.5, 0));
            ret.Add(new CityData("Stratford-Upon-Avon", 52.3, -1.71));
            ret.Add(new CityData("Edinburgh", 55.95, -3.16));
            break;
        }
        return ret;
    }
}

Alternative 1

I wanted to explore alternatives to the supplied code, so I decided to initialize the CityData list with the data instead of using "Add". In C++ this was more efficient (it set the initial list size instead of using the default capacity sizing algorithm among other things), but I'm not sure of performance in terms of C#. I would not expect any significant performance difference with this small sample though.

public List<CityData> getCities(string strCountry)
{
    List<CityData> ret = new List<CityData>();

    switch ( strCountry.ToLower() )
    {
        case "france":
            ret = new List<CityData>() {
                new CityData("Paris", strCountry, 48.87, 2.33),
                new CityData("Lourdes", strCountry, 43.1, 0.05),
                new CityData("Toulouse", strCountry, 43.6, 1.38)
            };
            break;

        case "germany":
            ret = new List<CityData>() {
                new CityData("Berlin", strCountry, 52.52, 13.42),
                new CityData("Munich", strCountry, 48.13, 11.57),
                new CityData("Hamburg", strCountry, 53.58, 9.98)
            };
            break;

        case "uk":
        default:
            strCountry = "UK";
            ret = new List<CityData>() {
                new CityData("London", strCountry, 51.5, 0),
                new CityData("Stratford-Upon-Avon", strCountry, 52.3, -1.71),
                new CityData("Edinburgh", strCountry, 55.95, -3.16)
            };
            break;
    }
    return ret;
}

Alternative 2

I haven't worked very much with LINQ, so I wanted to compare the same functionality using LINQ. I added a string "Country" to the CityData class and here is the result:

private static List<CityData> cityList = new List<CityData>()
{
    // UK
    new CityData("Paris", "UK", 48.87, 2.33),
    new CityData("Lourdes", "UK", 43.1, 0.05),
    new CityData("Toulouse", "UK", 43.6, 1.38),

    // France
    new CityData("Paris", "France", 48.87, 2.33),
    new CityData("Lourdes", "France", 43.1, 0.05),
    new CityData("Toulouse", "France", 43.6, 1.38),

    // Germany
    new CityData("Berlin", "Germany", 52.52, 13.42),
    new CityData("Munich", "Germany", 48.13, 11.57),
    new CityData("Hamburg", "Germany", 53.58, 9.98)
};

public List<CityData> getCities(string strCountry)
{
    IEnumerable<CityData> data = from city in cityList
                                 where city.Country == strCountry
                                 select city;
    return new List<CityData>(data);
}

This is still not the most efficient or optimal method, but I think it looks nice and is helps introduce LINQ in an understandable manner.

Conclusion

Samples and labs should be simple and yet it is a perfect time to include new technologies and push the boundaries so to speak. One thing I certainly don't want to see in samples are basic programming inefficiencies (things that make you cringe) due to lack of effort by the sample creator. That was not necessarily the case with this sample, but it would have been better to present alternatives such as the examples I listed.

Technorati tags: ,

author: Ryan Van Slooten | posted @ Friday, June 13, 2008 4:08 PM | Feedback (0)

Macro Guidelines for Excel VBA Beginners


I'm in the process of updating an Excel spreadsheet that is failing when it is running inside of Internet Explorer. The issue is related to the ActiveSheet and other global properties having a value of Nothing when the code is assuming they have valid references. As I am going through this spreadsheet, I am noting a wide variety of programming deficiencies and inefficiencies. Here is a list of some of the issues encountered:

If you run spreadsheets under Internet Explorer, use Application.ThisWorkbook and to ensure the browser evaluates the reference correctly.
NOTE: This is not an exhaustive list, nor do I claim that the "Better" examples are the best code example, but rather they are (hopefully) significant improvements over the "Bad" examples. In many cases, I have kept the design of the bad example so that you can see the differences however you should strive to completely refactor the code if possible.
  1. Do not abuse the "With" statement in VBA.

    Some people like "With" and some people don't and I don't particularly care for it. In particular, do not use nested "With" statements.

    Example of Bad Code

    ' ... BAD EXAMPLE: DO NOT USE ...
    With ActiveSheet
      With TestForm
        ' ...
      End With ' TestForm
    End With ' ActiveSheet
    ' ... BAD EXAMPLE: DO NOT USE ...
    
    
  2. Avoid using the "Goto" statement in VBA.

    "Goto" is almost always a bad idea and a sign of spaghetti code. There are very few cases were it is appropriate to use it -- very few cases!

    Example of Bad Code

    ' ... BAD EXAMPLE: DO NOT USE ...
    For i = iHere To lRealLastRow Step 2
      BuildStr = "A" & i + 1
      Range(BuildStr).Select
      If Range(BuildStr).Value = "---" Then GoTo DoneWithNames
      SelectItemDlg.Items_List.AddItem Range(BuildStr).Text
    Next
    
    DoneWithNames:
    ' ... BAD EXAMPLE: DO NOT USE ...
    

    Example of Better Code (I still would have designed it differently)

    ' ...
    Dim MyCell As Range
    For i = iHere To lRealLastRow Step 2
      Set MyCell = Cells(i + 1, 1)
      If MyCell.Value = "---" Then
        Exit For
      End If
      SelectItemDlg.Items_List.AddItem MyCell.Text
    Next
    ' ...
    
  3. Avoid using the name of a Form inside the Form code

    The current form is implied in the code. It is not necessary to use the form name to reference controls on the form. If you want to differeniate form controls, you can use the 'Me' keyword such as: Me.Hide

    Example of Bad Code

    ' ... BAD EXAMPLE: DO NOT USE ...
    ' This example is doubly bad because it uses the form name (ChartConfig)
    ' both explicitly and in a With statement
    If ChartConfig.Variable1.Text = ChartConfig.Variable2.Text Then
        MsgBox "Variable1 cannot be same as Variable2. Please choose another variable type.", vbOKCancel
    
        'Turn off the Change effect on Variable2.
        changeCasevar = False
        With ChartConfig
            .Variable2.Text = ""
            .Variable2Desc.Text = ""
            .Variable2Uom.Text = ""
        End With
        'Turn back on the Change effect on Variable2.
        changeCasevar = True
        Exit Sub
        '
    End If
    
    ' ... BAD EXAMPLE: DO NOT USE ...
    

    Example of Better Code (I still would have designed it differently)

    ' ...
    If Variable1.Text = Variable2.Text Then
        MsgBox "Variable1 cannot be same as Variable2. Please choose another variable type.", vbOKCancel
    
        'Turn off the Change effect on Variable2.
        changeCasevar = False
    
        Variable2.Text = ""
        Variable2Desc.Text = ""
        Variable2Uom.Text = ""
    
        'Turn back on the Change effect on Variable2.
        changeCasevar = True
        Exit Sub
    End If
    
    
  4. Use Error Handling and Resume to restore Application.ScreenUpdating

    Example of Bad Code

    ' ... BAD EXAMPLE: DO NOT USE ...
    ' There are quite a few global variables and a pass-by-reference
    ' boolean that should just be the function return value.
    Application.ScreenUpdating = False
    
    iOption = "Single"
    x = ChartPicker.Input_Val_Text
    Call CheckUserRangeInputsAndOutputs(iOption, OkToChart)
    iOption = ""
    
    If OkToChart = True Then
        With ChartConfig
            CreateChartingArrays
        End With
    End If
    
    Application.ScreenUpdating = True
    ' ... BAD EXAMPLE: DO NOT USE ...
      

    Example of Better Code (I still would have designed it differently)

    ' ...
      On Error GoTo ErrorHandler
    
      Application.ScreenUpdating = False
    
      If CheckUserRangeInputsAndOutputs("Single") Then
        ChartConfig.CreateChartingArrays
      End If
    
    Exit_Handler:
      Application.ScreenUpdating = True
      Exit Sub
    
    ErrorHandler:
      MsgBox "Error " & Err.Number & ": " & Err.Description
      Resume Exit_Handler
    
      
  5. Use If, Else If, and Else correctly.

    Example of Bad Code

    ' ... BAD EXAMPLE: DO NOT USE ...
    If TextBoxMinimum.Text = "" Then
      MsgBox "Must enter Minimum Value before proceeding", vbOK
      Result = False
      Exit Function
    End If
    
    If TextBoxMaximum.Text = "" Then
      MsgBox "Must enter Maximum Value before proceeding", vbOK
      Result = False
      Exit Function
    End If
    ' ... BAD EXAMPLE: DO NOT USE ...
    

    Example of Better Code

    ' ...
    ' For a explicit method, you can directly refer to the controls
    Result = False
    If Not IsNumeric(TextBoxMinimum.Text) Then
      MsgBox "Must enter Minimum Value before proceeding", vbOK
      TextBoxMinimum.SetFocus
    ElseIf Not IsNumeric(TextBoxMaximum.Text) Then
      MsgBox "Must enter Maximum Value before proceeding", vbOK
      TextBoxMaximum.SetFocus
    Else
      Result = True
    End If
    
    ' If you can use a generic method, you can iterate through all of the controls
    ' There are obviously better ways of doing this, especially with .NET
    Dim ValidateControl
    Dim ControlList = Array(TextBox1, TextBox2, TextBox3)
    For Each ValidateControl In ControlList
      If Input_Val_Text = Null Or Input_Val_Text = "" Then
        MsgBox "Must enter Value in " & ValidateControl.Name & " before proceeding", vbOK
        Result = False
        Exit Function
      End If
    Next
    
  6. Avoid using the ActiveSheet, Range, Cells, and other global variables.

    Rather than assuming a position, it is much better to set a variable to the desired Workbook, Worksheet, or Range.
    In particular, if there is any chance the spreadsheet might run under a browser, use Application.ThisWorkbook to ensure the browser evaluates the macro correctly.

    Example of Bad Code

    ' ... BAD EXAMPLE: DO NOT USE ...
    'NOTE: Arrays start with 1st position as "0", not "1"
    'Thus ColLtrs(0) is not used but placed there as a spacer for letter to column alignment:
    ColLtrs = Array("", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z")
    
    With ActiveSheet
      Range(ColLtrs(iOutCol) & iOutRow).Select
        
      If UnitSystem = "Metric" Then
        iRow = ActiveCell.Row
        For i = iRow To iLastRow - 1
          currCell = ColLtrs(ActiveCell.Column) & i + 1
    
          If Range(currCell).Value <> "" Then
            'capture the value from original calculatated data
            Range(currCell).Select
    
            strOrigValueCell = ColLtrs(iResultsCol) & ActiveCell.Row
            Result = ConvertUom(Range(strOrigValueCell))
          End If
        Next i
      End If
    End With
    ' ... BAD EXAMPLE: DO NOT USE ...
    

    Example of Better Code

    ' ...
    Dim mySheet As Worksheet
    Dim Value As Variant
    
    Set mySheet = ActiveSheet
    
    For i = iRow To iLastRow - 1
      Value = mySheet.Cells(i, iOutCol).Value
      If Value <> "" Then
        Result = ConvertUom(UnitSystem, Value)
      End If
    Next i
    
    ' To get a column letter/code, do not reinvent the wheel!
    ' Various functions from:
    ' http://www.dicks-blog.com/archives/2004/05/21/column-numbers-to-letters/
    
    Function ColLetter(ColNumber As Long) As String
        On Error Resume Next
        ColLetter = Application.Substitute(Application.ConvertFormula("R1C" & ColNumber, xlR1C1, xlA1, 4), "1", "")
    End Function
    
    Function ColumnLetter(ByVal c As Long) As String
      Dim p As Long
      While c
        p = 1 + (c - 1) Mod 26
        c = (c - p) \ 26
        ColumnLetter = Chr$(64 + p) & ColumnLetter
      Wend
    End Function
    
  7. Avoid using .Select and moving selection in macros unless absolutely.

    Do not change the current sheet or selection unless that is the intention of the macro. In general, don't mess with the user, flip sheets, or other heinous acts and try to leave things the way you found them.

    Example of Bad Code

    ' ... BAD EXAMPLE: DO NOT USE ...
    For i = 1 To 10
      Range("A" & i).Select
      ActiveCell.Value = "Row " & i
    Next i
    ' ... BAD EXAMPLE: DO NOT USE ...
    

    Example of Better Code

    Dim iColumn As Long
    Dim mySheet As Worksheet
    Dim myRange As Range
    
    iColumn = 1
    Set mySheet = ActiveSheet
    
    For iRow = 1 To 10
      Set rng = mySheet.Cells(iRow, iColumn)
      rng.Value = "Row " & iRow
    Next iRow
    

author: Ryan Van Slooten | posted @ Tuesday, May 27, 2008 6:28 PM | Feedback (0)

When aspnet_regsql.exe won't connect


I was building a quick test web site and I was using the aspnet_regsql tool to add membership to a SQLEXPRESS database. At first, I tried:

aspnet_regsql -A all -C "Data Source=.\SQLEXPRESS;Integrated Security=True;User Instance=True" -d "C:\code\Test\APP_DATA\aspnetdb.mdf"

For some reason, the SqlConnection insisted that it try to create the database and disregarded the full path to the database (note the path in the exception).

SQL Exception:
System.Data.SqlClient.SqlException: Directory lookup for the file "C:\Documents and Settings\user\Local Settings\Application Data\Microsoft\Microsoft SQL Server Data\SQLEXPRESS\C:\code\Asp.net\ServerControlTest\App_Data\Database.mdf" failed with the operating system error 123(The filename, directory name, or volume label syntax is incorrect.).
CREATE DATABASE failed. Some file names listed could not be created. Check related errors.
Creating the C:\code\Asp.net\ServerControlTest\App_Data\Database database...
   at System.Data.SqlClient.SqlConnection.OnError(SqlException exception, Boolean breakConnection)
   at System.Data.SqlClient.SqlInternalConnection.OnError(SqlException exception, Boolean breakConnection)
   at System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj)
   at System.Data.SqlClient.TdsParser.Run(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj)
   at System.Data.SqlClient.SqlCommand.RunExecuteNonQueryTds(String methodName, Boolean async)
   at System.Data.SqlClient.SqlCommand.InternalExecuteNonQuery(DbAsyncResult result, String methodName, Boolean sendToPipe)
   at System.Data.SqlClient.SqlCommand.ExecuteNonQuery()
   at System.Web.Management.SqlServices.ExecuteFile(String file, String server, String database, String dbFileName, SqlConnection connection, Boolean sessionState, Boolean isInstall, SessionStateType sessionStatetype)

Next I tried the simple commands (credit Scott Mitchell) that worked great:

sqlcmd -S localhost\SQLExpress -Q "EXEC sp_attach_db 'Foobar', N'pathToDBfile'"
aspnet_regsql.exe -S localhost\SQLExpress -d Foobar -E -A all

References

  1. Working with SQL Server 2005 Express Database
  2. Using ASPNET_RegSQL.exe with SQL Express databases in APP_DATA

author: Ryan Van Slooten | posted @ Thursday, April 24, 2008 3:50 PM | Feedback (0)

Unable to log into SQL Server after creating self-signed SSL certificate


In my SharePoint experiments with form-based authentication (FBA), I have been installing self-signed SSL certificates since I am developing in a virtual machine without a certificate authority. Last night, I shut down my virtual machine instead of the usual Suspend operation. This morning, I started the virtual machine but SharePoint wasn't working and said: Cannot connect to the configuration database.

I then tried to open SQL Server Management Console and tried to connect, only to receive the following message:

A connection was successfully established with the server, but then an error occurred during the pre-login handshake. (provider: Shared Memory Provider, error: 0 - No process is on the other end of the pipe.) (Microsoft SQL Server, Error: 233)

I didn't freak out, but I did check the SQL Server Service which was running, and then the SQL Server Log files which looked normal. There was one line which I didn't recognize:

The certificate was successfully loaded for encryption.

This is actually a normal line in the log file, but it was then that I realized that the self-signed certificates I created were interfering with the login process. I searched around for Internet resources, but finally found what I was looking for in the Certificate MMC snap-in configuration. Since I had created multiple SSL certificates, I deleted the unused certificates and checked the encryption and certificate settings in the SQL Server Configuration Manager. After a quick reboot, I was back in business.