Click here to Skip to main content
15,896,118 members
Articles / Desktop Programming / WPF

How to create stock charts using the Silverlight Toolkit

Rate me:
Please Sign up or sign in to vote.
4.70/5 (15 votes)
16 Feb 2009CPOL2 min read 142.3K   2.7K   65  
An article on how to create a Candlestick stock chart using the Silverlight Toolkit.
// (c) Copyright Microsoft Corporation.
// This source is subject to the Microsoft Public License (Ms-PL).
// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
// All other rights reserved.

using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Threading;
using Microsoft.Windows.Controls.DataVisualization.Charting;

namespace Microsoft.Windows.Controls.Samples
{
    /// <summary>
    /// Contains methods that generate sample Charts for any Series type.
    /// </summary>
    public static class SampleGenerators
    {
        /// <summary>
        /// Stores a shared ObservableCollection for use by the dynamic collection scenario.
        /// </summary>
        private static ObservableCollection<int> _dynamicCollectionItemsSource = new ObservableCollection<int>();

        /// <summary>
        /// Stores a shared List for use by the dynamic data items scenario.
        /// </summary>
        private static List<Pet> _dynamicDataItemsSource = new List<Pet>
        {
            new Pet { Species = "Dogs" },
            new Pet { Species = "Cats" },
            new Pet { Species = "Birds" },
            new Pet { Species = "Fish" },
        };

        /// <summary>
        /// Stores a shared List for use by the dynamic date items scenario.
        /// </summary>
        private static List<Pair> _dynamicDateItemsSource = new List<Pair>
        {
            new Pair { First = new DateTime(2008, 10, 11), Second = 0.0 },
            new Pair { First = new DateTime(2008, 10, 12), Second = 0.0 },
            new Pair { First = new DateTime(2008, 10, 13), Second = 0.0 },
            new Pair { First = new DateTime(2008, 10, 14), Second = 0.0 },
            new Pair { First = new DateTime(2008, 10, 15), Second = 0.0 },
            new Pair { First = new DateTime(2008, 10, 16), Second = 0.0 },
        };

        /// <summary>
        /// Stores a shared random number generator.
        /// </summary>
        private static Random _random = new Random();

        /// <summary>
        /// Initializes static members of the SampleGenerators class.
        /// </summary>
        [SuppressMessage("Microsoft.Performance", "CA1810:InitializeReferenceTypeStaticFieldsInline", Justification = "Need to do additional initialization.")]
        static SampleGenerators()
        {
            // Create a timer to update the dynamic data regularly
            DispatcherTimer dispatcherTimer = new DispatcherTimer();
            dispatcherTimer.Interval = TimeSpan.FromSeconds(2);
            dispatcherTimer.Tick += delegate
            {
                // Update _dynamicCollectionItemsSource
                _dynamicCollectionItemsSource.Add(_random.Next(1, 11));
                if (10 < _dynamicCollectionItemsSource.Count)
                {
                    _dynamicCollectionItemsSource.RemoveAt(0);
                }

                // Update _dynamicDataItemsSource
                foreach (Pet pet in _dynamicDataItemsSource)
                {
                    pet.Count = _random.Next(1, 20);
                }

                // Update _dynamicDateItemsSource
                foreach (Pair pair in _dynamicDateItemsSource)
                {
                    pair.Second = _random.NextDouble() * 20;
                }
            };
            dispatcherTimer.Start();
        }

        /// <summary>
        /// Generates numeric Series samples.
        /// </summary>
        /// <param name="panel">Panel to add the generated Charts to.</param>
        /// <param name="seriesConstructor">Function that returns a Series instance for each sample.</param>
        /// <param name="includeIndependentValueBinding">True if an IndependentValueBinding should be created.</param>
        public static void GenerateNumericSeriesSamples(Panel panel, Func<Series> seriesConstructor, bool includeIndependentValueBinding)
        {
            Binding independentValueBinding = includeIndependentValueBinding ? new Binding() : null;
            Scenario[] scenarios = new Scenario[]
            {
                new Scenario { Title = "One Int", ItemsSource = new int[] { 3 }, IndependentValueBinding = independentValueBinding },
                new Scenario { Title = "Three Ints", ItemsSource = new int[] { 4, 2, 7 }, IndependentValueBinding = independentValueBinding },
                new Scenario { Title = "Seven Doubles", ItemsSource = new double[] { 4.4, 6.2, 9.7, 7.0, 2.1, 8.3, 5.5 }, IndependentValueBinding = independentValueBinding },
                new Scenario { Title = "No Points", ItemsSource = new int[0], IndependentValueBinding = independentValueBinding },
                new Scenario { Title = "Some Points with Value 0", ItemsSource = new int[] { 0, 1, 0, 2, 0 }, IndependentValueBinding = independentValueBinding },
                new Scenario { Title = "Some Negative-Value Points", ItemsSource = new double[] { 2.1, 3.7, -2.5, -4.6, 1.0 }, IndependentValueBinding = independentValueBinding },
                new Scenario { Title = "High-Value Points", ItemsSource = new int[] { 2000000000, 1200000000, 2100000000, 1000000000, 2000000000 }, IndependentValueBinding = independentValueBinding },
                // new Scenario { Title = "100 Points", ItemsSource = Enumerable.Range(1, 100), IndependentValueBinding = independentValueBinding },
                new Scenario { Title = "Dynamic Collection", ItemsSource = _dynamicCollectionItemsSource, IndependentValueBinding = independentValueBinding },
            };
            GenerateSeriesSamples(panel, seriesConstructor, scenarios, null);
        }

