Lasso selection of data points?

4 posts, 1 answers
  1. Louis
    Louis avatar
    83 posts
    Member since:
    Aug 2013

    Posted 30 Jan 2014 Link to this post

    I need to allow users to select multiple points on a ChartView chart by click-and-dragging the mouse, ideally in an arbitrary closed shape (lasso), but minimally in the rectangle formed by the positions of the mouse-down and mouse-up events. Handling the mouse events to define the area, and performing a hit-test on the points should be easy enough for the rectangular case, but I also need visual feedback on the screen showing the shape being drawn.

    Are there any examples available showing the best way to accomplish something this with a ChartView? If not, is there anything besides the mouse events I should be exploring, and what's the best approach for the visual feedback aspect?

    Thanks,
    Louis
  2. Answer
    Petar Marchev
    Admin
    Petar Marchev avatar
    968 posts

    Posted 03 Feb 2014 Link to this post

    Hi Louis,

    What you describe sounds like a drag-to-select feature. Currently we do not have this built-in and you will need to handle this manually.

    I have attached a demo project for this. In it, you will find that the mouse down, up and move events are used. A selection rectangle is shown to annotate the selection range and the IsSelected property of the data points is handled in accordance to this selection range. Hope you will find it useful. 

    Regards,
    Petar Marchev
    Telerik
    TRY TELERIK'S NEWEST PRODUCT - EQATEC APPLICATION ANALYTICS for WPF.
    Learn what features your users use (or don't use) in your application. Know your audience. Target it better. Develop wisely.
    Sign up for Free application insights >>
  3. UI for WPF is Visual Studio 2017 Ready
  4. Louis
    Louis avatar
    83 posts
    Member since:
    Aug 2013

    Posted 03 Feb 2014 in reply to Petar Marchev Link to this post

    This is excellent, thank you very much Petar!

    Louis
  5. Louis
    Louis avatar
    83 posts
    Member since:
    Aug 2013

    Posted 10 Mar 2014 in reply to Petar Marchev Link to this post

    For anyone finding this post later, I had to make some changes to the ChartUtilities to allow for panning and zooming correctly. I've included the relevant bits below for reference, as apparently I can't attach the cs files.

    Also, in my case, I wasn't able to use LayoutSlot.Center.X/Y in UpdateDataPointsInSelectionRectangle, as my points had large heights and widths for some reason. I had to change to just using LayoutSlot.X/Y.

    Louis

    private static void chart_MouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
    {
        RadCartesianChart chart = (RadCartesianChart)sender;
        Point fromPosition = e.GetPosition(chart);
        SetFromPosition(chart, fromPosition);
        SetToPosition(chart, fromPosition);
     
        Point plotPoint = ConvertWindowToPlotAreaClip(fromPosition, chart);
     
        if (!chart.PlotAreaClip.Contains(plotPoint.X, plotPoint.Y))
        {
            return;
        }
     
        chart.CaptureMouse();
     
        Canvas adorner = Telerik.Windows.Controls.ChildrenOfTypeExtensions.ChildrenOfType<Canvas>(chart).First(c => c.Name == "adornerContainer");
        Style style = GetSelectionRectangleStyle(chart);
        FrameworkElement selectionRectangle = BuildSelectionRectangle(style);
        adorner.Children.Add(selectionRectangle);
        UpdateSelectionRectanglePositionAndSize(chart);
        SetIsSelectionRectangleShown(chart, true);
    }
     
    private static void chart_MouseLeftButtonUp(object sender, System.Windows.Input.MouseButtonEventArgs e)
    {
        RadCartesianChart chart = (RadCartesianChart)sender;
        chart.ReleaseMouseCapture();
     
        Canvas adorner = Telerik.Windows.Controls.ChildrenOfTypeExtensions.ChildrenOfType<Canvas>(chart).First(c => c.Name == "adornerContainer");
        FrameworkElement selectionRectangle = Telerik.Windows.Controls.ChildrenOfTypeExtensions.ChildrenOfType<FrameworkElement>(chart).FirstOrDefault(r => object.Equals(r.Tag, SelectionRectangleTag));
        if (selectionRectangle != null)
        {
            adorner.Children.Remove(selectionRectangle);
        }
        SetIsSelectionRectangleShown(chart, false);
    }
     
    private static void chart_MouseMove(object sender, System.Windows.Input.MouseEventArgs e)
    {
        RadCartesianChart chart = (RadCartesianChart)sender;
        if (!GetIsSelectionRectangleShown(chart))
        {
            return;
        }
     
        var plotAreaClip = chart.PlotAreaClip;
     
        Point windowsPoint = ConvertPlotAreaClipToWindow(new Point(plotAreaClip.X,plotAreaClip.Y), chart);
     
        double maxX = windowsPoint.X + plotAreaClip.Width;
        double maxY = windowsPoint.Y + plotAreaClip.Height;
     
        Point toPosition = e.GetPosition(chart);
        toPosition.X = Math.Max(windowsPoint.X, toPosition.X);
        toPosition.X = Math.Min(toPosition.X, maxX);
        toPosition.Y = Math.Max(windowsPoint.Y, toPosition.Y);
        toPosition.Y = Math.Min(toPosition.Y, maxY);
        SetToPosition(chart, toPosition);
     
        UpdateSelectionRectanglePositionAndSize(chart);
        UpdateDataPointsInSelectionRectangle(chart);
    }
     
    private static void UpdateDataPointsInSelectionRectangle(RadCartesianChart chart)
    {
        Point fromPosition = ConvertWindowToPlotAreaClip(GetFromPosition(chart),chart);
        Point toPosition = ConvertWindowToPlotAreaClip(GetToPosition(chart), chart);
        Rect rect = new Rect(fromPosition, toPosition);
     
        foreach (CategoricalSeries series in chart.Series)
        {
            foreach (CategoricalDataPoint dp in series.DataPoints)
            {
                dp.IsSelected = rect.Contains(dp.LayoutSlot.Center.X, dp.LayoutSlot.Center.Y);
            }
        }
    }
     
     
    private static Point ConvertWindowToPlotAreaClip(Point from, RadCartesianChart chart)
    {
        return new Point()
        {
            X = from.X + chart.HorizontalZoomRangeStart * chart.PlotAreaClip.Width * chart.Zoom.Width,
            Y = from.Y + (1.0 - chart.VerticalZoomRangeEnd) * chart.PlotAreaClip.Height * chart.Zoom.Height
        };
    }
     
    private static Point ConvertPlotAreaClipToWindow(Point from, RadCartesianChart chart)
    {
        return new Point()
        {
            X = from.X - chart.HorizontalZoomRangeStart * chart.PlotAreaClip.Width * chart.Zoom.Width,
            Y = from.Y - (1.0 - chart.VerticalZoomRangeEnd) * chart.PlotAreaClip.Height * chart.Zoom.Height
        };
    }
Back to Top