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!
Below is an example that should get you started on how to implement informative strips.Remember to make a feature request if you would like to see this functionality native to the chart.The Xaml:
<UserControl.Resources> <local:TestData x:Key="vm" /> </UserControl.Resources> <Grid x:Name="LayoutRoot" Background="White"> <ig:XamDataChart 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 StartX="2" EndX="3" Fill="Red" Label="Strip1"/> <local:StripInfo StartX="5" EndX="6" 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> </Grid>
And the code behind:
public partial class MainPage : UserControl { public MainPage() { InitializeComponent(); } } 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 { 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 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; 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 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 (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); } 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; } }
Let me know if this helps!
It should produce a result that looks like this:
Thank you for all the work you did here!
I want to test it now but I don’t know what references you are using, I can’t find the reference that will let me use the <local:ChartStripsBehavior>,
I did download the latest service release (2010.2) and it’s installed.
How do I add the local:ChartStripsBehavior to the Xaml page
Thank You,
Pieter,
ChartStripsBehavior is defined in the code behind section of the post. So when you copy in that code, it will be defined in your local assembly. You need to define a namespace prefix to access types in your local assembly from xaml, so up after the other namspace prefix decralations add this:
xmlns:local="clr-namespace:X"
where X is the namespace were you put ChartStripsBehavior. If that is in a seperate assembly than the one where you are writing this xaml page, then you will need something like this instead:
xmlns:local="clr-namespace:X;assembly=Y"
where X is the namespace that contains ChartStripsBehavior and Y is the assembly that contains ChartStripsBehavior.
see here for more info: http://msdn.microsoft.com/en-us/library/ms747086.aspx
That help?
Hi Graham
Thanks it helped a lot!
It’s almost working now; I am just having a problem with the ClearExisting function. It’s giving me a compile error on the ‘xAxis.RootCanvas.Children’ Description is – ‘Could not find an implementation of the query pattern for source type system.Windows.Controls.UIElementCollection'. 'Where' not found. Consider explicitly specifying the type of the range variable 'child'.’ Any idea how to fix this?
Thank you!
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.