Silverlight Chart and Google Financial Chart

下面是我自己使用业余时间实现的silverlight chart area , 并组合两个chart area实现了google fiancial chart . 下面是截图和代码。仅供参考和学习。

Helpers:

 public class AxisHelper
    { 
        public static void DrawAxis(ChartArea area, bool isXAxis)
        {
            GradeSetting gs = isXAxis ? area.XGradeSetting : area.YGradeSetting;
            var gls = area.Space.GetGradedLabels(gs, isXAxis);

            if (gls == null || gls.Count() == 0)
                return;

            PathFigure pf = isXAxis ? area.xpf : area.ypf;

            pf.StartPoint = new Point(area.Space.StartPoint.X, area.Space.StartPoint.Y);
            pf.Segments.Clear();
            //start
            LineSegment sls = new LineSegment();
            sls.Point = new Point(area.Space.StartPoint.X, area.Space.StartPoint.Y);
            pf.Segments.Add(sls);
            //End point
            var els = new LineSegment();
            SPoint sp = isXAxis ? area.Space.XEndPoint : area.Space.YEndPoint;
            els.Point = new Point(sp.X, sp.Y);
            pf.Segments.Add(els);

            foreach (var gl in gls)
            {
                Grid g = new Grid();
                if (isXAxis)
                {
                    g.RowDefinitions.Add(new RowDefinition { Height = new GridLength(0, GridUnitType.Auto) });
                    g.RowDefinitions.Add(new RowDefinition { Height = new GridLength(0, GridUnitType.Auto) });
                    g.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(0, GridUnitType.Auto) });
                }
                else
                {
                    g.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(0, GridUnitType.Auto) });
                    g.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(0, GridUnitType.Auto) });
                    g.RowDefinitions.Add(new RowDefinition { Height = new GridLength(0, GridUnitType.Auto) });
                }
                var cag = new ChartAreaGrid { Area = area, Margin = gl.Grade };
                g.Tag = cag;
                //grade
                Rectangle r = new Rectangle
                {
                    Width = gs.GradeWidth,
                    Height = gs.GradeHeigh,
                    Fill = new SolidColorBrush(Colors.Black),
                    HorizontalAlignment = HorizontalAlignment.Center
                };
                r.SetValue(isXAxis ? Grid.RowProperty : Grid.ColumnProperty, isXAxis ? 0 : 1);
                g.Children.Add(r);
                g.SizeChanged += new SizeChangedEventHandler(gy_SizeChanged);
                //label
                TextBlock txt = new TextBlock { Text = gl.Label, HorizontalAlignment = HorizontalAlignment.Center };
                txt.SetValue(isXAxis ? Grid.RowProperty : Grid.ColumnProperty, isXAxis ? 1 : 0);
                g.Children.Add(txt);
                area.canvas.Children.Add(g);
            }
        }

        static void gy_SizeChanged(object sender, SizeChangedEventArgs e)
        {
            Grid g = sender as Grid;
            ChartAreaGrid cag = g.Tag as ChartAreaGrid;

            if (g.ColumnDefinitions.Count == 2)
            {
                g.SetValue(Canvas.LeftProperty, cag.Area.Space.StartPoint.X - e.NewSize.Width + cag.Area.YGradeSetting.GradeWidth / 2);
                g.SetValue(Canvas.TopProperty, cag.Margin - e.NewSize.Height / 2);
            }
            else
            {
                g.SetValue(Canvas.LeftProperty, cag.Margin - cag.Area.XGradeSetting.GradeWidth / 2 - e.NewSize.Width / 2);
                g.SetValue(Canvas.TopProperty, cag.Area.Space.StartPoint.Y - cag.Area.YGradeSetting.GradeHeigh / 2);
            }
        }

        class ChartAreaGrid
        {
            public ChartArea Area { get; set; }
            public double Margin { get; set; }
        }


        public static void DrawTopLeftAxis(ChartArea area)
        {

            PathFigure pf = area.trpf;

            pf.StartPoint = new Point(area.Space.YEndPoint.X , area.Space.YEndPoint .Y );
            pf.Segments.Clear();
            //start
            LineSegment sls = new LineSegment();
            sls.Point = new Point(area.Space.XEndPoint.X , area.Space.YEndPoint.Y );
            pf.Segments.Add(sls);
            //End point
            var els = new LineSegment();
            els.Point = new Point(area.Space.XEndPoint.X ,area .Space .XEndPoint .Y );
            pf.Segments.Add(els);
        }

      

    }

