Search code examples
wpfchartstoolkit

Allow a user to create a line series on a wpf chart by clicking on the chart


I have a WPF chart currently displaying an Area series. I need to allow a user the ability to click on the chart and add a new point for a Line series. The problem I'm having is I can't seem to find a way to convert the point from a MouseButtonEventArgs to a LineDataPoint.

private void chtPowerFlowMap_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
    {
        Point p = e.GetPosition(chtPowerFlowMap);

        points.Add(p); //Issue here is this will return a point in screen coordinates and not in chart coordinates

        ls.ItemsSource = points; //ls is an existing lineseries and points is a List<Point> object

        chtPowerFlowMap.Series.Add(ls);
    }

Thanks in advance.


Solution

  • Here is a complete solution. You can click anywhere on the chart and there will be a new point at that palce.

    MainWindows.xaml

    <chart:Chart x:Name="chart" MouseLeftButtonDown="Chart_MouseLeftButtonDown">
            <chart:LineSeries ItemsSource="{Binding}" x:Name="lineSeries"
                              DependentValuePath="Value"
                              IndependentValuePath="Date"/>
    </chart:Chart>
    

    MainWindow.xaml.cs

    public partial class MainWindow : Window
    {
        private ObservableCollection<Item> items;
    
        public MainWindow()
        {
            InitializeComponent();
            Random rd = new Random();
    
            items = new ObservableCollection<Item>(
                    Enumerable.Range(0, 10)
                    .Select(i => new Item
                    {
                        Date = DateTime.Now.AddMonths(i - 10),
                        Value = rd.Next(10,50)
                    }));
            this.DataContext = items;
        }
    
        public class Item
        {
            public DateTime Date { get; set; }
            public double Value { get; set; }
        }
    
        private void Chart_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            var p = Mouse.GetPosition(this.lineSeries);
            //ranges in the real values
            var left = items.Min(i => i.Date);
            var right = items.Max(i => i.Date);
            var top = items.Max(i => i.Value);
            var bottom = items.Min(i => i.Value);
    
            var hRange = right - left;
            var vRange = top - bottom;
    
            //ranges in the pixels
            var width = this.lineSeries.ActualWidth;
            var height = this.lineSeries.ActualHeight;
    
            //from the pixels to the real value
            var currentX = left + TimeSpan.FromTicks((long)(hRange.Ticks * p.X / width));
            var currentY = top - vRange * p.Y / height;
    
            this.items.Add(new Item { Date = currentX, Value = currentY });
        }
    }