        /// <summary>
        /// Generates value Series samples.
        /// </summary>
        /// <param name="panel">Panel to add the generated Charts to.</param>
        /// <param name="seriesConstructor">Function that returns a Series instance for each sample.</param>
        public static void GenerateValueSeriesSamples(Panel panel, Func<Series> seriesConstructor)
        {
            Scenario[] scenarios = new Scenario[]
            {
                new Scenario { Title = "Pet Counts (No Names)", ItemsSource = pets, DependentValueBinding = new Binding("Count") },
                new Scenario { Title = "Dynamic Data Items (No Names)", ItemsSource = _dynamicDataItemsSource, DependentValueBinding = new Binding("Count") },
            };
            GenerateSeriesSamples(panel, seriesConstructor, scenarios, null);
        }

        /// <summary>
        /// Generates category/value Series samples.
        /// </summary>
        /// <param name="panel">Panel to add the generated Charts to.</param>
        /// <param name="seriesConstructor">Function that returns a Series instance for each sample.</param>
        public static void GenerateCategoryValueSeriesSamples(Panel panel, Func<Series> seriesConstructor)
        {
            Scenario[] scenarios = new Scenario[]
            {
                new Scenario { Title = "Pet Counts (Names)", ItemsSource = pets, DependentValueBinding = new Binding("Count"), IndependentValueBinding = new Binding("Species") },
                new Scenario { Title = "Dynamic Data Items (Names)", ItemsSource = _dynamicDataItemsSource, DependentValueBinding = new Binding("Count"), IndependentValueBinding = new Binding("Species") },
            };
            GenerateSeriesSamples(panel, seriesConstructor, scenarios, null);
        }

        /// <summary>
        /// Generates value/value Series samples.
        /// </summary>
        /// <param name="panel">Panel to add the generated Charts to.</param>
        /// <param name="seriesConstructor">Function that returns a Series instance for each sample.</param>
        public static void GenerateValueValueSeriesSamples(Panel panel, Func<Series> seriesConstructor)
        {
            List<Point> circle = new List<Point>();
            for (double i = 0; i < 2 * Math.PI; i += 0.1)
            {
                circle.Add(new Point(Math.Sin(i), Math.Cos(i)));
            }
            Scenario[] scenarios = new Scenario[]
            {
                new Scenario { Title = "Circle", ItemsSource = circle, DependentValueBinding = new Binding("X"), IndependentValueBinding = new Binding("Y") },
            };
            GenerateSeriesSamples(panel, seriesConstructor, scenarios, null);
        }

        /// <summary>
        /// Generates value/value Series samples.
        /// </summary>
        /// <param name="panel">Panel to add the generated Charts to.</param>
        /// <param name="seriesConstructor">Function that returns a Series instance for each sample.</param>
        public static void GenerateDateTimeValueSeriesSamples(Panel panel, Func<Series> seriesConstructor)
        {
            Scenario[] scenarios = new Scenario[]
            {
                new Scenario { Title = "Value by Date", ItemsSource = _dynamicDateItemsSource, DependentValueBinding = new Binding("Second"), IndependentValueBinding = new Binding("First") },
            };
            Action<Chart> chartModifier = (chart) =>
            {
                IAxis dateAxis = new DateTimeAxis { Orientation = AxisOrientation.Horizontal };
                chart.Axes.Add(dateAxis);
                IAxis valueAxis = new LinearAxis { Orientation = AxisOrientation.Vertical, Minimum = 0, Maximum = 20, ShowGridLines = true };
                chart.Axes.Add(valueAxis);
            };
            GenerateSeriesSamples(panel, seriesConstructor, scenarios, chartModifier);
        }

