UI:
1.Display current indexes
2.Ellipses of current selected node (Select node by move mouse)
3.Move 3 to show details of selected days
4. Backend Code
/// <summary> /// Interaction logic for CitiFinancialChart.xaml /// </summary> public partial class FinancialChart : UserControl { public FinancialChart() { InitializeComponent(); } Action<object> displayItem; Dictionary<string,Ellipse> ellipses=null ; Dictionary<string, ChartSeries> serieses = null; Dictionary<string, TextBlock> titles = null; public void SetChartData(IEnumerable data, Dictionary<string, string> seriesNamePathPairs, string xAxisPath, string yAxisHeader, Action<object> _displayItem) { displayItem = _displayItem; canvas .Children .Clear(); ellipses=new Dictionary<string,Ellipse>(); titles = new Dictionary<string, TextBlock>(); serieses = new Dictionary<string, ChartSeries>(); area.Series.Clear(); stck.Children.Clear(); foreach(var kvp in seriesNamePathPairs) { Ellipse ellipse=new Ellipse(); ellipse .Width =5; ellipse .Height =5; ellipse.Visibility = System.Windows.Visibility.Collapsed ; ellipses.Add(kvp.Key ,ellipse); canvas.Children.Add(ellipse); TextBlock txt = new TextBlock(); stck.Children.Add(txt); titles.Add(kvp.Key, txt); ChartSeries series = new ChartSeries(); series.ShowToolTip = false; series.BindingPathX = xAxisPath; series.BindingPathsY = new string[]{ kvp.Value}; series.Type = ChartTypes.Line; Binding binding = new Binding(); binding.ElementName = "timelineControl"; binding.Path = new PropertyPath("SelectedData", null); binding.Mode = BindingMode.TwoWay; binding.UpdateSourceTrigger = UpdateSourceTrigger.Explicit; series.SetBinding(ChartSeries.DataSourceProperty, binding); area.Series.Add(series); serieses.Add(kvp.Key, series); } this.yAxisHeader.Text = yAxisHeader; timelineControl.HoldUpdate = true; timelineControl.PrimaryAxis.DateTimeInterval = new TimeSpan(268, 0, 0, 0); timelineControl.DataSource = data ; timelineControl.BindingPathX = xAxisPath; timelineControl.BindingPathsY = seriesNamePathPairs.Values .ToArray(); timelineControl.HoldUpdate = false; timelineControl.EndInit(); DataContext = data; area.MouseMove += new System.Windows.Input.MouseEventHandler(area_MouseMove); area.MouseEnter += new System.Windows.Input.MouseEventHandler(area_MouseEnter); area.MouseLeave += new System.Windows.Input.MouseEventHandler(area_MouseLeave); } void area_MouseLeave(object sender, System.Windows.Input.MouseEventArgs e) { foreach (var el in ellipses) el.Value.Visibility = System.Windows.Visibility.Collapsed; } void area_MouseEnter(object sender, System.Windows.Input.MouseEventArgs e) { foreach (var el in ellipses) el.Value.Visibility = System.Windows.Visibility.Visible ; } void area_MouseMove(object sender, System.Windows.Input.MouseEventArgs e) { ChartArea area = sender as ChartArea; IChartDataPoint currentChartPoint=null ; foreach (var el in ellipses) { el.Value.Fill = serieses[el.Key].Interior; MoveEllipse(area, canvas , serieses[el.Key].Data , e, el.Value , out currentChartPoint); if (currentChartPoint != null) { titles[el.Key].Text = " " + el.Key + ":" + currentChartPoint.Y; titles[el.Key].Foreground = el.Value.Fill; } } if (displayItem != null && currentChartPoint != null) { displayItem(currentChartPoint.Item); } } void MoveEllipse(ChartArea area, Canvas canvas, IChartData chartData, System.Windows.Input.MouseEventArgs e, Ellipse ellipse, out IChartDataPoint currentChartPoint) { //Get mouse position related with chart area Point mousePointOnChartArea = e.GetPosition(area); //Get xAxis Value and Round value double xValue = area.PointToValue(area.PrimaryAxis, mousePointOnChartArea), yValue; double xRoundValue = Math.Round(xValue); //Wrapper ChartData to use Linq EnumerableChartData data = new EnumerableChartData(chartData); //Get Current chart point currentChartPoint = data.FirstOrDefault(x => x.X == xRoundValue); if (currentChartPoint == null) return; // Get next Chart point statisfied xValue betweens nextChartPoint.X and currentChartPoint.X IChartDataPoint nextChartPoint; if (xValue > xRoundValue) nextChartPoint = data.OrderBy(x => x.X).FirstOrDefault(x => x.X > xValue); else nextChartPoint = data.OrderBy(x => x.X).LastOrDefault(x => x.X < xValue); if (nextChartPoint == null) nextChartPoint = currentChartPoint; // Get Value of Y axis yValue = GetValueFromLinearFunction(currentChartPoint.X, currentChartPoint.Y, nextChartPoint.X, nextChartPoint.Y, xValue); // convert yValue to Point double yPoint = area.ValueToPoint(area.SecondaryAxis, yValue); var epc = e.GetPosition(canvas); ellipse.SetValue(Canvas.LeftProperty, epc.X - ellipse.Width / 2); ellipse.SetValue(Canvas.TopProperty, epc.Y + (yPoint - mousePointOnChartArea.Y) - ellipse.Height / 2); //ellipse .Fill = } double GetValueFromLinearFunction(double x1, double y1, double x2, double y2, double x) { if (x1 == x2) return y1; return y1 + ((y2 - y1) / (x2 - x1)) * (x - x1); } }
5.XAML
<UserControl x:Class="TimeLineControlSample.UserControls.FinancialChart" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:sync="http://schemas.syncfusion.com/wpf" xmlns:local="clr-namespace:TimeLineControlSample" mc:Ignorable="d" d:DesignHeight="300" d:DesignWidth="300"> <UserControl.Resources> <local:LabelConverter x:Key="LabelConverterKey" /> </UserControl.Resources> <Grid> <Grid.RowDefinitions> <RowDefinition Height="40"></RowDefinition> <RowDefinition Height="0.75*" /> <RowDefinition Height="0.25*" /> </Grid.RowDefinitions> <StackPanel Name="stck" Orientation="Horizontal" HorizontalAlignment="Right" ></StackPanel> <Canvas Grid.Row="1" Name="canvas" Background="Transparent"> </Canvas> <sync:Chart Grid.Row="1" Name="chart" > <sync:ChartArea Name="area"> <sync:ChartArea.PrimaryAxis> <sync:ChartAxis ValueType="DateTime" LabelDateTimeFormat="MMM/dd/yyy"> <sync:ChartAxis.Header> <TextBlock Text="Date" FontFamily="Segoe UI" FontSize="12"/> </sync:ChartAxis.Header> </sync:ChartAxis> </sync:ChartArea.PrimaryAxis> <sync:ChartArea.SecondaryAxis> <sync:ChartAxis> <sync:ChartAxis.Header> <TextBlock Name="yAxisHeader" FontFamily="Segoe UI" FontSize="12"/> </sync:ChartAxis.Header> </sync:ChartAxis> </sync:ChartArea.SecondaryAxis> </sync:ChartArea> </sync:Chart> <sync:TimeLineControl x:Name="timelineControl" Grid.Row="2" StartValue="0" EndValue="85" IsContextMenuEnabled="True"> <sync:TimeLineControl.PrimaryAxis> <sync:ChartAxis x:Name="primary" TickSize="0" SmallTickSize="0" RangePadding="None" EdgeLabelsDrawingMode="Fit" IntersectAction="Hide" ValueType="DateTime" LabelDateTimeFormat="yyyy" Interval="1" LabelPosition="Outside" LabelForeground="#FFD8F0F0" OpposedPosition="True"> <sync:ChartArea.ShowGridLines>False</sync:ChartArea.ShowGridLines> <sync:ChartArea.ShowMajorGridLines>True</sync:ChartArea.ShowMajorGridLines> <sync:ChartArea.OriginLineStroke> <Pen Brush="#FF4A9BC6" Thickness="1"/> </sync:ChartArea.OriginLineStroke> <sync:ChartAxis.LineStroke> <Pen Brush="#FF4A9BC6" Thickness="1"/> </sync:ChartAxis.LineStroke> </sync:ChartAxis> </sync:TimeLineControl.PrimaryAxis> <sync:TimeLineControl.Axes> <sync:ChartAxis x:Name="quater" ValueType="DateTime"> <sync:ChartArea.ShowGridLines>True</sync:ChartArea.ShowGridLines> <sync:ChartArea.ShowMajorGridLines>True</sync:ChartArea.ShowMajorGridLines> <sync:ChartAxis.LabelTemplate> <DataTemplate> <TextBlock Text="{Binding Converter={StaticResource LabelConverterKey}}"/> </DataTemplate> </sync:ChartAxis.LabelTemplate> </sync:ChartAxis> </sync:TimeLineControl.Axes> </sync:TimeLineControl> </Grid> </UserControl>
6. Test Window XMAL
<Window x:Class="TimeLineControlSample.TestCustomerControls" xmlns:my="clr-namespace:TimeLineControlSample.UserControls" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="TestCustomerControls" MinHeight="300" MinWidth="300"> <Grid> <my:CitiFinancialChart x:Name="cfc" /> </Grid> </Window>
7. Test Window Backend Code
public partial class TestCustomerControls : Window { public TestCustomerControls() { InitializeComponent(); DataCollection collection = new DataCollection(); cfc.SetChartData(collection.datalist, new Dictionary<string, string> { { "High", "High" }, { "Low", "Low" }, { "Last", "Last" } }, "TimeStamp", "date", null); } }