These images are of same graph but on zoom in zoomout of graph and on stretch and compression of window. Can any body suggest some thing to fix the problem Thanks.
Message before inserting images:
Hi,
I am working on xamdatachart markers I am trying to display markers on a series based on data not to all dataponts. I am successfull to do so but I have identified some abnormal behavours. On zoomin,zoomout of chart and on compression and stretching window its appearing and dis apearing from the series. Please find attached screen shots for better insight of the issue:
Are those scatter line series or line series? Which axes are you using? How many data points are in each series? That behavior is strange, but we might need to have more information about your data to replicate. Can you provide a sample?
Thanks,
Graham
Hi Graham,
Thanks for all of the great examples you've posted. They're really helping me get up to speed on the xamDataChart, which is a really nice control! Question on the point annotations based on the sample code you posted: how can I adjust the z order for the annotations? They're drawing behind some of the series, but I'd like them to draw on top. This is what it looks like now:
I played with setting the ZIndex and also looked around for a different "annotation" canvas that might draw after all of the series elements, but haven't found anything yet that works. Any suggestions would be most appreciated.
Gary Sinner
petroWEB
The current version of that code is storing the annotations in the first series' canvas. Which is z ordered lowest. You can change it to store them in the last series' canvas. By doing something like this:
private Series GetSeries() { if (_owner == null) { return null; } return _owner.Series.LastOrDefault(); }
You may also need to change ClearExisting to make sure it checks all series' canvases for annotations to remove if you do things that will reorder the series.
An alternate solution might be to set the ZIndex on the first series to a high number since thats were the annotations are put.
Yet another solution would be to put a new canvas in the parent that holds the series visually, give it a high ZIndex and put the annotations in that.
Does this help?
-Graham
Here's a version that uses a separate canvas for the annotations:Code behind:
public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); } private void click_Click(object sender, RoutedEventArgs e) { (Resources["vm"] as TestData)[1].Value++; } } public class TestData : ObservableCollection<TestDataItem> { public TestData() { Add(new TestDataItem() { Label = "1", Value = 1 }); Add(new TestDataItem() { Label = "2", Value = 2 }); Add(new TestDataItem() { Label = "3", Value = 3 }); Add(new TestDataItem() { Label = "4", Value = 4 }); Add(new TestDataItem() { Label = "5", Value = 5 }); Add(new TestDataItem() { Label = "6", Value = 6 }); Add(new TestDataItem() { Label = "7", Value = 7 }); } } public class TestDataItem : INotifyPropertyChanged { private string _label; public string Label { get { return _label; } set { _label = value; RaisePropertyChanged("Label"); } } private double _value; public double Value { get { return _value; } set { _value = value; RaisePropertyChanged("Value"); } } public event PropertyChangedEventHandler PropertyChanged; private void RaisePropertyChanged(string propertyName) { if (PropertyChanged != null) { PropertyChanged( this, new PropertyChangedEventArgs(propertyName)); } } } public class ChartBehaviors : DependencyObject { public static readonly DependencyProperty ChartPointsProperty = DependencyProperty.RegisterAttached("ChartPoints", typeof(ChartPointsBehavior), typeof(ChartBehaviors), new PropertyMetadata(null, (o, e) => CursorTooltipChanged( o as XamDataChart, e.OldValue as ChartPointsBehavior, e.NewValue as ChartPointsBehavior))); public static ChartPointsBehavior GetChartPoints( DependencyObject target) { return target.GetValue(ChartPointsProperty) as ChartPointsBehavior; } public static void SetChartPoints( DependencyObject target, ChartPointsBehavior behavior) { target.SetValue(ChartPointsProperty, behavior); } private static void CursorTooltipChanged( XamDataChart chart, ChartPointsBehavior oldValue, ChartPointsBehavior newValue) { if (chart == null) { return; } if (oldValue != null) { oldValue.OnDetach(chart); } if (newValue != null) { newValue.OnAttach(chart); } } } public class PointInfo : FrameworkElement { public PointInfo() { _id = Guid.NewGuid(); X = double.NaN; Y = double.NaN; } private PointInfo(Guid id) { _id = id; X = double.NaN; Y = double.NaN; } private Guid _id; public Guid Id { get { return _id; } private set { _id = value; } } public static readonly DependencyProperty XProperty = DependencyProperty.Register( "X", typeof(double), typeof(PointInfo), new PropertyMetadata(0.0, (o, e) => (o as PointInfo).OnPropertyChanged( "X", e.OldValue, e.NewValue))); private void OnPropertyChanged(string propertyName, object oldValue, object newValue) { if (Owner != null) { Owner.SignalNeedsRefresh(); } } public double X { get { return (double)GetValue(XProperty); } set { SetValue(XProperty, value); } } public static readonly DependencyProperty YProperty = DependencyProperty.Register( "Y", typeof(double), typeof(PointInfo), new PropertyMetadata(0.0, (o, e) => (o as PointInfo).OnPropertyChanged( "Y", e.OldValue, e.NewValue))); public double Y { get { return (double)GetValue(YProperty); } set { SetValue(YProperty, value); } } public static readonly DependencyProperty LabelProperty = DependencyProperty.Register( "Label", typeof(string), typeof(PointInfo), new PropertyMetadata("", (o, e) => (o as PointInfo).OnPropertyChanged( "Label", e.OldValue, e.NewValue))); public string Label { get { return (string)GetValue(LabelProperty); } set { SetValue(LabelProperty, value); } } public Brush Fill { get; set; } public DataTemplate PointTemplate { get; set; } public static readonly DependencyProperty DataProperty = DependencyProperty.Register( "Data", typeof(object), typeof(PointInfo), new PropertyMetadata(null, (o, e) => (o as PointInfo).OnPropertyChanged( "Data", e.OldValue, e.NewValue))); public object Data { get { return GetValue(DataProperty); } set { SetValue(DataProperty, value); } } public static readonly DependencyProperty DataIndexProperty = DependencyProperty.Register( "DataIndex", typeof(int), typeof(PointInfo), new PropertyMetadata(-1, (o, e) => (o as PointInfo).OnPropertyChanged( "DataIndex", e.OldValue, e.NewValue))); public int DataIndex { get { return (int)GetValue(DataIndexProperty); } set { SetValue(DataIndexProperty, value); } } public static readonly DependencyProperty SeriesIndexProperty = DependencyProperty.Register( "SeriesIndex", typeof(int), typeof(PointInfo), new PropertyMetadata(0, (o, e) => (o as PointInfo).OnPropertyChanged( "SeriesIndex", e.OldValue, e.NewValue))); public int SeriesIndex { get { return (int)GetValue(SeriesIndexProperty); } set { SetValue(SeriesIndexProperty, value); } } public PointInfo Clone() { PointInfo newPointInfo = new PointInfo(_id); newPointInfo.X = X; newPointInfo.Y = Y; newPointInfo.Label = Label; newPointInfo.PointTemplate = PointTemplate; newPointInfo.Fill = Fill; newPointInfo.DataIndex = DataIndex; BindingOperations.SetBinding( newPointInfo, PointInfo.DataProperty, new Binding("Data") { Source = this }); return newPointInfo; } public PointInfoCollection Owner { get; set; } } public class PointInfoCollection : ObservableCollection<PointInfo> { protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e) { base.OnCollectionChanged(e); if (e.OldItems != null) { foreach (var item in e.OldItems.OfType<PointInfo>()) { item.Owner = null; } } if (e.NewItems != null) { foreach (var item in e.NewItems.OfType<PointInfo>()) { item.Owner = this; } } } internal void SignalNeedsRefresh() { if (NeedsRefresh != null) { NeedsRefresh(this); } } public delegate void NeedsRefreshEventHandler(object o); public event NeedsRefreshEventHandler NeedsRefresh; } public class AnnotationCanvas : Canvas { } public class ChartPointsBehavior : DependencyObject { public ChartPointsBehavior() { Points = new PointInfoCollection(); } public static readonly DependencyProperty StripsProperty = DependencyProperty.Register("Points", typeof(PointInfoCollection), typeof(ChartPointsBehavior), new PropertyMetadata(null, (o, e) => (o as ChartPointsBehavior) .OnPointsChanged( e.OldValue as PointInfoCollection, e.NewValue as PointInfoCollection))); public PointInfoCollection Points { get { return (PointInfoCollection)GetValue(StripsProperty); } set { SetValue(StripsProperty, value); } } public DataTemplate PointTemplate { get; set; } private void OnPointsChanged( PointInfoCollection oldValue, PointInfoCollection newValue) { if (oldValue != null) { oldValue.CollectionChanged -= Points_CollectionChanged; oldValue.NeedsRefresh -= NewValue_NeedsRefresh; } if (newValue != null) { newValue.CollectionChanged += Points_CollectionChanged; newValue.NeedsRefresh += NewValue_NeedsRefresh; } RefreshPoints(); } void NewValue_NeedsRefresh(object o) { RefreshPoints(); } void Points_CollectionChanged( object sender, NotifyCollectionChangedEventArgs e) { RefreshPoints(); } private XamDataChart _owner = null; private AnnotationCanvas _canvas = null; public void OnAttach(XamDataChart chart) { if (_owner != null) { OnDetach(_owner); } _owner = chart; _owner.WindowRectChanged += Owner_WindowRectChanged; _owner.SizeChanged += Owner_SizeChanged; _owner.Axes.CollectionChanged += Axes_CollectionChanged; _owner.Axes.CollectionResetting += Axes_CollectionResetting; RefreshPoints(); } void Owner_SizeChanged(object sender, SizeChangedEventArgs e) { RefreshPoints(); } void Axes_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e) { if (e.OldItems != null) { foreach (Axis axis in e.OldItems) { axis.SizeChanged -= Axis_SizeChanged; } } if (e.NewItems != null) { foreach (Axis axis in e.NewItems) { axis.SizeChanged += Axis_SizeChanged; } } RefreshPoints(); } void Axis_SizeChanged(object sender, SizeChangedEventArgs e) { RefreshPoints(); } void Axes_CollectionResetting(object sender, EventArgs e) { foreach (Axis axis in _owner.Axes) { axis.SizeChanged -= Axis_SizeChanged; } RefreshPoints(); } void Owner_WindowRectChanged(object sender, Infragistics.RectChangedEventArgs e) { RefreshPoints(); } private void GetAxes( out Axis xAxis, out Axis yAxis) { if (_owner == null) { xAxis = null; yAxis = null; return; } xAxis = (from axis in _owner.Axes where axis is NumericXAxis || axis is CategoryXAxis || axis is CategoryDateTimeXAxis select axis).FirstOrDefault(); yAxis = (from axis in _owner.Axes where axis is NumericYAxis select axis).FirstOrDefault(); } private Series GetSeries() { if (_owner == null) { return null; } return _owner.Series.FirstOrDefault(); } private AnnotationCanvas GetCanvas() { if (_canvas != null) { return _canvas; } Series series = GetSeries(); if (series == null) { return null; } Panel parent = series.Parent as Panel; if (parent == null) { return null; } _canvas = new AnnotationCanvas(); Canvas.SetZIndex(_canvas, 10000); parent.Children.Add(_canvas); return _canvas; } private void RemoveCanvas() { if (_canvas == null) { return; } Panel parent = _canvas.Parent as Panel; if (parent == null) { _canvas = null; return; } parent.Children.Remove(_canvas); _canvas = null; } private void ClearExisting() { var canvas = GetCanvas(); if (canvas == null) { return; } var existing = from child in canvas.Children .OfType<ContentControl>() where child.Content != null && child.Content is PointInfo select child; existing.ToList().ForEach( (ele) => canvas.Children.Remove(ele)); } private void RefreshPoints() { Axis xAxis; Axis yAxis; GetAxes(out xAxis, out yAxis); if (xAxis == null || yAxis == null || xAxis.RootCanvas == null) { return; } ClearExisting(); Rect viewport = GetViewportRect(xAxis); foreach (PointInfo info in Points) { PointInfo toAdd = info.Clone(); if (double.IsNaN(toAdd.X)) { toAdd.X = viewport.Left; } else { toAdd.X = xAxis.GetScaledValue( toAdd.X, _owner.WindowRect, viewport); } if (double.IsNaN(toAdd.Y)) { toAdd.Y = viewport.Top; } else { toAdd.Y = yAxis.GetScaledValue( toAdd.Y, _owner.WindowRect, viewport); } if (toAdd.PointTemplate == null) { toAdd.PointTemplate = this.PointTemplate; } if (toAdd.PointTemplate != null && !double.IsNaN(toAdd.Y) && !double.IsInfinity(toAdd.Y) && !double.IsNaN(toAdd.X) && !double.IsInfinity(toAdd.X)) { var canvas = GetCanvas(); if (canvas == null) { continue; } var pointControl = new ContentControl(); Canvas.SetZIndex(pointControl, 2000); if (toAdd.DataIndex >= 0 && toAdd.SeriesIndex >= 0 && toAdd.SeriesIndex < _owner.Series.Count) { var series = _owner.Series[toAdd.SeriesIndex]; toAdd.SetBinding( PointInfo.DataProperty, new Binding( "ItemsSource[" + toAdd.DataIndex + "]") { Source = series }); } pointControl.ContentTemplate = toAdd.PointTemplate; pointControl.Content = toAdd; canvas.Children.Add(pointControl); pointControl.Measure( new Size( double.PositiveInfinity, double.PositiveInfinity)); Canvas.SetLeft(pointControl, toAdd.X - (pointControl.DesiredSize.Width / 2.0)); Canvas.SetTop(pointControl, toAdd.Y - (pointControl.DesiredSize.Height / 2.0)); } } } private Rect GetViewportRect( Axis axis) { double top = 0; double bottom = axis.ActualHeight; double left = 0; double right = axis.ActualWidth; double width = right - left; double height = bottom - top; if (width > 0.0 && height > 0.0) { return new Rect(left, top, width, height); } return Rect.Empty; } public void OnDetach(XamDataChart chart) { if (_owner != chart) { return; } _owner.WindowRectChanged -= Owner_WindowRectChanged; _owner.SizeChanged -= Owner_SizeChanged; _owner.Axes.CollectionChanged -= Axes_CollectionChanged; _owner.Axes.CollectionResetting -= Axes_CollectionResetting; Axis xAxis; Axis yAxis; GetAxes(out xAxis, out yAxis); if (xAxis == null || yAxis == null) { return; } ClearExisting(); RemoveCanvas(); _owner = null; } }
Hope this helps!-Graham
HI Graham,
Thanks for the solution. Thats exactly what I was looking for.
Ditto for me. Working great now. Thanks Graham!
Gary