using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Collections;
using SLPocs.Helpers;
using System.Linq;
using System.Collections.Generic;
using SLPocs.Entities;

namespace SLPocs.Controls.Charts
{
    public class SeriesHelper
    {

        public static void DrawLinealSries(ChartArea area)
        {
            foreach (var pth in area.Space.Pathes)
            {
                DrawLinealSeries(area, pth);
            }
        }

        static void DrawLinealSeries(ChartArea area, SPath pth)
        {
            Path p = new Path();
            area.canvas.Children.Add(p);
            p.Stroke = new SolidColorBrush(pth.Color);
            p.StrokeThickness = 2;
            PathGeometry pg = new PathGeometry();
            p.Data = pg;
            PathFigure fig = new PathFigure();
            pg.Figures.Add(fig);
            fig.StartPoint = new Point(pth[0].X, pth[0].Y);
            foreach (var pnt in pth)
            {
                var ls = new LineSegment();

                ls.Point = new Point(pnt.X, pnt.Y);
                fig.Segments.Add(ls);
            }
        }

    }
}

using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Collections.Generic;
using System.Linq;

namespace SLPocs.Controls.Charts
{
    public class UerActionHelper
    {
        public static void AddResponseElement(ChartArea area, TraceType traceType)
        {
            switch (traceType)
            {
                case  TraceType.CrossLine :
                    ShowCross(area);
                    break;
                case TraceType.EllipseTrace :
                    ShowEllipses(area);
                    break;
                case  TraceType.SelectRange :
                    ShowSelector(area);
                    break;
            }
        }

        public static void ShowCross(ChartArea area)
        {
            Line xline = new Line();
            xline.Tag = "xline";
            xline.X1 = area.Space.YEndPoint.X;
            xline.Y1 = area.Space.YEndPoint.Y;
            xline.X2 = area.Space.StartPoint.X;
            xline.Y2 = area.Space.StartPoint.Y;
            xline.Stroke = new SolidColorBrush(Colors.Green);
            xline.StrokeThickness = 2;
            area.canvas.Children.Add(xline);

            Line yline = new Line();
            yline.Tag = "yline";
            yline.X1 = area.Space.StartPoint.X;
            yline.Y1 = area.Space.StartPoint.Y;
            yline.X2 = area.Space.XEndPoint.X;
            yline.Y2 = area.Space.XEndPoint.Y; ;
            yline.Stroke = new SolidColorBrush(Colors.Green);
            yline.StrokeThickness = 2;
            area.canvas.Children.Add(yline);
        }

        private static void ShowEllipses(ChartArea area)
        {
            foreach (var sp in area.Space.Pathes)
            {
                Ellipse eps = new Ellipse();
                eps.Tag = sp.Key;
                eps.Height = 6;
                eps.Width = 6;
                eps.Fill = new SolidColorBrush(sp.Color );
                area.canvas.Children.Add(eps);
                eps.SetValue(Canvas.LeftProperty, 0.0);
                eps.SetValue(Canvas.TopProperty, 0.0);
                eps.Visibility = Visibility.Collapsed;
            }
        }

        private static void ShowSelector(ChartArea area)
        {
            throw new NotImplementedException();
        }

        public  static void MoveResponseElements(ChartArea area, MouseEventArgs e)
        {
            switch (area.TraceType)
            {
                case TraceType.CrossLine :
                    MoveCrossLine(area, e);
                    break;
                case TraceType.EllipseTrace :
                    MoveEllipses(area, e);
                    break;
            }
        }


        static void MoveEllipses(ChartArea area, MouseEventArgs e)
        {
            double x = e.GetPosition(area.canvas).X;
            var ys = area.Space.GetYPiexes(x);
            var ells = area.canvas.Children.Where(a => a is Ellipse).Select(a=>a as Ellipse ).ToList();
            foreach (var pth in area.Space.Pathes)
            {
                double? y = ys[pth.Key];
                var ell = ells.First(a => a.Tag == pth.Key);
                if (x < area.Space.StartPoint.X || x > area.Space.XEndPoint.X || !y.HasValue )
                    ell.Visibility = Visibility.Collapsed;
                else
                {
                    ell.Visibility = Visibility.Visible;
                    ell.SetValue(Canvas.TopProperty, y.Value-ell.Height/2);
                    ell.SetValue(Canvas.LeftProperty, x-ell.Width/2);
                }
                   
            }
           
        }

