We have a requirement where we need to change the color of column series based on the underlying value. We have a reference value set to 0 for NumericYAxis which means negative value bars need to displayed with different color. Also some bars need to be painted differently based on estimate val etc.
It would be great if we can override some sort of DataTemplate to drive this req based on the data-triggers
please see:
http://community.infragistics.com/forums/p/48210/256989.aspx#256989
-Graham
I was wondering since our requirement is pretty refined, display different color of bar for negative values (our ref point is 0)
We tried using a following style trigger on ColumnSeries, binding seems to be working but trigger is not working.
<Style x:Key="ColumnStyle" TargetType="{x:Type Charts:ColumnSeries}">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=IsNeg}" Value="true">
<Setter Property="Brush" Value="Red"/>
</DataTrigger>
<DataTrigger Binding="{Binding Path=IsNeg}" Value="false">
<Setter Property="Brush" Value="Blue"/>
</Style.Triggers>
</Style>
Any pointers??
Its not simple as we don't let you specify a data template for the columns. I would recommend making a feature request.
But here is how you could do it:
The xaml:
<Window.Resources> <local:TestData x:Key="data" /> </Window.Resources> <Grid> <igChart:XamDataChart x:Name="theChart"> <igChart:XamDataChart.Resources> <Style TargetType="Rectangle"> <Setter Property="local:StyleBinder.StyleBinderHelper"> <Setter.Value> <local:StyleBinderHelper> <local:StyleBinderHelper.Template> <DataTemplate> <local:RectangleColorChanger YAxis="{Binding OuterContext.Series.YAxis}" Value="{Binding Owner.ActualHeight}" Threshold="0" Rectangle="{Binding Owner}" /> </DataTemplate> </local:StyleBinderHelper.Template> </local:StyleBinderHelper> </Setter.Value> </Setter> </Style> </igChart:XamDataChart.Resources> <igChart:XamDataChart.Axes> <igChart:NumericYAxis x:Name="yAxis" /> <igChart:CategoryXAxis x:Name="xAxis" ItemsSource="{StaticResource data}" Label="{}{Label}"/> </igChart:XamDataChart.Axes> <igChart:XamDataChart.Series> <igChart:ColumnSeries x:Name="series" ItemsSource="{StaticResource data}" XAxis="{Binding ElementName=xAxis}" YAxis="{Binding ElementName=yAxis}" ValueMemberPath="Value" /> </igChart:XamDataChart.Series> </igChart:XamDataChart> </Grid>
And the code behind:
public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); } } 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 = -1 }); Add(new TestDataItem() { Label = "E", Value = -2 }); Add(new TestDataItem() { Label = "F", Value = -1 }); Add(new TestDataItem() { Label = "G", Value = 1 }); Add(new TestDataItem() { Label = "H", Value = 2 }); } } public class TestDataItem { public string Label { get; set; } public double Value { get; set; } } public static class StyleBinder { public static readonly DependencyProperty StyleBinderHelperProperty = DependencyProperty.RegisterAttached( "StyleBinderHelper", typeof(StyleBinderHelper), typeof(StyleBinder), new PropertyMetadata(null, (o, e) => StyleBinderHelperChanged(o, e))); private static void StyleBinderHelperChanged( DependencyObject o, DependencyPropertyChangedEventArgs e) { FrameworkElement ele = o as FrameworkElement; if (ele == null) { return; } BindingExpression be = ele.GetBindingExpression ( StyleBinder.StyleBinderContextProperty); if (be == null) { ele.SetBinding(StyleBinderContextProperty, new Binding()); } if (e.NewValue != null) { ((StyleBinderHelper)e.NewValue).PushContent(ele); } } public static void SetStyleBinderHelper( DependencyObject target, StyleBinderHelper value) { target.SetValue(StyleBinderHelperProperty, value); } public static StyleBinderHelper GetStyleBinderHelper( DependencyObject target) { return (StyleBinderHelper)target.GetValue(StyleBinderHelperProperty); } public static readonly DependencyProperty StyleBinderContentProperty = DependencyProperty.RegisterAttached( "StyleBinderContent", typeof(FrameworkElement), typeof(StyleBinder), new PropertyMetadata(null, (o, e) => StyleBinderContentChanged(o, e))); private static void StyleBinderContentChanged( DependencyObject o, DependencyPropertyChangedEventArgs e) { } public static void SetStyleBinderContent( DependencyObject target, FrameworkElement value) { target.SetValue(StyleBinderContentProperty, value); } public static FrameworkElement GetStyleBinderContent( DependencyObject target) { return (FrameworkElement)target.GetValue(StyleBinderContentProperty); } public static readonly DependencyProperty StyleBinderContextProperty = DependencyProperty.RegisterAttached( "StyleBinderContext", typeof(object), typeof(StyleBinder), new PropertyMetadata(null, (o, e) => StyleBinderContextChanged(o, e))); private static void StyleBinderContextChanged( DependencyObject o, DependencyPropertyChangedEventArgs e) { FrameworkElement content = StyleBinder.GetStyleBinderContent(o); if (content != null) { content.DataContext = new HelperContext() { OuterContext = e.NewValue, Owner = o as FrameworkElement }; } } public static void SetStyleBinderContext( DependencyObject target, object value) { target.SetValue(StyleBinderContextProperty, value); } public static object GetStyleBinderContext( DependencyObject target) { return target.GetValue(StyleBinderContextProperty); } } public class StyleBinderHelper : FrameworkElement { internal void PushContent(FrameworkElement ele) { if (Template == null) { return; } FrameworkElement content = Template.LoadContent() as FrameworkElement; if (content == null) { return; } content.DataContext = new HelperContext() { OuterContext = StyleBinder.GetStyleBinderContext(ele), Owner = ele }; StyleBinder.SetStyleBinderContent(ele, content); } public DataTemplate Template { get; set; } } public class HelperContext : INotifyPropertyChanged { private object _outerContext; public object OuterContext { get { return _outerContext; } set { _outerContext = value; RaisePropertyChanged("OuterContext"); } } private FrameworkElement _owner; public FrameworkElement Owner { get { return _owner; } set { _owner = value; RaisePropertyChanged("Owner"); } } public event PropertyChangedEventHandler PropertyChanged; private void RaisePropertyChanged(string propertyName) { if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); } } } public class RectangleColorChanger : FrameworkElement { public static readonly DependencyProperty ValueProperty = DependencyProperty.Register( "Value", typeof(double), typeof(RectangleColorChanger), new PropertyMetadata(0.0, (o, e) => (o as RectangleColorChanger) .ValueChanged(e))); public double Value { get { return (double)GetValue(ValueProperty); } set { SetValue(ValueProperty, value); } } public static readonly DependencyProperty ThresholdProperty = DependencyProperty.Register( "Threshold", typeof(double), typeof(RectangleColorChanger), new PropertyMetadata(0.0, (o, e) => (o as RectangleColorChanger) .ThresholdChanged(e))); private void ThresholdChanged( DependencyPropertyChangedEventArgs e) { UpdateColor(); } public double Threshold { get { return (double)GetValue(ThresholdProperty); } set { SetValue(ThresholdProperty, value); } } public static readonly DependencyProperty RectangleProperty = DependencyProperty.Register( "Rectangle", typeof(Rectangle), typeof(RectangleColorChanger), new PropertyMetadata(null, (o, e) => (o as RectangleColorChanger) .RectangleChanged(e))); private void UpdateColor() { if (YAxis == null) { return; } if (Rectangle == null) { return; } Rect viewport = new Rect(0,0,YAxis.ActualWidth, YAxis.ActualHeight); double zero = YAxis.GetScaledValue(YAxis.ReferenceValue, YAxis.Chart.WindowRect, viewport); double top = Rectangle.TransformToVisual(YAxis).Transform(new Point(0, 0)).Y; double bottom = Rectangle.TransformToVisual(YAxis).Transform(new Point(0, Rectangle.Height)).Y; double val = 0; if (top < zero && bottom <= zero) { val = YAxis.GetUnscaledValue(top, YAxis.Chart.WindowRect, viewport); } else { val = YAxis.GetUnscaledValue(bottom, YAxis.Chart.WindowRect, viewport); } if (val < Threshold) { Rectangle.Fill = new SolidColorBrush(Colors.Red); } } private void RectangleChanged(DependencyPropertyChangedEventArgs e) { UpdateColor(); } public Rectangle Rectangle { get { return (Rectangle)GetValue(RectangleProperty); } set { SetValue(RectangleProperty, value); } } private void ValueChanged(DependencyPropertyChangedEventArgs e) { UpdateColor(); } public static readonly DependencyProperty YAxisProperty = DependencyProperty.Register( "YAxis", typeof(NumericYAxis), typeof(RectangleColorChanger), new PropertyMetadata(null, (o, e) => (o as RectangleColorChanger) .YAxisChanged(e))); private void YAxisChanged( DependencyPropertyChangedEventArgs e) { UpdateColor(); } public NumericYAxis YAxis { get { return (NumericYAxis)GetValue(YAxisProperty); } set { SetValue(YAxisProperty, value); } } }
Hope this helps!
Any updates on this. I believe it has something to do with changes to the way the chart is drawn in version 13.1. We are also experiencing issues with the tool tip display. It looks like the coordinates are off. If you hover over a bar you get the tool tip for the point before instead of the point you are actually on. My guess is this is actually a similar issue. If the chart size changes at all, it is not reflected the same way it was in version 12. If no answer/solution is produced soon, we will be rolling back to version 12.
This solution has worked fantastic for us. Unfortunately, we just upgraded to version 13.2 and it no longer works. Any idea on why it broke with the upgrade?
Thanks for all of the help.
This implementation is way too complicated for our requirement, specially we have other requirement where color need to be changed based on the other property value of the underlying binded view model.
We tried hacking by subscribing UpdateLayout event and using Rootcanvas from series and changing the rectangle color dynamically. Also, our usercontrol is not rendered on screen at all, we have other issues with UpdateLayout (say, formatting the Y axis scale with two decimal places etc) which don't work
Can you please provide a better resolution, either column style or finding a event which occurs after post rendering (instead of handling updateLayout event)
Thanks Graham, you have always been a immense help.
This solution would work for this case i.e. display bar in different color for negative values. I forgot to mention that we have another scenario where we want to display last column as grey. I saw you defined dependency prop as Threshold to differentiate between +ive and neg values, wondering if there is a way to figure out the last col of the column series.