        /// <summary>
        /// Generates multiple value Series samples.
        /// </summary>
        /// <param name="panel">Panel to add the generated Charts to.</param>
        /// <param name="seriesConstructor">Function that returns a Series instance for each sample.</param>
        /// <param name="includeIndependentValueBinding">True if an IndependentValueBinding should be created.</param>
        public static void GenerateMultipleValueSeriesSamples(Panel panel, Func<Series> seriesConstructor, bool includeIndependentValueBinding)
        {
            Binding independentValueBinding = includeIndependentValueBinding ? new Binding() : null;
            //double[] items = new double[] { 3.1, 1.6, 4.9, 0.8, 2.2 };
            //List<IEnumerable> itemsRepeated = new List<IEnumerable>();
            //for (int i = 0; i < 30; i++)
            //{
            //    itemsRepeated.Add(items);
            //}
            Scenario[] scenarios = new Scenario[]
            {
                new Scenario { Title = "Three Series", ItemsSources = new IEnumerable[] { new int[] { 1, 2, 3 }, new int[] { 4, 5, 6 }, new int[] { 7, 8, 9 } }, IndependentValueBinding = independentValueBinding },
                // new Scenario { Title = "Thirty Series", ItemsSources = itemsRepeated, IndependentValueBinding = independentValueBinding },
            };
            GenerateSeriesSamples(panel, seriesConstructor, scenarios, null);
        }

        /// <summary>
        /// Generates various Series samples.
        /// </summary>
        /// <param name="panel">Panel to add the generated Charts to.</param>
        /// <param name="seriesConstructor">Function that returns a Series instance for each sample.</param>
        /// <param name="scenarios">Collection of scenarios to generate.</param>
        /// <param name="chartModifier">Function that applies any necessary modifications to the Chart.</param>
        private static void GenerateSeriesSamples(Panel panel, Func<Series> seriesConstructor, IEnumerable<Scenario> scenarios, Action<Chart> chartModifier)
        {
            Style wrapperStyle = Application.Current.Resources["WrapperStyle"] as Style;

            // For each scenario...
            foreach (Scenario scenario in scenarios)
            {
                // Create the sample Chart
                Chart chart = new Chart { Title = scenario.Title, MaxWidth = 500, MaxHeight = 270 };
                foreach (IEnumerable itemsSource in scenario.ItemsSources)
                {
                    DataPointSeries series = seriesConstructor() as DataPointSeries;
                    series.ItemsSource = itemsSource;
                    series.DependentValueBinding = scenario.DependentValueBinding;
                    series.IndependentValueBinding = scenario.IndependentValueBinding;
                    chart.Series.Add(series);
                }
                if (null != chartModifier)
                {
                    chartModifier(chart);
                }

                // Wrap the Chart in a suitably formatted Grid
                Grid grid = new Grid { Style = wrapperStyle };
                grid.Children.Add(chart);
                panel.Children.Add(grid);
            }
        }

        /// <summary>
        /// Collection of Pet objects for use by Chart samples.
        /// </summary>
        private static Pet[] pets = new Pet[]
        {
            new Pet { Species = "Dogs", Count = 5 },
            new Pet { Species = "Cats", Count = 4 },
            new Pet { Species = "Birds", Count = 6 },
            new Pet { Species = "Fish", Count = 9 }
        };

        /// <summary>
        /// Class representing an automatically generated Chart sample.
        /// </summary>
        private class Scenario
        {
            /// <summary>
            /// Gets or sets the title of the scenario.
            /// </summary>
            public string Title { get; set; }

            /// <summary>
            /// Sets the ItemsSource for the Chart's Series.
            /// </summary>
            public IEnumerable ItemsSource
            {
                set
                {
                    ItemsSources = new IEnumerable[] { value };
                }
            }

            /// <summary>
            /// Gets or sets the ItemsSources for the Chart's Series.
            /// </summary>
            public IEnumerable<IEnumerable> ItemsSources { get; set; }

            /// <summary>
            /// Gets or sets the DependentValueBinding for the Chart's Series.
            /// </summary>
            public Binding DependentValueBinding { get; set; }

            /// <summary>
            /// Gets or sets the IndependentValueBinding for the Chart's Series.
            /// </summary>
            public Binding IndependentValueBinding { get; set; }
        }
    }
}

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)


Written By
South Africa South Africa
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions