Here's an example of how you might do an intersecting marker overlay:
The xaml:
<igChart:XamChart x:Name="theChart"> <igChart:XamChart.Crosshairs> <igChart:Crosshairs x:Name="crosshairs" DisplayMode="Vertical" Enabled="True"> </igChart:Crosshairs> </igChart:XamChart.Crosshairs> <igChart:XamChart.Series> <igChart:Series ChartType="Line"> <igChart:Series.DataPoints> <igChart:DataPoint Label="0" Value="1" /> <igChart:DataPoint Label="1" Value="2" /> <igChart:DataPoint Label="2" Value="3" /> <igChart:DataPoint Label="3" Value="4" /> <igChart:DataPoint Label="4" Value="1" /> <igChart:DataPoint Label="5" Value="2" /> <igChart:DataPoint Label="6" Value="3" /> <igChart:DataPoint Label="7" Value="4" /> </igChart:Series.DataPoints> </igChart:Series> </igChart:XamChart.Series> </igChart:XamChart> <local:CrosshairAugmentor Chart="{Binding ElementName=theChart}" XValue="{Binding ElementName=crosshairs, Path=GraphX}"> </local:CrosshairAugmentor>
And the code behind:
public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); } } public class CrosshairAugmentor : FrameworkElement { public static readonly DependencyProperty XValueProperty = DependencyProperty.Register( "XValue", typeof(double), typeof(CrosshairAugmentor), new PropertyMetadata(0.0, (o, e) => (o as CrosshairAugmentor).OnXValueChanged( (double)e.OldValue, (double)e.NewValue))); public double XValue { get { return (double)GetValue(XValueProperty); } set { SetValue(XValueProperty, value); } } private void OnXValueChanged( double oldValue, double newValue) { Update(); } public static readonly DependencyProperty ChartProperty = DependencyProperty.Register( "Chart", typeof(XamChart), typeof(CrosshairAugmentor), new PropertyMetadata(null, (o, e) => (o as CrosshairAugmentor).OnChartChanged( (XamChart)e.OldValue, (XamChart)e.NewValue))); public XamChart Chart { get { return (XamChart)GetValue(ChartProperty); } set { SetValue(ChartProperty, value); } } private void OnChartChanged( XamChart oldValue, XamChart newValue) { Update(); } private Canvas _canv; private void Update() { if (Chart == null) { return; } if (Chart.Series.Count < 1) { return; } Series s = Chart.Series[0]; if (_canv == null) { _canv = new Canvas(); //_canv.Background = new SolidColorBrush(Colors.Red); (Chart.Parent as Panel).Children.Add(_canv); } _canv.Children.Clear(); System.Diagnostics.Debug.WriteLine(XValue); int beforeIndex = (int)Math.Floor(XValue); int afterIndex = (int)Math.Ceiling(XValue); if (beforeIndex < 0) { beforeIndex = 0; } if (afterIndex < 0) { afterIndex = 0; } if (beforeIndex == 0) { return; } beforeIndex--; afterIndex--; if (afterIndex > s.DataPoints.Count - 1) { return; } double yBefore = s.DataPoints[beforeIndex].Value; double yAfter = s.DataPoints[afterIndex].Value; double p = XValue - Math.Floor(XValue); double q = 1.0 - p; double yValue = (yAfter * p) + (yBefore * q); double xValue = XValue; double posX = Chart.GetPosition(AxisType.PrimaryX, xValue); double posY = Chart.GetPosition(AxisType.PrimaryY, yValue); var el = new Ellipse(); el.IsHitTestVisible = false; el.Fill = new SolidColorBrush(Colors.Red); el.Width = 7; el.Height = 7; el.Stroke = new SolidColorBrush(Colors.Black); el.StrokeThickness = 2; Point plotAt = new Point(posX - 3.5, posY - 3.5); Canvas.SetTop(el, plotAt.Y); Canvas.SetLeft(el, plotAt.X); _canv.Children.Add(el); } }
-Graham
Exactly on the line where crosshair touched and not only limited to the data points.
I will give a try for dates based on your pseudocode.
Thanks for your help!
Would you want the marker to show up on the line exactly where the crosshair touched or at the closest point where a data value actually exists?
The data chart works equally well with small numbers of values, it has just been designed from the ground up to handle large volumes of data well.
Math.Floor of the GraphX value should give you the index into the collection of the point before where your crosshair is. Math.Ceiling should give you the next point. If you look them up and record their dates, all you need to do is interpolate between them to see the exact date of the cursor.
some pseudocode:
double p = graphX - Math.Floor(graphX);
double q = 1.0 - p;
DateTime d1 = Items[Math.Floor(graphX)].DateValue;
DateTime d2 = Items[Math.Ceiling(graphX)].DateValue;
DateTime interpolated =
new DateTime(p * d2.Ticks + q * d1.Ticks);
something like that. Hope this helps!
Hi Graham,
Yes, crosshair line combined with a marker showing up where the crosshair touches the line series. Havent explored the xamdatachart, as if my understanding is correct is best used for large amount of data. We just have about 15-30 data points on the chart. Let me know if that is possible on a xamchart.
I also had another question about the crosshairs. I have dates on my x-axis, the crosshair X value just shows the point value and not the corresponding dates. I do understand that it uses dates just as label while the actual points drawn will be 1,2,...n. But, is there a way to get the corresponding date value?
Are you talking about just a crosshair line or that combined with a marker showing up where the crosshair touches the line series? We have samples on how you would accomplish this with the XamDataChart, but none for the XamChart. The XamChart aggressively refreshes when you change information about the markers, so you would have to approach it by overlaying some information in a layer on top of the chart, instead. But I'm uncertain if we provide enough information for where to render the marker. Have you looked into using the XamDataChart for this scenario? If you can confirm this is what you are trying to do, I can see if you might be able to do it with the XamChart.