If there are multiple players on a chart is it possible to have a unified tooltip that displays the current Y value of each layer currently in contact with the vertical crosshair without having to have the cursor touching each layer individually?
You can adapt the code from this post: https://ko.infragistics.com/community/forums/f/retired-products-and-controls/39579/xamwebdatachart---crosshairs-position
to display the crosshair value for each series in the chart. The key would be to use the assigned x and y axis for each series to map from the world coordinate points to the axis values for each set of axes. Once you have resolved the values for each series, just store them somewhere that you can bind to from the tooltip. Would you like more info, or does this put you on the right path?
-Graham
With the release bits is there a more elegant solution to this or shold I still use the code from your previous post? In the post you linked to you indicated that the initial release did not have the bits to make this elegant.
Actually any example you have would be helpful. Since I am adding all series, axes, etc. dynamically from code I have difficulties getting the standard functionality to work much less something a bit more advanced like this.
Thanks!Mike
Mike,
See how something like this works out for you:
The Xaml:
<UserControl.Resources> <local:TestData x:Key="data" /> <local:TestData2 x:Key="data2" /> <DataTemplate x:Key="tooltipTemplate"> <Border BorderBrush="Gray" BorderThickness="1" CornerRadius="5" Background="White" IsHitTestVisible="False" Padding="5"> <ItemsControl ItemsSource="{Binding}"> <ItemsControl.ItemTemplate> <DataTemplate> <StackPanel Orientation="Horizontal"> <ContentPresenter Content="{Binding}" ContentTemplate="{Binding Series.LegendItemBadgeTemplate}" /> <TextBlock Text="Series: " /> <TextBlock Text="{Binding Series.Title}" /> <TextBlock Text=" " /> <TextBlock Text="Value: " /> <TextBlock Text="{Binding Item.Value}" /> </StackPanel> </DataTemplate> </ItemsControl.ItemTemplate> </ItemsControl> </Border> </DataTemplate> </UserControl.Resources> <Grid x:Name="LayoutRoot" Background="White"> <igChart:XamDataChart SeriesCursorMouseMove="XamDataChart_SeriesCursorMouseMove" CrosshairVisibility="Visible"> <local:ChartBehaviors.CursorTooltip> <local:CursorTooltipBehavior TooltipTemplate="{StaticResource tooltipTemplate}" /> </local:ChartBehaviors.CursorTooltip> <igChart:XamDataChart.Axes> <igChart:CategoryXAxis x:Name="xAxis" ItemsSource="{StaticResource data}" Label="{}{Date}" > </igChart:CategoryXAxis> <igChart:NumericYAxis x:Name="yAxis" /> </igChart:XamDataChart.Axes> <igChart:XamDataChart.Series> <igChart:ColumnSeries x:Name="columnSeries" Title="Column 1" XAxis="{Binding ElementName=xAxis}" YAxis="{Binding ElementName=yAxis}" ItemsSource="{StaticResource data}" ValueMemberPath="Value" /> <igChart:ColumnSeries x:Name="columnSeries2" Title="Column 2" XAxis="{Binding ElementName=xAxis}" YAxis="{Binding ElementName=yAxis}" ItemsSource="{StaticResource data2}" ValueMemberPath="Value" /> </igChart:XamDataChart.Series> </igChart:XamDataChart> </Grid>
The supporting code:
public class ChartBehaviors : DependencyObject { public static readonly DependencyProperty CursorTooltipProperty = DependencyProperty.RegisterAttached("CursorTooltip", typeof(CursorTooltipBehavior), typeof(ChartBehaviors), new PropertyMetadata(null, (o, e) => CursorTooltipChanged( o as XamDataChart, e.OldValue as CursorTooltipBehavior, e.NewValue as CursorTooltipBehavior))); public static CursorTooltipBehavior GetCursorTooltip( DependencyObject target) { return target.GetValue(CursorTooltipProperty) as CursorTooltipBehavior; } public static void SetCursorTooltip( DependencyObject target, CursorTooltipBehavior behavior) { target.SetValue(CursorTooltipProperty, behavior); } private static void CursorTooltipChanged( XamDataChart chart, CursorTooltipBehavior oldValue, CursorTooltipBehavior newValue) { if (chart == null) { return; } if (oldValue != null) { oldValue.OnDetach(chart); } if (newValue != null) { newValue.OnAttach(chart); } } } public class SeriesItemInfo : INotifyPropertyChanged { private Series _series; public Series Series { get { return _series; } set { _series = value; if (PropertyChanged != null) { PropertyChanged( this, new PropertyChangedEventArgs("Series")); } } } private object _item; public object Item { get { return _item; } set { _item = value; if (PropertyChanged != null) { PropertyChanged( this, new PropertyChangedEventArgs("Item")); } } } public event PropertyChangedEventHandler PropertyChanged; } public class SeriesItemInfoCollection : ObservableCollection<SeriesItemInfo> { public void UpdateSeriesItem( Series series, object item) { bool found = false; var indexes = from curr in this where curr.Series == series select this.IndexOf(curr); foreach (int index in indexes) { found = true; this[index].Item = item; } if (!found) { this.Add( new SeriesItemInfo() { Series = series, Item = item }); } } } public class CursorTooltipBehavior { private bool _isOverChart = false; private Popup _popup = new Popup(); private ContentControl _content = new ContentControl(); private Panel _container; private XamDataChart _owner = null; private DataTemplate _tooltipTemplate; private SeriesItemInfoCollection _items = new SeriesItemInfoCollection(); public DataTemplate TooltipTemplate { get { return _tooltipTemplate; } set { _tooltipTemplate = value; _content.ContentTemplate = _tooltipTemplate; } } protected bool IsOverChart { get { return _isOverChart; } set { bool last = _isOverChart; _isOverChart = value; if (_isOverChart && !last) { ShowPopup(); } if (!_isOverChart && last) { HidePopup(); } } } private void HidePopup() { _popup.IsOpen = false; } private void ShowPopup() { _popup.IsOpen = true; } public void OnAttach(XamDataChart chart) { if (_owner != null) { OnDetach(_owner); } chart.MouseLeave += Chart_MouseLeave; chart.MouseMove += Chart_MouseMove; chart.SeriesCursorMouseMove += Chart_SeriesCursorMouseMove; _popup.IsOpen = false; _popup.Child = _content; _content.ContentTemplate = TooltipTemplate; _content.Content = _items; if (chart.Parent != null && chart.Parent is Panel) { _container = chart.Parent as Panel; _container.Children.Add(_popup); } } public void OnDetach(XamDataChart chart) { if (_owner != chart) { return; } chart.MouseLeave -= Chart_MouseLeave; chart.MouseMove -= Chart_MouseMove; chart.SeriesCursorMouseMove -= Chart_SeriesCursorMouseMove; IsOverChart = false; _items.Clear(); _owner = null; } void Chart_MouseMove(object sender, MouseEventArgs e) { SetPopupOffsets(e.GetPosition(_owner)); IsOverChart = true; } private void SetPopupOffsets(Point point) { _popup.VerticalOffset = point.Y; _popup.HorizontalOffset = point.X + 10; } void Chart_SeriesCursorMouseMove( object sender, ChartCursorEventArgs e) { if (e.Series != null && e.Item != null) { _items.UpdateSeriesItem(e.Series, e.Item); } } void Chart_MouseLeave( object sender, MouseEventArgs e) { IsOverChart = false; } }
Let me know how it goes!
Hello Graham,
Although this is a old post but after spending few hours, I found this post very helpful to me. I have two lines series on my chart with CategoryDateTimeXAxis and NumericYAxis. I need to show both series values in Tooltip with Crosshair.
I used your above code, and I can see tooltip but I am afraid, Item.Value is empty in Tooltip like this.
I used the exact give code in your post. Please help.
Hi, what does the class that you are using for your data items look like? Does it have a property called Value? If your value is in a different property make sure you update the binding that reads Item.Value to read Item.YourProperty where YourProperty is whatever property contains the value of the item.
Let me know if this helps.
Thank you Graham for your comments.
I have following data item
public class ChartData : INotifyPropertyChanged { private DateTime _date; private double _value1, _value2;
public DateTime Date { get { return _date; } set { if (_date == value) return; _date = value; OnPropertyChanged("Date"); } } public double Value1 { get { return _value1; } set { if (_value1 == value) return; _value1 = value; OnPropertyChanged("Value1"); } } public double Value2 { get { return _value2; } set { if (_value2 == value) return; _value2 = value; OnPropertyChanged("Value2"); } }
Here Value1 is for first line series and Value2 is for second line series. Basically my same data item is being used for both lines series. So how can I get their values?
Thanks,
I haven't heard anthing from yourside. Please guide me how can I accomplish my requirements.
M. Irfan
The following is a modified version to support storing the values in a single item type:
<UserControl.Resources> <local:TestData x:Key="data" /> <DataTemplate x:Key="tooltipTemplate"> <Border BorderBrush="Gray" BorderThickness="1" CornerRadius="5" Background="White" IsHitTestVisible="False" Padding="5"> <ItemsControl ItemsSource="{Binding}"> <ItemsControl.ItemTemplate> <DataTemplate> <StackPanel Orientation="Horizontal"> <ContentPresenter Content="{Binding}" ContentTemplate="{Binding Series.LegendItemBadgeTemplate}" /> <TextBlock Text="Series: " /> <TextBlock Text="{Binding Series.Title}" /> <TextBlock Text=" " /> <TextBlock Text="Value: " /> <TextBlock Text="{Binding Value}" /> </StackPanel> </DataTemplate> </ItemsControl.ItemTemplate> </ItemsControl> </Border> </DataTemplate> </UserControl.Resources> <Grid x:Name="LayoutRoot" Background="White"> <igChart:XamDataChart CrosshairVisibility="Visible"> <local:ChartBehaviors.CursorTooltip> <local:CursorTooltipBehavior TooltipTemplate="{StaticResource tooltipTemplate}" /> </local:ChartBehaviors.CursorTooltip> <igChart:XamDataChart.Axes> <igChart:CategoryXAxis x:Name="xAxis" ItemsSource="{StaticResource data}" Label="{}{Date}" > </igChart:CategoryXAxis> <igChart:NumericYAxis x:Name="yAxis" MinimumValue="0" /> </igChart:XamDataChart.Axes> <igChart:XamDataChart.Series> <igChart:ColumnSeries x:Name="columnSeries" Title="Column 1" XAxis="{Binding ElementName=xAxis}" YAxis="{Binding ElementName=yAxis}" ItemsSource="{StaticResource data}" ValueMemberPath="Value1" /> <igChart:ColumnSeries x:Name="columnSeries2" Title="Column 2" XAxis="{Binding ElementName=xAxis}" YAxis="{Binding ElementName=yAxis}" ItemsSource="{StaticResource data}" ValueMemberPath="Value2" /> </igChart:XamDataChart.Series> </igChart:XamDataChart> </Grid> </UserControl>
And code behind:
public partial class MainPage : UserControl { public MainPage() { InitializeComponent(); } } public class TestData : ObservableCollection<TestDataItem> { public TestData() { Add(new TestDataItem() { Label = "A", Value1 = 1, Value2 = 4 }); Add(new TestDataItem() { Label = "B", Value1 = 2, Value2 = 3 }); Add(new TestDataItem() { Label = "C", Value1 = 3, Value2 = 2, }); Add(new TestDataItem() { Label = "D", Value1 = 4, Value2 = 1 }); } } public class TestDataItem { public string Label { get; set; } public double Value1 { get; set; } public double Value2 { get; set; } } public class ChartBehaviors : DependencyObject { public static readonly DependencyProperty CursorTooltipProperty = DependencyProperty.RegisterAttached("CursorTooltip", typeof(CursorTooltipBehavior), typeof(ChartBehaviors), new PropertyMetadata(null, (o, e) => CursorTooltipChanged( o as XamDataChart, e.OldValue as CursorTooltipBehavior, e.NewValue as CursorTooltipBehavior))); public static CursorTooltipBehavior GetCursorTooltip( DependencyObject target) { return target.GetValue(CursorTooltipProperty) as CursorTooltipBehavior; } public static void SetCursorTooltip( DependencyObject target, CursorTooltipBehavior behavior) { target.SetValue(CursorTooltipProperty, behavior); } private static void CursorTooltipChanged( XamDataChart chart, CursorTooltipBehavior oldValue, CursorTooltipBehavior newValue) { if (chart == null) { return; } if (oldValue != null) { oldValue.OnDetach(chart); } if (newValue != null) { newValue.OnAttach(chart); } } } public class SeriesItemInfo : INotifyPropertyChanged { private Series _series; public Series Series { get { return _series; } set { _series = value; if (PropertyChanged != null) { PropertyChanged( this, new PropertyChangedEventArgs("Series")); } } } private object _item; public object Item { get { return _item; } set { _item = value; if (PropertyChanged != null) { PropertyChanged( this, new PropertyChangedEventArgs("Item")); } } } private object _value; public object Value { get { return _value; } set { _value = value; if (PropertyChanged != null) { PropertyChanged( this, new PropertyChangedEventArgs("Value")); } } } public event PropertyChangedEventHandler PropertyChanged; } public class SeriesItemInfoCollection : ObservableCollection<SeriesItemInfo> { public void UpdateSeriesItem( Series series, object item) { bool found = false; var indexes = from curr in this where curr.Series == series select this.IndexOf(curr); object value = null; if (item != null && series != null) { var inf = item.GetType() .GetProperty( ((AnchoredCategorySeries)series) .ValueMemberPath); if (inf != null) { value = inf.GetValue(item, null); } } foreach (int index in indexes) { found = true; this[index].Item = item; this[index].Value = value; } if (!found) { this.Add( new SeriesItemInfo() { Series = series, Item = item, Value = value }); } } } public class CursorTooltipBehavior { private bool _isOverChart = false; private Popup _popup = new Popup(); private ContentControl _content = new ContentControl(); private Panel _container; private XamDataChart _owner = null; private DataTemplate _tooltipTemplate; private SeriesItemInfoCollection _items = new SeriesItemInfoCollection(); public DataTemplate TooltipTemplate { get { return _tooltipTemplate; } set { _tooltipTemplate = value; _content.ContentTemplate = _tooltipTemplate; } } protected bool IsOverChart { get { return _isOverChart; } set { bool last = _isOverChart; _isOverChart = value; if (_isOverChart && !last) { ShowPopup(); } if (!_isOverChart && last) { HidePopup(); } } } private void HidePopup() { _popup.IsOpen = false; } private void ShowPopup() { _popup.IsOpen = true; } public void OnAttach(XamDataChart chart) { if (_owner != null) { OnDetach(_owner); } chart.MouseLeave += Chart_MouseLeave; chart.MouseMove += Chart_MouseMove; chart.SeriesCursorMouseMove += Chart_SeriesCursorMouseMove; _popup.IsOpen = false; _popup.Child = _content; _content.ContentTemplate = TooltipTemplate; _content.Content = _items; if (chart.Parent != null && chart.Parent is Panel) { _container = chart.Parent as Panel; _container.Children.Add(_popup); } } public void OnDetach(XamDataChart chart) { if (_owner != chart) { return; } chart.MouseLeave -= Chart_MouseLeave; chart.MouseMove -= Chart_MouseMove; chart.SeriesCursorMouseMove -= Chart_SeriesCursorMouseMove; IsOverChart = false; _items.Clear(); _owner = null; } void Chart_MouseMove(object sender, MouseEventArgs e) { SetPopupOffsets(e.GetPosition(_owner)); IsOverChart = true; } private void SetPopupOffsets(Point point) { _popup.VerticalOffset = point.Y; _popup.HorizontalOffset = point.X + 10; } void Chart_SeriesCursorMouseMove( object sender, ChartCursorEventArgs e) { if (e.Series != null && e.Item != null) { _items.UpdateSeriesItem(e.Series, e.Item); } } void Chart_MouseLeave( object sender, MouseEventArgs e) { IsOverChart = false; } }
There are other ways to accomplish this, but this represents the simplest way, I believe.Hope this helps!-Graham
Hi,
The sample above is structured around there being seperate data items for all the points but with the same property name so that you can use a simple items control and item template in the tooltip. Since you have the data for each series in the same data item you would need to make some modifications in order to be able to display the data from the different series. I'll see if I can modify the sample for your case.