wpf-14682-controls-and-libraries-charts-suite-chart-control-tooltip-and-crosshair-cursor-crosshair-cursor.md
The crosshair cursor allows users to track series point or indicator point coordinates on the chart, and helps users to compare different graphs.
Run Demo: Tooltip and Crosshair Cursor
The crosshair cursor comprises two intersecting horizontal and vertical lines ( value line and argument line ) with the corresponding axis labels at the end of the lines. The crosshair also contains a label ( crosshair label ) that is positioned on the line intersection. This label shows the argument and values for highlighted points. Initially the crosshair cursor displays only its argument line with the crosshair label. See the following section on how to display or hide crosshair elements: Display and Hide Crosshair Elements.
The crosshair cursor is enabled initially. If you want to disable the crosshair at the chart level, set the chart’s CrosshairEnabled property to False. However, you can still enable crosshair for specific series or indicators. To do this, enable the XYSeries2D.CrosshairEnabled or Indicator.CrosshairEnabled property.
The following markup enables the crosshair cursor only for the second series:
<dxc:ChartControl CrosshairEnabled="False" >
<dxc:XYDiagram2D>
<dxc:LineSeries2D x:Name="series1"/>
<dxc:LineSeries2D x:Name="series2" CrosshairEnabled="True"/>
</dxc:XYDiagram2D>
</dxc:ChartControl>
Call the XYDiagram2D.ShowCrosshair method to show the crosshair cursor. For example, the following code demonstrates how to show the crosshair programmatically on a chart’s Mouse.MouseUp event.
private void OnChartControlMouseUp(object sender, MouseButtonEventArgs e) {
XYDiagram2D xyDiagram = chartControl.Diagram as XYDiagram2D;
if (xyDiagram == null) return;
xyDiagram.ShowCrosshair(e.GetPosition(chartControl));
}
Private Sub OnChartControlMouseUp(ByVal sender As Object, ByVal e As MouseButtonEventArgs)
Dim xyDiagram As XYDiagram2D = TryCast(chartControl.Diagram, XYDiagram2D)
If xyDiagram Is Nothing Then
Return
End If
xyDiagram.ShowCrosshair(e.GetPosition(chartControl))
End Sub
Pass the Point.Empty value to the XYDiagram2D.ShowCrosshair(Point) method to hide the crosshair cursor.
The crosshair cursor’s snap mode specifies what points the crosshair cursor highlights: points with the nearest argument to the mouse cursor or the nearest value.
Nearest Argument (Default) Nearest Value
The following markup specifies the CrosshairOptions.SnapMode property to make the crosshair cursor to highlight points with similar values:
<dxc:ChartControl.CrosshairOptions>
<dxc:CrosshairOptions SnapMode="NearestValue"/>
</dxc:ChartControl.CrosshairOptions>
The crosshair cursor contains information about points that are in the chart visual range. Enable the CrosshairOptions.ShowOutOfRangePoints property to include points that are out of the chart viewport to the crosshair label.
<dxc:ChartControl.CrosshairOptions>
<dxc:CrosshairOptions ShowOutOfRangePoints="True">
</dxc:ChartControl.CrosshairOptions>
The Chart control can display the crosshair cursor with argument axis labels, value lines, and value axis labels in addition to the crosshair cursor’s label and argument line:
The following properties allow you to display and hide crosshair cursor elements:
<dxc:ChartControl>
<dxc:ChartControl.CrosshairOptions>
<dxc:CrosshairOptions ShowCrosshairLabels="True"
ShowValueLabels="True"
ShowValueLine="True"
ShowArgumentLabels="True"
ShowArgumentLine="True"/>
<!--...-->
</dxc:ChartControl>
If a chart contains multiple series, the crosshair label displays a group header that shows the common argument for multiple points. To hide the group header, set the CrosshairOptions.ShowGroupHeaders property to False. You can specify the CrosshairOptions.GroupHeaderPattern property to format the header. See the following section for more information: Format Crosshair Content. For an example on how to customize the group header appearance, refer to the following section: Specify Crosshair Label Content Appearance.
The crosshair cursor highlights points that a user hovers over. To disable this feature, set the CrosshairOptions.HighlightPoints property to False.
<dxc:ChartControl>
<dxc:ChartControl.CrosshairOptions>
<dxc:CrosshairOptions HighlightPoints="False"/>
</dxc:ChartControl.CrosshairOptions>
</dxc:ChartControl>
Format patterns allow you to manage how crosshair elements display their content. You can specify these patterns for the elements labeled in the following image:
The following code specifies patterns the crosshair cursor should apply to its elements’ text:
<dxc:ChartControl x:Name="chartControl">
<dxc:ChartControl.CrosshairOptions>
<dxc:CrosshairOptions GroupHeaderPattern="{}{A:MMMM d}"
ShowOnlyInFocusedPane="False"
ShowValueLabels="True"
ShowValueLine="True"/>
</dxc:ChartControl.CrosshairOptions>
<dxc:XYDiagram2D>
<dxc:StockSeries2D DisplayName="Stock Prices"
CrosshairLabelPattern="{}{S}:
Close: ${CV:.###}
High: ${HV:.###}">
<dxc:StockSeries2D.Indicators>
<dxc:RateOfChange DisplayName="Rate of Change"
CrosshairEnabled="True"
CrosshairLabelPattern="{}{I}:
Value: {V:F3}">
</dxc:RateOfChange>
</dxc:StockSeries2D.Indicators>
</dxc:StockSeries2D>
<dxc:XYDiagram2D.AxisY>
<dxc:AxisY2D>
<dxc:AxisY2D.CrosshairAxisLabelOptions>
<dxc:CrosshairAxisLabelOptions Pattern="{}{V:.##}"/>
</dxc:AxisY2D.CrosshairAxisLabelOptions>
</dxc:AxisY2D>
</dxc:XYDiagram2D.AxisY>
<dxc:XYDiagram2D.SecondaryAxesY>
<dxc:SecondaryAxisY2D>
<dxc:SecondaryAxisY2D.CrosshairAxisLabelOptions>
<dxc:CrosshairAxisLabelOptions Pattern="{}{V:.##}"/>
</dxc:SecondaryAxisY2D.CrosshairAxisLabelOptions>
</dxc:SecondaryAxisY2D>
</dxc:XYDiagram2D.SecondaryAxesY>
</dxc:XYDiagram2D>
</dxc:ChartControl>
Patterns can comprise plain text and placeholders with format specifiers. The following table lists available placeholders:
Show the table
| Pattern | Description |
|---|---|
| {S} | Displays the Series.DisplayName value. |
| {A} | Displays a series point argument. |
| {V} | Displays series point values. |
| Stacked series-specific placeholders | |
| {VP} | Displays series point values as percentages. |
| {G} | Displays the name of a stacked group. |
| {TV} | Displays a total group value. |
| Bubble series-specific placeholders | |
| {W} | Displays the weight. |
| Range series-specific placeholders | |
| {V1} | Displays the first value. |
| {V2} | Displays the second value. |
| {VD} | Displays the duration between the first and second data point values formatted using a common time format (e.g. HH:MM:SS for date time values and #.## for numeric values). |
| {VDTD} | Displays the duration between the first and second date-time data point values in days. |
| {VDTH} | Displays the duration between the first and second date-time data point values in hours. |
| {VDTM} | Displays the duration between the first and second date-time data point values in minutes. |
| {VDTS} | Displays the duration between the first and second date-time data point values in seconds. |
| {VDTMS} | Displays the duration between the first and second date-time data point values in milliseconds. |
| Financial series specific placeholders | |
| {OV} | Displays the open value. |
| {HV} | Displays the high value. |
| {LV} | Displays the low value. |
| {CV} | Displays the close value. |
| Indicator specific placeholders | |
| {I} | Displays an indicator name. |
| {A} | Displays an indicator point’s argument. |
| {V} | Displays a single-line indicator or MACD indicator point’s value. |
| {SV} | Displays a MACD indicator signal line’s point value. |
| {AV} | Displays the MovingAverage indicator value. |
| {LV} | Displays the MovingAverage indicator lower envelope line point’s value. |
| {UV} | Displays the Moving Average indicator upper envelope line point’s value. |
| {T} | Displays the Error Bar indicator point’s top value. |
| {B} | Displays the Error Bar indicator point’s bottom value. |
| X-axis label specific placeholders | |
| {A} | Displays a series point argument. |
| Y-axis label specific placeholders | |
| {V} | Displays series point values. |
| {VP} | Displays series point values as percentages. |
In XAML, insert empty brackets into the beginning of a pattern if it starts with a placeholder. Refer to the following page for more information: {} Escape sequence / markup extension.
When the Chart control or a series is bound to data, the pattern may contain data field values in addition to placeholders. For example, the data source contains the Discount field and the pattern may look like: {S}: {V:F2} (Discount: {Discount:P0}).
The CrosshairOptionsBase.CommonLabelPosition property allows you specify the crosshair label position. You can use one of the following objects:
CrosshairMousePosition (Default)
When this mode is used, the crosshair label follows the mouse cursor.
<dxc:ChartControl.CrosshairOptions>
<dxc:CrosshairOptions>
<dxc:CrosshairOptions.CommonLabelPosition>
<dxc:CrosshairMousePosition Offset="12, 12"/>
</dxc:CrosshairOptions.CommonLabelPosition>
</dxc:CrosshairOptions>
</dxc:ChartControl.CrosshairOptions>
CrosshairFreePosition
When this mode is used, the crosshair label is attached to a corner of a dock target object.
<dxc:ChartControl.CrosshairOptions>
<dxc:CrosshairOptions>
<dxc:CrosshairOptions.CommonLabelPosition>
<dxc:CrosshairFreePosition DockCorner="TopLeft"
DockTarget="{Binding ElementName=chart}"
Offset="12, 12"/>
</dxc:CrosshairOptions.CommonLabelPosition>
</dxc:CrosshairOptions>
</dxc:ChartControl.CrosshairOptions>
The crosshair cursor can display its content in a legend instead of the Crosshair label. The crosshair cursor uses a legend the Series.Legend or Indicator.Legend property specifies. If these properties are not defined, the crosshair cursor uses the first legend in the ChartControlBase.Legends collection.
The code below makes the crosshair cursor to display its content in a legend and configures legend settings:
<dxc:ChartControl>
<dxc:ChartControl.CrosshairOptions>
<dxc:CrosshairOptions ContentShowMode="Legend"/>
</dxc:ChartControl.CrosshairOptions>
<dxc:ChartControl.Legends>
<dxc:Legend MaxCrosshairContentWidth="50"
MaxCrosshairContentHeight="20"
Orientation="Vertical"/>
</dxc:ChartControl.Legends>
<!-- ... -->
</dxc:ChartControl>
Note
Use the XYSeries2D.CrosshairContentShowMode and Indicator.CrosshairContentShowMode properties to specify an element the crosshair cursor should use for an individual series/indicator to display related information.
The crosshair cursor can display individual labels for each series/indicator instead of a common label:
The following code demonstrates how to enable individual crosshair labels:
<dxc:ChartControl.CrosshairOptions>
<dxc:CrosshairOptions CrosshairLabelMode="ShowForEachSeries"/>
</dxc:ChartControl.CrosshairOptions>
Note
The crosshair label is automatically displayed with a beak in the CrosshairLabelMode.ShowForEachSeries and CrosshairLabelMode.ShowForNearestSeries modes.
If the chart contains multiple panes, it draws the crosshair cursor with a common label through all panes. Enable the CrosshairOptions.ShowOnlyInFocusedPane property to show the crosshair only in the pane that a user hovers over with the mouse cursor.
<dxc:ChartControl>
<dxc:ChartControl.CrosshairOptions>
<dxc:CrosshairOptions ShowOnlyInFocusedPane="True"/>
</dxc:ChartControl.CrosshairOptions>
<!-- ... -->
</dxc:ChartControl>
The crosshair cursor supports the following modes that specify how to display crosshair lines:
Auto (Default)
Crosshair lines are drawn through highlighted points.
Free
Crosshair lines intersect at the mouse pointer position.
The following code snippet makes the crosshair draw argument and value lines at the mouse cursor position:
<dxc:ChartControl.CrosshairOptions>
<dxc:CrosshairOptions LinesMode="Free"/>
</dxc:ChartControl.CrosshairOptions>
The Chart control allows you to fine-tune the appearance of crosshair cursor axis labels, lines, and popup:
The example below configures the crosshair cursor’s appearance to resemble to the one in the image above:
<dxc:ChartControl>
<dxc:ChartControl.CrosshairOptions>
<dxc:CrosshairOptions ArgumentLineBrush="Orange" ValueLineBrush="DarkGray"
ShowArgumentLabels="True" ShowValueLabels="True"
ShowValueLine="True">
<dxc:CrosshairOptions.ArgumentLineStyle>
<dxc:LineStyle Thickness="2">
<dxc:LineStyle.DashStyle>
<DashStyle Dashes="2 5"/>
</dxc:LineStyle.DashStyle>
</dxc:LineStyle>
</dxc:CrosshairOptions.ArgumentLineStyle>
<dxc:CrosshairOptions.ValueLineStyle>
<dxc:LineStyle Thickness="2">
<dxc:LineStyle.DashStyle>
<DashStyle Dashes="2 2"/>
</dxc:LineStyle.DashStyle>
</dxc:LineStyle>
</dxc:CrosshairOptions.ValueLineStyle>
<dxc:CrosshairOptions.PopupTemplate>
<DataTemplate>
<Border Background="#C0FFFFFF" BorderBrush="#C0C0C0C0"
BorderThickness="1" Padding="4">
<ItemsControl ItemsSource="{Binding PresentationData}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<ContentPresenter Content="{Binding}"
ContentTemplate="{Binding Path=CrosshairSeriesLabelTemplate}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Border>
</DataTemplate>
</dxc:CrosshairOptions.PopupTemplate>
</dxc:CrosshairOptions>
</dxc:ChartControl.CrosshairOptions>
<dxc:XYDiagram2D>
<dxc:XYDiagram2D.SeriesTemplate>
<dxc:BarSideBySideSeries2D>
<dxc:BarSideBySideSeries2D.LegendMarkerTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Grid Width="12" Height="12">
<Ellipse Stretch="Uniform" Fill="{Binding Path=MarkerBrush}"
Stroke="{Binding Path=MarkerLineBrush}" StrokeThickness="2"/>
</Grid>
<TextBlock Text="{Binding Path=Text}" Margin="4,0,0,0"
VerticalAlignment="Center" Width="55"/>
</StackPanel>
</DataTemplate>
</dxc:BarSideBySideSeries2D.LegendMarkerTemplate>
</dxc:BarSideBySideSeries2D>
</dxc:XYDiagram2D.SeriesTemplate>
<dxc:XYDiagram2D.AxisX>
<dxc:AxisX2D>
<dxc:AxisX2D.CrosshairLabelTemplate>
<DataTemplate>
<Grid>
<Border BorderThickness="1">
<Border.Background>
<SolidColorBrush Color="Lavender" />
</Border.Background>
<Label BorderThickness="1" BorderBrush="DarkGray"
FontStyle="Italic" Content="{Binding Path=Text}"
Padding="1" Foreground="Black"
FontSize="14"/>
</Border>
</Grid>
</DataTemplate>
</dxc:AxisX2D.CrosshairLabelTemplate>
</dxc:AxisX2D>
</dxc:XYDiagram2D.AxisX>
</dxc:XYDiagram2D>
</dxc:ChartControl>
Note
To specify the legend marker template for an indicator, use the Indicator.LegendMarkerTemplate property.
Use the XYSeries2D.CrosshairLabelTemplate property to configure appearance for crosshair series items and group header.
You can access the crosshair group header in the first series crosshair label template. The following code configures the crosshair label’s group header and first series item appearance:
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:ResourceUsageChart"
xmlns:dxc="http://schemas.devexpress.com/winfx/2008/xaml/charts"
x:Class="ResourceUsageChart.MainWindow"
xmlns:dxt="http://schemas.devexpress.com/winfx/2008/xaml/charts/themekeys"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<dxc:ChartControl>
<dxc:ChartControl.CrosshairOptions>
<dxc:CrosshairOptions ShowGroupHeaders="True"/>
</dxc:ChartControl.CrosshairOptions>
<dxc:ChartControl.Diagram>
<dxc:XYDiagram2D>
<dxc:LineSeries2D
x:Name="process1Memory"
ArgumentDataMember="Second"
DisplayName="Process 1"
ValueDataMember="Process1Memory">
<dxc:LineSeries2D.CrosshairLabelTemplate>
<DataTemplate>
<StackPanel Orientation="Vertical">
<TextBlock Background="LightBlue"
Text="{Binding HeaderText}"
HorizontalAlignment="Left"
VerticalAlignment="Center" Margin="0,2"
Foreground="{Binding Foreground}"
FontWeight="Bold"
FontStyle="{Binding FontStyle}"
FontSize="20"
FontStretch="{Binding FontStretch}"
FontFamily="{Binding FontFamily}"
Visibility="{Binding HeaderTextVisibility}"/>
<StackPanel Orientation="Horizontal" Margin="0,2">
<Grid Width="{DynamicResource {dxt:ChartControlThemeKey ResourceKey=LegendMarkerWidth}}"
Height="{DynamicResource {dxt:ChartControlThemeKey ResourceKey=LegendMarkerHeight}}"
VerticalAlignment="Top" Margin="0, 3, 0, 0"
Visibility="{Binding MarkerVisibility}">
<Rectangle Stretch="Uniform" Fill="Transparent"/>
<ContentPresenter Content="{Binding MarkerItem}" ContentTemplate="{Binding RelativeSource={RelativeSource Self}, Path= Content.MarkerTemplate}"/>
</Grid>
<TextBlock Grid.Column="1" Text="{Binding Text}"
VerticalAlignment="Center"
Margin="{DynamicResource {dxt:ChartControlThemeKey ResourceKey=LegendItemTextMargin}}"
Foreground="CadetBlue"
FontWeight="{Binding FontWeight}"
FontStyle="{Binding FontStyle}"
FontSize="{Binding FontSize}"
FontStretch="{Binding FontStretch}"
FontFamily="{Binding FontFamily}"
Visibility="{Binding TextVisibility}"/>
</StackPanel>
</StackPanel>
</DataTemplate>
</dxc:LineSeries2D.CrosshairLabelTemplate>
</dxc:LineSeries2D>
<dxc:LineSeries2D
x:Name="process2Memory"
DisplayName="Process 2"
ArgumentDataMember="Second"
ValueDataMember="Process2Memory"/>
<dxc:LineSeries2D
x:Name="process3Memory"
DisplayName="Process 3"
ArgumentDataMember="Second"
ValueDataMember="Process3Memory"/>
</dxc:XYDiagram2D>
</dxc:ChartControl.Diagram>
</dxc:ChartControl>
</Grid>
</Window>
You can use the ChartControl.CustomDrawCrosshair event to customize the crosshair cursor’s appearance based on series or indicator data.
The following example shows how to use the CustomDrawCrosshair event to replicate crosshair cursor appearance to resemble the one in the image above:
<dxc:ChartControl CustomDrawCrosshair="ChartControlCustomDrawCrosshair">
<dxc:ChartControl.CrosshairOptions>
<dxc:CrosshairOptions ShowValueLabels="True"
ShowValueLine="True"
ShowArgumentLabels="True"/>
</dxc:ChartControl.CrosshairOptions>
<!--...-->
</dxc:ChartControl>
private void ChartControlCustomDrawCrosshair(object sender, CustomDrawCrosshairEventArgs e) {
e.CrosshairLineElement.Brush = Brushes.DarkSlateGray;
foreach(CrosshairAxisLabelElement axisLabelElement in e.CrosshairAxisLabelElements) {
axisLabelElement.Background = Brushes.Gray;
}
if(e.CrosshairElementGroups.Count != 0) {
foreach(CrosshairElement crosshairElement in e.CrosshairElementGroups[0].CrosshairElements) {
if(crosshairElement != null) {
crosshairElement.LabelElement.Text = string.Format("${0}M", crosshairElement.SeriesPoint.Value);
if(crosshairElement.SeriesPoint.Value > 10) {
crosshairElement.AxisLabelElement.Background = Brushes.Green;
crosshairElement.LineElement.Brush = Brushes.Green;
}
else {
crosshairElement.AxisLabelElement.Background = Brushes.Orange;
crosshairElement.LineElement.Brush = Brushes.Orange;
crosshairElement.LineElement.LineStyle = new LineStyle { Thickness = 1 };
}
}
}
}
}
Private Sub ChartControlCustomDrawCrosshair(ByVal sender As Object, ByVal e As CustomDrawCrosshairEventArgs)
e.CrosshairLineElement.Brush = Brushes.DarkSlateGray
For Each axisLabelElement As CrosshairAxisLabelElement In e.CrosshairAxisLabelElements
axisLabelElement.Background = Brushes.Gray
Next
If e.CrosshairElementGroups.Count <> 0 Then
For Each crosshairElement As CrosshairElement In e.CrosshairElementGroups(0).CrosshairElements
If crosshairElement IsNot Nothing Then
crosshairElement.LabelElement.Text = String.Format("${0}M", crosshairElement.SeriesPoint.Value)
If crosshairElement.SeriesPoint.Value > 10 Then
crosshairElement.AxisLabelElement.Background = Brushes.Green
crosshairElement.LineElement.Brush = Brushes.Green
Else
crosshairElement.AxisLabelElement.Background = Brushes.Orange
crosshairElement.LineElement.Brush = Brushes.Orange
crosshairElement.LineElement.LineStyle = New LineStyle With {
.Thickness = 1
}
End If
End If
Next
End If
End Sub
Note
CrosshairElementGroup.IndicatorElements allows you to access indicator items when CrosshairOptions.ContentShowMode is Label.
Use the CustomDrawCrosshairEventArgs.CrosshairLegendElements collection to access the crosshair items when CrosshairOptions.ContentShowMode is Legend. For indicators, use CustomDrawCrosshairEventArgs.IndicatorLegendElements.