Click here to Skip to main content
12,300,021 members (46,052 online)
Click here to Skip to main content

Stats

14.3K views
112 downloads
25 bookmarked
Posted

A Simple Solution to Some Problems with Asynchrony in Silverlight

, 9 Mar 2010 CPOL
A small, extensible suite of classes that elegantly solve some common problems involving asynchrony and event handling that tend to occur when Silverlight and WCF are used together.
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Shapes;

namespace TaskManagerDemo.Controls
{
    public partial class WaitSpinner : UserControl
    {
        public static readonly int DefaultSliceCount = 20;
        public static readonly double DefaultSliceRotationAngle = 360.0 / DefaultSliceCount;

        /// <summary>
        /// The number of slices or divisions
        /// </summary>
        private int _sliceCount;
        public int SliceCount
        {
            get
            {
                return _sliceCount;
            }
            set
            {
                _sliceCount = value;
                if (value <= 0)
                    SliceCenterAngle = 0.0;
                else
                    SliceCenterAngle = 360.0 / value;
            }
        }

        /// <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; }

        /// <summary>
        /// The horizontal radius (X) of the spinning cursor ellipse
        /// </summary>
        public double RadiusX { get; set; }

        /// <summary>
        /// The vertical radius (Y) of the spinning cursor ellipse
        /// </summary>
        public double RadiusY { get; set; }

        /// <summary>
        /// The center angle of the slice, determined by the number of slices: 360.0 / SliceCount.
        /// </summary>
        public double SliceCenterAngle { get; private set; }

        /// <summary>
        /// The rotation angle (curvature) of the slices.
        /// </summary>
        public double SliceRotationAngle { get; set; }

        public WaitSpinner()
        {
            InitializeComponent();
            this.Loaded += new RoutedEventHandler(Page_Loaded);
        }

        private void Page_Loaded(object sender, RoutedEventArgs e)
        {
            AlternateSlices = true;
            SliceCount = DefaultSliceCount;
            SliceRotationAngle = DefaultSliceRotationAngle;
            RadiusX = SpinningCanvas.Width / 2.0;
            RadiusY = SpinningCanvas.Height / 2.0;
            Origin = new Point(RadiusX, RadiusY);

            Update();
        }

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

        private void CreateBackground(Canvas cursorCanvas)
        {
            RadialGradientBrush brush = new RadialGradientBrush();
            GradientStop stop1 = new GradientStop();
            stop1.Color = Colors.White;
            stop1.Offset = 0.5;
            brush.GradientStops.Add(stop1);

            GradientStop stop2 = new GradientStop();
            stop2.Color = Color.FromArgb(255, 62, 102, 128);
            stop2.Offset = 1;
            brush.GradientStops.Add(stop2);

            brush.Center = new Point(.5, .5);
            brush.GradientOrigin = new Point(.5, .5);
            Ellipse ellipse = new Ellipse();
            ellipse.Height = cursorCanvas.Height;
            ellipse.Width = cursorCanvas.Width;
            ellipse.Fill = brush;
            cursorCanvas.Children.Add(ellipse);
        }

        /// <summary>
        /// Create an ellipse using rotated slices to build the ellipse
        /// </summary>
        private 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(255, 50, 98, 124));
                path.StrokeThickness = .5;
                path.Fill = new SolidColorBrush(Color.FromArgb(255, 196, 213, 222));
                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;
            }
        }

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

            // 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;

            // 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);

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

            return pathFigure;
        }

        /// <summary>
        /// Returns a point on the ellipse based on the rotationAngle, using
        /// a specified RadiusX/Y and origin.
        /// </summary>
        private Point CalculatePointOnEllipse(double rotationAngle, double radiusX, double radiusY, Point origin)
        {
            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 = origin.X - x;
            result.Y = origin.Y + y;
            return result;
        }

        /// <summary>
        /// Returns a point on the ellipse based on the rotationAngle,
        /// using the modular variables for location and size.
        /// </summary>
        private Point CalculatePointOnEllipse(double rotationAngle)
        {
            return CalculatePointOnEllipse(rotationAngle, RadiusX, RadiusY, Origin);
        }
    }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

George Henry 1954
Software Developer (Senior) Concur
United States United States
George Henry has worked as a software developer for more than 20 years. He is currently employed by Concur in Bellevue, Washington, USA.

You may also be interested in...

| Advertise | Privacy | Terms of Use | Mobile
Web02 | 2.8.160525.2 | Last Updated 9 Mar 2010
Article Copyright 2009 by George Henry 1954
Everything else Copyright © CodeProject, 1999-2016
Layout: fixed | fluid