        static void MoveCrossLine(ChartArea area, MouseEventArgs e)
        {
            var lines = area .canvas.Children.Where(x => x is Line);
            foreach (var line in lines)
            {
                var l = line as Line;
                if (l.Tag == "xline")
                {
                    double x = e.GetPosition(area. canvas).X;
                    if (x > area.Space.XEndPoint.X)
                        x = area.Space.XEndPoint.X;
                    else if (x < area.Space.StartPoint.X)
                        x = area.Space.StartPoint.X;
                    l.X1 = x;
                    l.X2 = x;
                }
                else
                {
                    double y = e.GetPosition(area.canvas).Y;
                    if (y > area.Space.StartPoint.Y)
                        y = area.Space.StartPoint.Y;
                    else if (y < area.Space.YEndPoint.Y)
                        y = area.Space.YEndPoint.Y;

                    l.Y1 = y;
                    l.Y2 = y;
                }
            }
        }

    }
}

using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Windows.Data;

namespace SLPocs.Controls
{
        public class BindingHelper
        {
            public static void SetBindindFromSource(DependencyObject control, DependencyProperty property, object source, string path, BindingMode mode = BindingMode.OneWay, IValueConverter converter = null, UpdateSourceTrigger updateTrigger = UpdateSourceTrigger.Default)
            {
                Binding binding = new Binding();
                binding.Path = new PropertyPath(path);
                binding.Mode = mode;
                binding.Source = source;
                binding.Converter = converter;

                BindingOperations.SetBinding(control , property, binding);
            }
        }
 
}

using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Collections;
using System.Collections.Generic;

namespace SLPocs.Helpers
{
    public class DynamicHelper
    {
        public static Func<object, object> GetSelector(Type T, string property)
        {
            var o = System.Linq.Expressions.Expression.Variable(typeof(object));
            var block = System.Linq.Expressions.Expression.Block(
                System.Linq.Expressions.Expression.Convert(
                System.Linq.Expressions.Expression.Property(System.Linq.Expressions.Expression.Convert(o, T), property), typeof(object))
               );
            return System.Linq.Expressions.Expression.Lambda<Func<object, object>>(block, o).Compile();
        }

        public static object First(IEnumerable collection)
        {
            if (collection != null)
            {
                foreach (var o in collection)
                    return o;
            }

            return null;               
        }

        public static IEnumerable<T> Select<T>(IEnumerable collection, Func<object, object> selector)
        {
            foreach (var o in collection)
                yield return (T)selector(o);
        }

    }
}

using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using SLPocs.Entities;

namespace SLPocs.Helpers
{
    public class MathHelper
    {
        public static double GetLineValue(double x, double x0, double y0, double x1, double y1)
        {
            return y0 + (y1 - y0) * (x - x0) / (x1 - x0);
        }

        public static double ConvertDatetimeToDouble(DateTime dt, DateTime from, TimeUnit tu)
        {
            if (tu == TimeUnit.Year)
                return dt.Year - from.Year;
            if (tu == TimeUnit.Month)
                return 12 * (dt.Year - from.Year) + dt.Month - from.Month;
            if (tu == TimeUnit.Day)
                return (dt - from).TotalDays;
            if (tu == TimeUnit.Hour)
                return (dt - from).TotalHours;
            if (tu == TimeUnit.Min)
                return (dt - from).TotalMinutes;
            if (tu == TimeUnit.Second)
                return (dt - from).TotalSeconds;
            if (tu == TimeUnit.MinSecond)
                return (dt - from).TotalMilliseconds;
            return 0;
        }

        public static DateTime AddTime(double add, DateTime from, TimeUnit tu)
        {
            if (tu == TimeUnit.Year)
                return from.AddYears((int)add);
            if (tu == TimeUnit.Month)
                return from.AddMonths((int)add);
            if (tu == TimeUnit.Day)
                return from.AddDays((int)add);
            if (tu == TimeUnit.Hour)
                return from.AddHours(add);
            if (tu == TimeUnit.Min)
                return from.AddMinutes(add);
            if (tu == TimeUnit.Second)
                return from.AddSeconds(add);
            if (tu == TimeUnit.MinSecond)
                return from.AddMilliseconds(add);
            return from;
        }

        public static void Shrink(FrameworkElement element, double xrate, double yrate)
        {
            element.Width = element.Width * xrate;
            element.Height = element.Height * yrate;
            double value = (double)element.GetValue(Canvas.LeftProperty);
            element.SetValue(Canvas.LeftProperty, value * xrate);
            value = (double)element.GetValue(Canvas.TopProperty);
            element.SetValue(Canvas.TopProperty, value * yrate);
        }

    }
}

XAML

<UserControl xmlns:my="clr-namespace:SLPocs.Controls.Charts"  x:Class="SLPocs.Controls.Charts.ChartArea"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk"
    mc:Ignorable="d"
    d:DesignHeight="300" d:DesignWidth="400">

    <UserControl.Resources >
       
        <Grid x:Key="rangeSelector">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="Auto"></ColumnDefinition>
                <ColumnDefinition Width="*"></ColumnDefinition>
                <ColumnDefinition Width="Auto"></ColumnDefinition>
            </Grid.ColumnDefinitions>

            <sdk:GridSplitter x:Name="leftSplitter"
                              Grid.Column="1"
                              VerticalAlignment="Stretch"
                              Width="2"
                              HorizontalAlignment="Left"
                              Background="Gray"></sdk:GridSplitter>
            <sdk:GridSplitter x:Name="rightSplitter"
                              Grid.Column="1"
                              VerticalAlignment="Stretch"
                              Width="2"
                              HorizontalAlignment="Right"
                              Background="Gray"></sdk:GridSplitter>
            <Border x:Name="selector_Bdr"
                    Grid.Column="1"
                    BorderThickness="0,2,0,2"
                    BorderBrush="Gray"
                    Background="Gray"
                    Opacity="0.1"
                    Margin="2" AllowDrop="True"  ></Border>
        </Grid>
       
    </UserControl.Resources>

        <Grid x:Name="LayoutRoot" MouseMove="LayoutRoot_MouseMove" Background="White" ShowGridLines="True">
           
        <Canvas x:Name="canvas" Background="Transparent">
        </Canvas>

        <my:RangeSelector x:Name="rangeSelector" />
           
           
            <Path x:Name="XAxis"
              Stroke="Black"              
              StrokeThickness="2">
            <Path.Data>
                <PathGeometry>
                    <PathFigure x:Name="xpf">                       
                    </PathFigure>
                </PathGeometry>
            </Path.Data>
        </Path>

        <Path x:Name="YAxis"
              Stroke="Black"              
              StrokeThickness="2">
            <Path.Data>
                <PathGeometry>
                    <PathFigure x:Name="ypf">
                    </PathFigure>
                </PathGeometry>
            </Path.Data>
        </Path>

        <Path x:Name="TopRightAxis"
              Stroke="Black"              
              StrokeThickness="2">
            <Path.Data>
                <PathGeometry>
                    <PathFigure x:Name="trpf">
                    </PathFigure>
                </PathGeometry>
            </Path.Data>
        </Path>

    </Grid>
</UserControl>

<UserControl x:Class="SLPocs.Controls.Charts.RangeSelector"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk"
    mc:Ignorable="d"
    Background="Transparent"
    HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
    d:DesignHeight="300" d:DesignWidth="400">
   
    <Grid x:Name="LayoutRoot" Background="Transparent" >
        <Canvas x:Name="selectorCanvas" Background="Transparent" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
            <Border x:Name="leftSelectorGrid"  
                    BorderThickness="0"
                    Width="5"
                    Background="Gray"
                    MouseLeftButtonDown="leftSelectorGrid_MouseLeftButtonDown"
                    MouseLeftButtonUp="leftSelectorGrid_MouseLeftButtonUp"
                    MouseMove="leftSelectorGrid_MouseMove"></Border>
            <Border x:Name="selector"
                    BorderThickness="0,2,0,2"
                    Background="Gray"
                    Opacity="0.2"
                    MouseLeftButtonDown="selector_MouseLeftButtonDown"
                    MouseLeftButtonUp="selector_MouseLeftButtonUp"
                    MouseMove="selector_MouseMove"></Border>
            <Border x:Name="rightSelectorGrid" 
                    BorderThickness="0"
                    Width="5"
                    Background="Gray"  
                    MouseLeftButtonDown="rightSelectorGrid_MouseLeftButtonDown"
                    MouseLeftButtonUp="rightSelectorGrid_MouseLeftButtonUp"
                    MouseMove="rightSelectorGrid_MouseMove"></Border>
        </Canvas>
    </Grid>
</UserControl>

Behind code

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Collections;
using SLPocs.Entities;
using System.Windows.Data;

namespace SLPocs.Controls.Charts
{
    public partial class ChartArea : UserControl
    {
        public ChartArea()
        {
            InitializeComponent();
            this.SizeChanged += new SizeChangedEventHandler(ChartArea_SizeChanged);
            Space = new LinealSpace();
            Space.SetStartPoint(MarginX, MarginY+100);
            Space.SetLength(100, 100);
            rangeSelector.StartPercValue = 0.7;
            rangeSelector.EndPercValue = 0.9;

            rangeSelector.UpdateRangeAction = this.HandelSelectedItemsChanged;
        }


        public Size Size;
        protected override Size ArrangeOverride(Size finalSize)
        {
            Size = base.ArrangeOverride(finalSize);
            return Size;
        }

        bool initialized = false;
        void ChartArea_SizeChanged(object sender, SizeChangedEventArgs e)
        {
            UpdateUI();
            initialized = true;
        }

        public void UpdateUI()
        {
            Space.SetLength(this.Size.Width - 2 * MarginX, this.Size.Height - 2 * MarginY);
            Space.SetStartPoint(MarginX, this.Size.Height - MarginY);
            canvas.Children.Clear();

            SeriesHelper.DrawLinealSries(this);
            AxisHelper.DrawAxis(this, false);
            AxisHelper.DrawAxis(this, true);

            if (isRangeSelector)
            {
                InitializeRangeSelectorSpace();
                AxisHelper.DrawTopLeftAxis(this);
            }
            else
                rangeSelector.Visibility = System.Windows.Visibility.Collapsed;
              
            if(isTrace )
                UerActionHelper.AddResponseElement(this, this.TraceType); ;
        }

        public LinealSpace Space = null;
        IEnumerable dataSource;
        public IEnumerable DataSource
        {
            get { return dataSource ; }
            set
            {
               dataSource = value;
               Space.FillData(dataSource, XPath, XGradeSetting .TimeUnit,IsRangeSeletor , YPaths);
               if (initialized)
                   UpdateUI();
            }
        }

        public IEnumerable selectedItems;
        public IEnumerable SelectedItems { get { return selectedItems; } }
        public Action<IEnumerable> UpdateSelectedItems { get; set; }
        public void HandelSelectedItemsChanged(double start, double end)
        {
            if (isRangeSelector)
            {
                double xlen= Space.XEndPoint .X -Space.StartPoint .X ;
                double _start = xlen * start + Space.StartPoint.X, _end = xlen * end + Space.StartPoint.X;

                selectedItems = Space.Pathes[0].Where(x => x.X >= _start && x.X < _end).Select(x => x.Item);
                if (UpdateSelectedItems != null)
                    UpdateSelectedItems(selectedItems);
            }
        }

        public TraceType TraceType = TraceType.CrossLine;

        public string Caption { get; set; }

      


        #region XAxis Settings

        public double MarginX = 100;
        public string XPath = "X";

        public GradeSetting XGradeSetting = new GradeSetting {
            FixedPoints = 10,
            Format = "yyyy/MM/dd",
            GradeHeigh = 4,
            GradeWidth = 2,
            Internl = 100,
            GradeType = GradeType.FixedInternal,
            TimeUnit = TimeUnit.Day
        };

        #endregion

        #region YAxis Settings

        public string[] YPaths = new string[]{"Y1","Y2","Y3"};
        public double MarginY = 100;

        public GradeSetting YGradeSetting = new GradeSetting
        {
            FixedPoints = 10,
            Format = "0.00",
            GradeHeigh = 2,
            GradeWidth = 4,
            Internl = 10,
            GradeType = GradeType.FixedInternal,
            TimeUnit = TimeUnit.Day
        };   

        #endregion

        #region Selector

        bool isRangeSelector = true;
        public bool IsRangeSeletor { get { return isRangeSelector; } set { isRangeSelector = value; } }  
        void InitializeRangeSelectorSpace()
        {
            rangeSelector.Width = Space.XEndPoint.X - Space.StartPoint.X;
            rangeSelector.Height = Space.StartPoint.Y - Space.YEndPoint.Y;
            rangeSelector.Margin = new Thickness { Bottom = MarginX, Top = MarginX, Left = MarginY, Right = MarginY };
        }

        #endregion

        #region User Response

        bool isTrace = true;
        public bool IsTrace { get { return isTrace; } set { isTrace = value; } }

        private void LayoutRoot_MouseMove(object sender, MouseEventArgs e)
        {
            if (isTrace)
                UerActionHelper.MoveResponseElements(this, e);          
              
        }

        #endregion

    }

    public enum TraceType
    {
        CrossLine,
        EllipseTrace,
        SelectRange,
    }
}

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using SLPocs.Helpers;

namespace SLPocs.Controls.Charts
{
    public partial class RangeSelector : UserControl
    {
        public RangeSelector()
        {
            InitializeComponent();
            this.SizeChanged += new SizeChangedEventHandler(RangeSelector_SizeChanged);
            leftSelectorGrid.Cursor = Cursors.SizeWE;
            selector.Cursor = Cursors.Hand;
            rightSelectorGrid.Cursor = Cursors.SizeWE;
        }

        public Action<double, double> UpdateRangeAction { get; set; }

        public double StartPercValue { get; set; }
        public double EndPercValue { get; set; }
        Size size;
        void RangeSelector_SizeChanged(object sender, SizeChangedEventArgs e)
        {
            size = e.NewSize;
            UpdateUI();
        }

        private void UpdateUI()
        {
            leftSelectorGrid.Height = size.Height;
            leftSelectorGrid.SetValue(Canvas.LeftProperty, size.Width * StartPercValue-leftSelectorGrid.Width);          
            selector.Height = size.Height;
            selector.Width = size.Width * (EndPercValue - StartPercValue);
            selector.SetValue(Canvas.LeftProperty, size.Width * StartPercValue);
            rightSelectorGrid.Height = size.Height;
            rightSelectorGrid.SetValue(Canvas.LeftProperty, size.Width * StartPercValue + selector.Width);
        } 

        double preLX;
        bool isLMouseCaptured;
        private void leftSelectorGrid_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            SetPreX(sender, e, ref preLX,ref isLMouseCaptured);
        }

        private void leftSelectorGrid_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
             HandleRelease(sender, e, ref preLX, ref isLMouseCaptured);
        }

        private void leftSelectorGrid_MouseMove(object sender, MouseEventArgs e)
        {
            HandleMove(sender, e, ref preLX, isLMouseCaptured,true );
           
        }

        double preSX;
        bool isSMouseCaptured;
        private void selector_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            SetPreX(sender ,e ,ref preSX ,ref isSMouseCaptured );
        }

        private void selector_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
             HandleRelease(sender, e, ref preSX, ref isSMouseCaptured);
        }

        private void selector_MouseMove(object sender, MouseEventArgs e)
        {
            HandleMove(sender, e, ref preSX, isSMouseCaptured,null );           
        }


        double preRX;
        bool isRMouseCaptured;
        private void rightSelectorGrid_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            SetPreX(sender, e, ref preRX, ref isRMouseCaptured);
        }

        private void rightSelectorGrid_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            HandleRelease(sender, e, ref preRX, ref isRMouseCaptured);
        }

        private void rightSelectorGrid_MouseMove(object sender, MouseEventArgs e)
        {
            HandleMove(sender, e, ref preRX, isRMouseCaptured,false);
        }
        void SetPreX(object sender, MouseEventArgs e, ref double px,ref bool isCaptured)
        {
            Border item = sender as Border;
            px = e.GetPosition(selectorCanvas).X;
            if (px < 0)
                px = 0;
            else if (px > size.Width)
                px = size.Width;
            item.CaptureMouse();
            isCaptured = true;
        }

        void HandleMove(object sender, MouseEventArgs e, ref double px, bool isCaptured,bool? isLeft)
        {
            Border item = sender as Border;
            if (isCaptured)
            {
                double itemX = (double)item.GetValue(Canvas.LeftProperty);
                double ex = e.GetPosition(selectorCanvas).X;
                if (ex < 0)
                    ex = 0;
                else if (ex > size.Width)
                    ex = size.Width;
              
                double deltaX =ex- px;
                if (deltaX < 0 && deltaX + itemX < 0)
                    StartPercValue = 0;
                else if (deltaX > 0 && itemX + item.Width + deltaX > size.Width)
                    EndPercValue = 1;
                else if (isLeft.HasValue && isLeft.Value)
                    StartPercValue += deltaX / size.Width;
                else if (isLeft.HasValue && !isLeft.Value)
                    EndPercValue += deltaX / size.Width;
                else
                {
                    StartPercValue += deltaX / size.Width;
                    EndPercValue += deltaX / size.Width;
                }

                UpdateUI();
                // Update position global variables.
                px = ex;
            }
        }

        void HandleRelease(object sender, MouseEventArgs e, ref double px, ref bool isCaptured)
        {
            Border item = sender as Border;
            isCaptured = false;
            item.ReleaseMouseCapture();
            if (UpdateRangeAction != null)
                UpdateRangeAction(StartPercValue, EndPercValue);
        }

    }

    public struct RectangleArea
    {
        public double X1 { get; set; }
        public double Y1 { get; set; }
        public double X2 { get; set; }
        public double Y2 { get; set; }
        public bool IsRelative { get; set; }

        public void Shrink(double xrate, double yrate)
        {
            X1 *= xrate;
            X2 *= xrate;
            Y1 *= yrate;
            Y2 *= yrate;
        }
    }
}

