Hi,
Is the any way to draw any constant line in XamDataChart?
I'm interested on draw a line for max value (and min value) from a serie.
Thanks in advanced
You could probably adapt this sample: http://community.infragistics.com/forums/p/45157/246174.aspx#246174
to display such a line instead of strips.
You could also just create a line series or scatter line series based on your calculation of the minimum and maximum.
You may also want to look at the custom indicator series, for drawing horizontal lines.
-Graham
Hi Graham,
can you post any example for custom indicator? seems the best approach.
Thanks
Here's an example of how to approach this using custom indicators:
<UserControl.Resources> <local:TestData x:Key="data" /> </UserControl.Resources> <Grid x:Name="LayoutRoot" Background="White"> <igChart:XamDataChart x:Name="theChart"> <igChart:XamDataChart.Axes> <igChart:CategoryXAxis x:Name="xAxis" ItemsSource="{StaticResource data}" Label="{}{Label}" /> <igChart:NumericYAxis x:Name="yAxis" MinimumValue="-100" MaximumValue="100" /> </igChart:XamDataChart.Axes> <igChart:XamDataChart.Series> <igChart:LineSeries x:Name="line" ItemsSource="{StaticResource data}" XAxis="{Binding ElementName=xAxis}" YAxis="{Binding ElementName=yAxis}" ValueMemberPath="Value" /> <igChart:CustomIndicator x:Name="min" Indicator="CustomIndicator_Indicator" BasedOnColumns="CustomIndicator_BasedOnColumns" ItemsSource="{StaticResource data}" XAxis="{Binding ElementName=xAxis}" YAxis="{Binding ElementName=yAxis}" OpenMemberPath="Value" /> <igChart:CustomIndicator x:Name="max" Indicator="max_Indicator" BasedOnColumns="CustomIndicator_BasedOnColumns" ItemsSource="{StaticResource data}" XAxis="{Binding ElementName=xAxis}" YAxis="{Binding ElementName=yAxis}" OpenMemberPath="Value" /> </igChart:XamDataChart.Series> </igChart:XamDataChart> </Grid>
With code behind:
public partial class MainPage : UserControl { public MainPage() { InitializeComponent(); } private void CustomIndicator_Indicator(object sender, Infragistics.Controls.Charts.FinancialEventArgs e) { double min = e.DataSource.OpenColumn.Min(); for (int i = 0; i < e.DataSource.IndicatorColumn.Count; i++) { e.DataSource.IndicatorColumn[i] = min; } } private void CustomIndicator_BasedOnColumns(object sender, Infragistics.Controls.Charts.FinancialEventArgs e) { e.BasedOn = new List<string>() { "OpenColumn" }; } private void max_Indicator(object sender, Infragistics.Controls.Charts.FinancialEventArgs e) { double max = e.DataSource.OpenColumn.Max(); for (int i = 0; i < e.DataSource.IndicatorColumn.Count; i++) { e.DataSource.IndicatorColumn[i] = max; } } } public class TestDataItem { public string Label { get; set; } public double Value { get; set; } } public class TestData : ObservableCollection<TestDataItem> { private static Random _rand = new Random(); public TestData() { double curr = 10; for (int i = 0; i < 400; i++) { if (_rand.NextDouble() > .5) { curr += _rand.NextDouble() * 3.0; } else { curr -= _rand.NextDouble() * 3.0; } if (curr < -90) { curr = -90; } if (curr > 90) { curr = 90; } Add(new TestDataItem() { Label = i.ToString(), Value = curr }); } } }
Thanks a lot, it works fine
I have the same question with vertical lines: how is it possible to draw vertical lines at specific places in the chart. The previous solution is very good for horizontal lines, but doesn't work for vertical lines.
Need help!
Thank you in advance,
JRG
I made a couple changes to an existing sample of mine to show you one way that you can accomplish this. If you are dealing with a scatter chart and want to draw a vertical line, you can just add an additional scatterline series that draws a line from the minimum to the maximum of your other series y values.Here is the Xaml:
<Window.Resources> <local:TestData x:Key="vm" /> </Window.Resources> <Grid x:Name="LayoutRoot" Background="White"> <Grid.RowDefinitions> <RowDefinition /> <RowDefinition Height="Auto"/> </Grid.RowDefinitions> <ig:XamDataChart x:Name="theChart" VerticalZoomable="True" HorizontalZoomable="True" > <local:ChartBehaviors.ChartStrips> <local:ChartStripsBehavior> <local:ChartStripsBehavior.StripTemplate> <DataTemplate> <Border Background="{Binding Fill}" Opacity=".4" Width="{Binding Width}" Height="{Binding Height}" > <TextBlock FontSize="20" HorizontalAlignment="Center" VerticalAlignment="Center" Text="{Binding Label}" RenderTransformOrigin=".5,.5" > <TextBlock.RenderTransform> <RotateTransform Angle="90" /> </TextBlock.RenderTransform> </TextBlock> </Border> </DataTemplate> </local:ChartStripsBehavior.StripTemplate> <local:ChartStripsBehavior.Strips> <!-- create a strip between indexes 2 and 3 on the category axis --> <local:StripInfo UseDates="False" StartX="2" EndX="2" Fill="Red" Label="Strip1"/> <local:StripInfo UseDates="False" StartX="4" EndX="4" Fill="Blue" Label="Strip2" /> </local:ChartStripsBehavior.Strips> </local:ChartStripsBehavior> </local:ChartBehaviors.ChartStrips> <ig:XamDataChart.Axes> <ig:CategoryXAxis x:Name="xAxis" ItemsSource="{StaticResource vm}" Label="{}{Label}" /> <ig:NumericYAxis x:Name="yAxis" /> </ig:XamDataChart.Axes> <ig:XamDataChart.Series> <ig:LineSeries x:Name="line" XAxis="{Binding ElementName=xAxis}" YAxis="{Binding ElementName=yAxis}" ValueMemberPath="Value" ItemsSource="{StaticResource vm}"/> </ig:XamDataChart.Series> </ig:XamDataChart> <Button Grid.Row="1" x:Name="push" Content="push" Click="push_Click" /> </Grid>
And the code behind:
public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); } private void push_Click(object sender, RoutedEventArgs e) { StripInfo si = new StripInfo(); si.StartX = 1; si.EndX = 2; si.Fill = new SolidColorBrush(Colors.Green); ChartBehaviors.GetChartStrips(theChart).Strips.Add(si); } } public class TestData : ObservableCollection<TestDataItem> { public TestData() { Add(new TestDataItem() { Label = "A", Value = 1 }); Add(new TestDataItem() { Label = "B", Value = 2 }); Add(new TestDataItem() { Label = "C", Value = 3 }); Add(new TestDataItem() { Label = "D", Value = 4 }); Add(new TestDataItem() { Label = "E", Value = 5 }); Add(new TestDataItem() { Label = "F", Value = 6 }); Add(new TestDataItem() { Label = "G", Value = 7 }); } } public class TestDataItem { public string Label { get; set; } public double Value { get; set; } } public class ChartBehaviors : DependencyObject { public static readonly DependencyProperty ChartStripsProperty = DependencyProperty.RegisterAttached("ChartStrips", typeof(ChartStripsBehavior), typeof(ChartBehaviors), new PropertyMetadata(null, (o, e) => CursorTooltipChanged( o as XamDataChart, e.OldValue as ChartStripsBehavior, e.NewValue as ChartStripsBehavior))); public static ChartStripsBehavior GetChartStrips( DependencyObject target) { return target.GetValue(ChartStripsProperty) as ChartStripsBehavior; } public static void SetChartStrips( DependencyObject target, ChartStripsBehavior behavior) { target.SetValue(ChartStripsProperty, behavior); } private static void CursorTooltipChanged( XamDataChart chart, ChartStripsBehavior oldValue, ChartStripsBehavior newValue) { if (chart == null) { return; } if (oldValue != null) { oldValue.OnDetach(chart); } if (newValue != null) { newValue.OnAttach(chart); } } } public class StripInfo { public StripInfo() { _id = Guid.NewGuid(); StartX = double.NaN; EndX = double.NaN; StartY = double.NaN; EndY = double.NaN; } private StripInfo(Guid id) { _id = id; StartX = double.NaN; EndX = double.NaN; StartY = double.NaN; EndY = double.NaN; } private Guid _id; public Guid Id { get { return _id; } private set { _id = value; } } public double StartX { get; set; } public double EndX { get; set; } public DateTime StartDate { get; set; } public DateTime EndDate { get; set; } public bool UseDates { get; set; } public double StartY { get; set; } public double EndY { get; set; } public string Label { get; set; } public double Width { get { return Math.Max(Math.Abs(EndX - StartX), 1.0); } } public double Height { get { return Math.Abs(EndY - StartY); } } public Brush Fill { get; set; } public DataTemplate StripTemplate { get; set; } public StripInfo Clone() { StripInfo newStripInfo = new StripInfo(_id); newStripInfo.StartX = StartX; newStripInfo.StartY = StartY; newStripInfo.EndX = EndX; newStripInfo.EndY = EndY; newStripInfo.Label = Label; newStripInfo.StripTemplate = StripTemplate; newStripInfo.Fill = Fill; newStripInfo.UseDates = UseDates; newStripInfo.StartDate = StartDate; newStripInfo.EndDate = EndDate; return newStripInfo; } } public class StripInfoCollection : ObservableCollection<StripInfo> { } public class ChartStripsBehavior : DependencyObject { public ChartStripsBehavior() { Strips = new StripInfoCollection(); } public static readonly DependencyProperty StripsProperty = DependencyProperty.Register("Strips", typeof(StripInfoCollection), typeof(ChartStripsBehavior), new PropertyMetadata(null, (o, e) => (o as ChartStripsBehavior) .OnStripsChanged( e.OldValue as StripInfoCollection, e.NewValue as StripInfoCollection))); public StripInfoCollection Strips { get { return (StripInfoCollection)GetValue(StripsProperty); } set { SetValue(StripsProperty, value); } } public DataTemplate StripTemplate { get; set; } private void OnStripsChanged( StripInfoCollection oldValue, StripInfoCollection newValue) { if (oldValue != null) { oldValue.CollectionChanged -= Strips_CollectionChanged; } if (newValue != null) { newValue.CollectionChanged += Strips_CollectionChanged; } RefreshStrips(); } void Strips_CollectionChanged( object sender, NotifyCollectionChangedEventArgs e) { RefreshStrips(); } private XamDataChart _owner = 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; RefreshStrips(); } void Owner_SizeChanged(object sender, SizeChangedEventArgs e) { RefreshStrips(); } 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; } } RefreshStrips(); } void Axis_SizeChanged(object sender, SizeChangedEventArgs e) { RefreshStrips(); } void Axes_CollectionResetting(object sender, EventArgs e) { foreach (Axis axis in _owner.Axes) { axis.SizeChanged -= Axis_SizeChanged; } RefreshStrips(); } void Owner_WindowRectChanged(object sender, Infragistics.RectChangedEventArgs e) { RefreshStrips(); } 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 void ClearExisting(Axis xAxis) { if (xAxis.RootCanvas == null) { return; } var existing = from child in xAxis.RootCanvas.Children.OfType<UIElement>() where child is ContentControl && (child as ContentControl).Content != null && (child as ContentControl).Content is StripInfo select child; existing.ToList().ForEach( (ele) => xAxis.RootCanvas.Children.Remove(ele)); } private void RefreshStrips() { Axis xAxis; Axis yAxis; GetAxes(out xAxis, out yAxis); if (xAxis == null || yAxis == null || xAxis.RootCanvas == null) { return; } ClearExisting(xAxis); Rect viewport = GetViewportRect(xAxis); foreach (StripInfo info in Strips) { StripInfo toAdd = info.Clone(); if (!toAdd.UseDates) { if (double.IsNaN(toAdd.StartX)) { toAdd.StartX = viewport.Left; } else { toAdd.StartX = xAxis.GetScaledValue( toAdd.StartX, _owner.WindowRect, viewport); } if (double.IsNaN(toAdd.EndX)) { toAdd.EndX = viewport.Right; } else { toAdd.EndX = xAxis.GetScaledValue( toAdd.EndX, _owner.WindowRect, viewport); } } else { toAdd.StartX = xAxis.GetScaledValue(toAdd.StartDate.Ticks, _owner.WindowRect, viewport); toAdd.EndX = xAxis.GetScaledValue(toAdd.EndDate.Ticks, _owner.WindowRect, viewport); } if (double.IsNaN(toAdd.StartY)) { toAdd.StartY = viewport.Top; } else { toAdd.StartY = yAxis.GetScaledValue( toAdd.StartY, _owner.WindowRect, viewport); } if (double.IsNaN(toAdd.EndY)) { toAdd.EndY = viewport.Bottom; } else { toAdd.EndY = yAxis.GetScaledValue( toAdd.EndY, _owner.WindowRect, viewport); } if (toAdd.StripTemplate == null) { toAdd.StripTemplate = this.StripTemplate; } if (toAdd.StripTemplate != null && !double.IsNaN(toAdd.StartY) && !double.IsInfinity(toAdd.StartY) && !double.IsNaN(toAdd.StartX) && !double.IsInfinity(toAdd.StartX)) { ContentControl stripControl = new ContentControl(); stripControl.ContentTemplate = toAdd.StripTemplate; stripControl.Content = toAdd; Canvas.SetLeft(stripControl, toAdd.StartX); Canvas.SetTop(stripControl, toAdd.StartY); xAxis.RootCanvas.Children.Add(stripControl); } } } 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(xAxis); _owner = null; } }
Hope this helps!
You could also probably approximate a vertical line with a step line series or area series, perhaps, that moves to the maximum Y of your other series at the places where you want a line, but I'm not certain you would end up with a straight vertical line.
You really helped me with little working example in post Binding ADO and xamDataChart. Could you give me the same little working example for vertical lines, please. The above example is too complicated for me((