Basically I have done a code for a project which works with a shift and ctrl selection. The program consists of a chart with specified points, which the user is able to select with the different key controls in order to move them. The highlightedIndex is a list of int. I am also working with WPF and later on I have to bind this with a DataGrid, so I do not know if this is the right approach or is any easier way to get this done with the Binding from WPF.
I want to be able to select the points with Ctrl or Shift as Windows works, it should look like this.
public partial class MainWindow : Window
{
public ChartValues<ObservablePoint> Chartvalues { get; set; }
public SeriesCollection SeriesCollection { get; set; }
public CartesianMapper<ObservablePoint> Mapper { get; set; }
public Brush DangerBrush { get; set; }
private int movingPointIdx = -1; //-1: no point moving
private const int roundedNumber = 2;
private const double min_offset = 0.01;
private List<int> highlightedIndex = new List<int>();
private int lastClickedPointIdx = -1;
int firstvaluepoint = 0;
public MainWindow()
{
InitializeComponent();
Chartvalues = new ChartValues<ObservablePoint>();
InitPoints();
Mapper = Mappers.Xy<ObservablePoint>()
.X(item => item.X)
.Y(item => item.Y)
.Stroke((item, index) => highlightedIndex.Contains(index) ? DangerBrush : null);
DangerBrush = new SolidColorBrush(Color.FromRgb(253, 23, 23));
var lineSeries = new LineSeries
{
Title = "Line 1",
Values = Chartvalues,
StrokeThickness = 4,
Fill = Brushes.Transparent,
PointGeometrySize = 15,
LineSmoothness = 0.2,
Configuration = Mapper,
DataLabels = false,
};
SeriesCollection = new SeriesCollection { lineSeries };
DataContext = this;
}
private void InitPoints() //plot the values of the chart
{
Chartvalues.Clear();
double[] xvals = { 0, 2, 3, 4, 5, 6, 7, 8, 9 };
double[] yvals = { 0, 1, 2, 3, 1, 2, 3, 1, 2 };
for (int i = 0; i < xvals.Length; i++)
{
Chartvalues.Add(new ObservablePoint { X = xvals[i], Y = yvals[i] });
}
}
private void ChartOnDataClick(object sender, ChartPoint p)
{
Chartvalues = (ChartValues<ObservablePoint>)SeriesCollection[0].Values;
foreach (ObservablePoint val in Chartvalues)
{
if (val.X == p.X && val.Y == p.Y) //this is the clicked point
{
movingPointIdx = Chartvalues.IndexOf(val); //get index of currently clicked point
Chartvalues[movingPointIdx].X = Chartvalues[movingPointIdx].X; //highlights selected point
KeyPressed();
}
}
}
private void KeyPressed()
{
bool shiftclick = false;
int firstselectedpoint = 0;
if (Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl)) //Ctrl
{
if (highlightedIndex.Contains(movingPointIdx)) //remove point if already is in the highlightedIndex list
{
highlightedIndex.Remove(movingPointIdx);
firstselectedpoint = movingPointIdx;
shiftclick = false;
return;
}
else //otherwise add it to the highlightedIndex list
{
highlightedIndex.Add(movingPointIdx);
highlightedIndex.Sort();
firstselectedpoint = movingPointIdx;
shiftclick = false;
}
}
else if (Keyboard.IsKeyDown(Key.LeftShift) || Keyboard.IsKeyDown(Key.RightShift)) //Shift
{
if (!shiftclick)
{
highlightedIndex.Clear();
shiftclick = true;
}
if (lastClickedPointIdx > -1) //highlights all points
{
if (firstvaluepoint > firstselectedpoint)
{
for (int i = firstselectedpoint; i <= firstvaluepoint; i++)
{
highlightedIndex.Add(i);
}
}
else
{
for (int i = firstvaluepoint; i <= firstselectedpoint; i++)
{
highlightedIndex.Add(i);
}
}
}
}
else //no modifier key pressed -> only add the current point to the list
{
highlightedIndex.Clear();
highlightedIndex.Add(movingPointIdx);
}
lastClickedPointIdx = movingPointIdx;
if (!shiftclick)
firstvaluepoint = firstselectedpoint;
}
private void ChartOnMouseMove(object sender, MouseEventArgs e)
{
if (e.LeftButton != MouseButtonState.Pressed)
return;
if (movingPointIdx == -1)
return;
var newPoint = diagram.ConvertToChartValues(e.GetPosition(diagram));
Chartvalues[movingPointIdx].X = Math.Round(newPoint.X, roundedNumber);
Chartvalues[movingPointIdx].Y = Math.Round(newPoint.Y, roundedNumber);
CheckForOnePointBoundaries(Chartvalues[movingPointIdx].X);
}
private void CheckForOnePointBoundaries(double xBoundary)//checks if the point has reacher his boundary (next or last point)
{
double leftBoundary = 0;
double rightBoundary = Chartvalues[Chartvalues.Count - 1].X;
if (movingPointIdx > 0)
leftBoundary = Chartvalues[movingPointIdx - 1].X + min_offset;
if (movingPointIdx < Chartvalues.Count - 1)
rightBoundary = Chartvalues[movingPointIdx + 1].X - min_offset;
if (xBoundary < leftBoundary)
{
Chartvalues[movingPointIdx].X = leftBoundary;
}
else if (xBoundary > rightBoundary)
{
Chartvalues[movingPointIdx].X = rightBoundary;
}
}
private void ChartOnMouseLeftButtonUp(object sender, MouseButtonEventArgs e) => movingPointIdx = -1; // deactivate point moving
private void AddPoints(object sender, MouseButtonEventArgs e)
{
if (e.ChangedButton == MouseButton.Left)
{
var newPoint = diagram.ConvertToChartValues(e.GetPosition(diagram));
var addedPoint = new ObservablePoint(Math.Round(newPoint.X, roundedNumber), Math.Round(newPoint.Y, roundedNumber));
Chartvalues.Add(addedPoint);
SortChartValues();
highlightedIndex.Clear();
for (int i = 0; i < Chartvalues.Count; i++)
{
if (addedPoint.X == Chartvalues[i].X) // adds the clicked point to the list
{
highlightedIndex.Add(i);
i = Chartvalues.Count;
}
}
}
}
private void SortChartValues()
{
List<Point> list = new List<Point>();
foreach (ObservablePoint val in Chartvalues)
{
list.Add(new Point(val.X, val.Y));
}
list = list.OrderBy(p => p.X).ToList();
Chartvalues.Clear();
for (int i = 0; i < list.Count; i++)
{
Chartvalues.Add(new ObservablePoint { X = list[i].X, Y = list[i].Y });
}
}
private void RemovePoints(object sender, RoutedEventArgs e)
{
if (movingPointIdx == -1)
return;
if (Chartvalues.Count > 1)
{
Chartvalues.RemoveAt(movingPointIdx);
highlightedIndex.RemoveAt(movingPointIdx);
movingPointIdx = -1;
}
}
}
XAML
<Grid>
<lvc:CartesianChart Margin="10,35,27,5" x:Name="diagram" LegendLocation="Top" Series="{Binding SeriesCollection}" DataClick="ChartOnDataClick" DisableAnimations="True"/>
</Grid>

highlightedIndexdefined? It looks like this is all a method part of a class. Please share the rest of the class as well, preferably with a small usage example. \$\endgroup\$