I have a C# WPF application using MVVM with a simple RadCartesianChart that shows a single ScatterLineSeries representing a surface profile. It works well.
Now I want to enable my user to mark out certain horizontal regions of the plot for use in my application. The general idea is this
- User clicks a button that adds a visual indicator representing the region.
- The indicator appears. It looks like a big wide bar going from the bottom Y of the plot to the top Y and covering an arbitrary X width.
- The user can then use the mouse (or their finger) to made the bar wider/narrower or move it left or right across the plot
(I have attached an image showing roughly what this should look like. It is from a much older version of this application that doesn't use C#, WPF or Telerik.)
My problem is not in writing interaction/manipulation code. It is in determining exactly what Telerik entity I should interact with. That is I don't know how best to achieve this with the tools Telerik gives me.
I can think of several approaches.
- For example, I looked into using Chart Annotations. They seem perfect for marking out chart regions and your examples are good. But I can't find any examples that show me how the user might *interact* with them. How do I add a mouse/touch handler for an annotation? Is it possible? Is that the best way?
- Or maybe I should add mouse/touch handlers for the RadCartesianChart object. Then in code-behind I could try to figure out what annotation my mouse/finger is over and go from there, is that the way?
- Or should I instead try to create my own buttons, completely separate from the cahrt that overlay the whole plot and match up exactly with chart plot area. Is that the way to go?
- Is there some other, cleaner way to achieve this?
My Chart in XAML is like this:
<
tk:RadCartesianChart
x:Name
=
"Chart"
HorizontalAlignment
=
"Stretch"
VerticalAlignment
=
"Stretch"
Background
=
"{StaticResource GsBackgroundDark}"
Foreground
=
"{StaticResource GsForegroundLight}"
Height
=
"300"
HoverMode
=
"FadeOtherSeries"
SelectionPalette
=
"FlowerSelected"
ga:ChartUtilities.IsDragToSelectEnabled
=
"True"
ga:ChartUtilities.SelectionRectangleStyle
=
"{StaticResource SelectionRectangleStyle}"
>
<
tk:RadCartesianChart.Resources
>
<
Style
x:Key
=
"LabelStyle"
TargetType
=
"TextBlock"
>
<
Setter
Property
=
"Foreground"
Value
=
"{StaticResource GsForegroundLight}"
/>
</
Style
>
</
tk:RadCartesianChart.Resources
>
<
tk:RadCartesianChart.HorizontalAxis
>
<
tk:LinearAxis
Title
=
"{Binding Path=XAxisTitle}"
Foreground
=
"{StaticResource GsForegroundLight}"
LabelStyle
=
"{StaticResource LabelStyle}"
LabelFormat
=
"0.00"
/>
</
tk:RadCartesianChart.HorizontalAxis
>
<
tk:RadCartesianChart.VerticalAxis
>
<
tk:LinearAxis
x:Name
=
"YAxis"
Title
=
"{Binding Path=YAxisTitle}"
Foreground
=
"{StaticResource GsForegroundLight}"
LabelStyle
=
"{StaticResource LabelStyle}"
LabelFormat
=
"0.00"
/>
</
tk:RadCartesianChart.VerticalAxis
>
<
tk:RadCartesianChart.AnnotationsProvider
>
<
tk:ChartAnnotationsProvider
Source
=
"{Binding Regions}"
>
<
tk:ChartAnnotationDescriptor
d:DataContext
=
"{d:DesignInstance gavm:ProfileRegionVm}"
>
<
tk:ChartAnnotationDescriptor.Style
>
<
Style
TargetType
=
"tk:CartesianMarkedZoneAnnotation"
>
<
Setter
Property
=
"VerticalFrom"
Value
=
"-1000"
/>
<
Setter
Property
=
"VerticalTo"
Value
=
"1000"
/>
<
Setter
Property
=
"HorizontalFrom"
Value
=
"{Binding HorizontalFrom}"
/>
<
Setter
Property
=
"HorizontalTo"
Value
=
"{Binding HorizontalTo}"
/>
<
Setter
Property
=
"Stroke"
Value
=
"Yellow"
/>
</
Style
>
</
tk:ChartAnnotationDescriptor.Style
>
</
tk:ChartAnnotationDescriptor
>
</
tk:ChartAnnotationsProvider
>
</
tk:RadCartesianChart.AnnotationsProvider
>
<
tk:RadCartesianChart.Series
>
<
tk:ScatterLineSeries
ItemsSource
=
"{Binding Path=ProfilePointsConverted}"
XValueBinding
=
"X"
YValueBinding
=
"Y"
>
<
tk:ScatterLineSeries.LegendSettings
>
<
tk:SeriesLegendSettings
Title
=
"Profile"
/>
</
tk:ScatterLineSeries.LegendSettings
>
</
tk:ScatterLineSeries
>
</
tk:RadCartesianChart.Series
>
</
tk:RadCartesianChart
>
My ProfileRegionVm class -- which represents an instance of this horizontal reagion is like this:
using
System.Collections.Generic;
using
GApp.Core.ViewModels;
using
SWPoint = System.Windows.Point;
namespace
GApp.Analyze.ViewModels
{
public
class
ProfileRegionVm : BaseVm
{
public
ProfileRegionVm()
{
}
private
List<SWPoint> _profilePoints;
private
List<SWPoint> Points
{
get
=> _profilePoints;
set
=> SetProperty(
ref
_profilePoints, value);
}
private
Sdk.Line _line;
private
Sdk.Line Line
{
get
=> _line;
set
=> SetProperty(
ref
_line, value);
}
private
string
_name;
public
string
Name
{
get
=> _name;
set
=> SetProperty(
ref
_name, value);
}
private
double
_verticalFrom;
public
double
VerticalFrom
{
get
=> _verticalFrom;
set
=> SetProperty(
ref
_verticalFrom, value);
}
private
double
_verticalTo;
public
double
VerticalTo
{
get
=> _verticalTo;
set
=> SetProperty(
ref
_verticalTo, value);
}
private
double
_horizontalFrom;
public
double
HorizontalFrom
{
get
=> _horizontalFrom;
set
=> SetProperty(
ref
_horizontalFrom, value);
}
private
double
_horizontalTo;
public
double
HorizontalTo
{
get
=> _horizontalTo;
set
=> SetProperty(
ref
_horizontalTo, value);
}
}
}