Hi,
I would like to know how to use the Strip line in the XamDataChart. I would like to add different colour bands on the chart according to the data in the chart? Can anyone please advise me on this function?
Thanks,
Pieter
Pieter, you can set a brush on the Strip property on both axes. You should pick a color with some transparency (change the alpha value) and you will see a neat effect on the strip crossing. But are you talking about putting the strip at a configurable set of values? We don't directly support this, but I may be able to help you out with a sample. Could you explain in more detail what you are trying to do?
-Graham
Hi Graham,
Thanks again for replying.
I am adding an image this is just an example of what I should do. There is a trace on the graph if it drops below 0 it should make the background red, if it stays on a point for longer than 10 minutes the background must be orange and the last one if it goes above 20 it must change the background to green. My problem is I don’t know how to do this checks in XAML and I don’t know what function to call in C#?
I will appreciate any advice on this. The XAML code is the same as my other post on the X Axis.
Thank You!
It works perfect! Thank you so much!!
Will it be possible to add new strips in the code behind, I did try something but it's not working. Don't know if it's possible.
StripLines.StripInfo SI = new StripLines.StripInfo();
//StripInfo SI = new StripInfo();
SI.StartX = 3;
SI.EndX = 4;
SI.Label = "Test";
Brush br = Brushes.Blue;
SI.Fill = br;
I am new to WPF and did not touch Silverlight jet so all this is still new to me.
Thanks for all the help!
If you've defined the ChartStripsBehavior in the xaml, then you can programmatically add strips like this:
StripInfo si = new StripInfo(); si.StartX = 1; si.EndX = 2; si.Fill = new SolidColorBrush(Colors.Green); ChartBehaviors.GetChartStrips(theChart).Strips.Add(si);
If you haven't then you can set up the ChartStripsBehavior in the code behind and then call
ChartBehaviors.SetChartStrips(theChart, stripsBehavior);
Let me know if you have any questions. The above is what's known as an Attached Property in Silverlight/WPF.
Thanks Graham,
It’s working perfect. Will it be possible to use this strip lines on a CategoryDatetimeXAxis?
I the play around with the sample but I am unable to get it working with the CategoryDatetimeXAxis. I am using the datetime axis in my project. It’s the project in this (http://forums.infragistics.com/forums/p/44925/247753.aspx#247753) post I am working on.
Pieter,
One of our DSEs has already openned a case for you in the forum post you provided above, and you will receive a follow up from the DSE.
Thank you,
Sam
Here's a version that should let you specify dates
<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> <Grid 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> </Grid> </DataTemplate> </local:ChartStripsBehavior.StripTemplate> <local:ChartStripsBehavior.Strips> <!-- create a strip between indexes 2 and 3 on the category axis --> <local:StripInfo UseDates="True" StartDate="1/2/2000" EndDate="1/3/2000" Fill="Red" Label="Strip1"/> <local:StripInfo UseDates="True" StartDate="1/5/2000" EndDate="1/6/2000" Fill="Blue" Label="Strip2" /> </local:ChartStripsBehavior.Strips> </local:ChartStripsBehavior> </local:ChartBehaviors.ChartStrips> <ig:XamDataChart.Axes> <ig:CategoryDateTimeXAxis x:Name="xAxis" ItemsSource="{StaticResource vm}" Label="{}{Date}" DateTimeMemberPath="Date"/> <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 here is the updated 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() { Date = new DateTime(2000, 1, 1), Value = 1 }); Add(new TestDataItem() { Date = new DateTime(2000, 1, 2), Value = 2 }); Add(new TestDataItem() { Date = new DateTime(2000, 1, 3), Value = 3 }); Add(new TestDataItem() { Date = new DateTime(2000, 1, 4), Value = 4 }); Add(new TestDataItem() { Date = new DateTime(2000, 1, 5), Value = 5 }); Add(new TestDataItem() { Date = new DateTime(2000, 1, 6), Value = 6 }); Add(new TestDataItem() { Date = new DateTime(2000, 1, 7), Value = 7 }); } } public class TestDataItem { public DateTime Date { 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.Abs(EndX - StartX); } } 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!-Graham
Aha. I was using a CategoryXAxis when I should have been using a CategoryDateTimeXAxis with the DateTime MemberPath set.
My strips are not drawing. Do you have any idea why the GetScaledValue function would return numbers like 1.1378021398827587E+18 for a date of 11/1/2011 1:00:00 AM (634557060000000000 Ticks), a window {0,0,1,1}, and a viewport {0,0,467.99,277.04}? That's the end date. The start date is 7/20/2011 1:00:00 AM (634467204000000000 Ticks) scales to 1.137641022222069E+18. I don't think this function is behaving correctly. Do you happen to have a scaling function that works correctly?
An approach like above presented needs to be more complicated in order to deal with that kind of volume. Is there a sample that you can provide that replicates your scenario? I may be able to point out how you can adjust this extension in order to perform well with that level of annotation. But, as a warning, the code is likely to be more complicated.
Performance is absolutely horrendeous on these strips. I have a chart with 2000 points and 200 annotations like this.
For anyone else interested, Graham's solution relies on XamDataChart events firing to refresh strips which you may need to address when the chart is set as the XamZoombar preview content.
Additionally the GetScaledValue calls rely on x axis ActualMinimumValue and ActualMaximumValue having meaningful values, which in my situation was not the case. This is easily overcome by implementing your own scaling method.