How to use it

<UserControl xmlns:my1="clr-namespace:SLPocs.Controls.Charts"  xmlns:syncfusion="clr-namespace:Syncfusion.Windows.Chart;assembly=Syncfusion.Chart.Silverlight"  xmlns:my="clr-namespace:SLPocs.Controls.Grids"  x:Class="SLPocs.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    d:DesignHeight="300" d:DesignWidth="400">

    <Grid x:Name="LayoutRoot" Background="White">
        <Grid.RowDefinitions >
            <RowDefinition Height="*"></RowDefinition>
            <RowDefinition Height="400"></RowDefinition>
        </Grid.RowDefinitions>
        <my1:ChartArea x:Name="detail"/>
        <my1:ChartArea x:Name="summary" Grid.Row="1" />
    </Grid>
</UserControl>

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using SLPocs.Controls.Texts;
using SLPocs.Controls.Arrows;
using SLPocs.Controls.Charts;
using SLPocs.Controls;
using System.Collections.ObjectModel;
using SLPocs.Entities;
using System.Windows.Markup;
using System.Collections;

namespace SLPocs
{
    public partial class MainPage : UserControl
    {
        public MainPage()
        {
            InitializeComponent();

            summary.DataSource = TestChartData.GetTestData();
            summary.IsTrace = false;
            summary.TraceType = TraceType.EllipseTrace;
            summary.XGradeSetting.GradeType = GradeType.FixedInternal;
            summary.UpdateSelectedItems = this.UpdateSelectedItems;

            detail.IsTrace = true;
            detail.IsRangeSeletor = false;
            detail.TraceType = TraceType.EllipseTrace;
            detail.XGradeSetting.GradeType = GradeType.FixedPoints ;

                     
        }

        void UpdateSelectedItems(IEnumerable data)
        {
            detail.DataSource = data;
        }
    }

    public class TestChartData
    {
        public double Y1 { get; set; }
        public double Y2 { get; set; }
        public double Y3 { get; set; }

        public DateTime X { get; set; }

        public static IEnumerable<TestChartData> GetTestData()
        {
            for (int i = 0; i < 2000; i++)
                yield return new TestChartData
                {
                    Y1 =100+ 50*Math.Sin(Math.PI *i/60),
                    Y2 = 100 + 50 * Math.Sin(Math.PI * i / 60+Math.PI /4),
                    Y3 = 100 + 50 * Math.Sin(Math.PI * i / 60+Math.PI/2),
                    X = DateTime.Now .AddDays (i ),
                };
        }
    }
}

原文地址:https://www.cnblogs.com/mjgb/p/2828505.html