oxyplot / oxyplot

A cross-platform plotting library for .NET
https://oxyplot.github.io/
MIT License
3.3k stars 963 forks source link

Touch events for Android and iOS #688

Closed sridharsaminathan closed 8 years ago

sridharsaminathan commented 8 years ago

i am using OxyPlot in Xamarin Android and Xamarin iOS i have to show the plotted point in Graph and i need to write click for the marked point. i can find MouseUp/MouseDown (MouseHover) delegate, for Andorid and iOS we need click or Touch event, please help to solve the issue. Thanks in advance.

objorke commented 8 years ago

I think this can be done in a custom plot controller! There should already be touch handlers there. We have not yet decided if events on the plot elements should be kept or removed.

sridharsaminathan commented 8 years ago

On clicking the series points i need to Show the X and Y values in the graph. Is that possible?

TheAlmightyBob commented 8 years ago

So this is too late to be helpful, but this sounds like a duplicate of #340?

iOS can be worked around by adding your own TapGestureRecognizer to the PlotView. I don't know about Android.

CharlinAgramonte commented 8 years ago

@TheAlmightyBob i have exactly the same problem, if you add a Gesture Recogniser how can you know the point selected?

hackzai commented 8 years ago

@TheAlmightyBob , @CharlinAgramonte I do have the same issue on 1.0.0-unstable 1983 version .

TheAlmightyBob commented 8 years ago

It's been a while since I did this and I don't have the code in front of me, but some things to look at:

hackzai commented 8 years ago

I just resolved it today morning by implement the long tapped gesture in my custom PlotViewRenderer in iOS, and execute the bind ICommand instance for the rest of the logic to get the selected column series. Everything just work like charm.

pattisapu01 commented 8 years ago

@hackzai, I have the same issue here. I am trying to follow what you said regarding binding to Icommand and getting the selected series from the PlotViewRenderer. Can you post some code please? Thx!

hackzai commented 8 years ago

@teeboy75,

Below are the sample code I extract from my project and hope it help.

Create your own Plot control that derive from OxyPlot PlotView class and implement your own LongTappedCommand and CommandParameter bindable property.

    public class Plot : OxyPlot.Xamarin.Forms.PlotView
    {
        public static readonly BindableProperty LongTappedCommandProperty =
            BindableProperty.Create(
                "LongTappedCommand",
                typeof(RelayCommand<object>),
                typeof(Plot),
                null,
                BindingMode.TwoWay);

        public static readonly BindableProperty CommandParameterProperty =
                    BindableProperty.Create(
                        "CommandParameter",
                        typeof(object),
                        typeof(Plot),
                        null,
                        BindingMode.TwoWay);

        public RelayCommand<object> LongTappedCommand
        {
            get
            {
                return (RelayCommand<object>)this.GetValue(LongTappedCommandProperty);
            }

            set
            {
                this.SetValue(LongTappedCommandProperty, value);
            }
        }

        public object CommandParameter
        {
            get
            {
                return (object)this.GetValue(CommandParameterProperty);
            }

            set
            {
                this.SetValue(CommandParameterProperty, value);
            }
        }
    }

Create a custom renderer which derive from OxyPlot PlotViewRenderer and implement the UILongPressGestureRecognizer

    public class PlotRenderer : PlotViewRenderer
    {
        private UILongPressGestureRecognizer longPressGestureRecognizer;

        public PlotRenderer()
        {
        }

        protected override void OnElementChanged(ElementChangedEventArgs<PlotView> e)
        {
            base.OnElementChanged(e);

            if (e.OldElement != null && this.Control != null)
            {
                if (this.longPressGestureRecognizer != null)
                {
                    this.RemoveGestureRecognizer(this.longPressGestureRecognizer);
                }
            }

            if (e.NewElement != null && this.Control != null)
            {
                var plot = e.NewElement as Controls.Plot;
                if (plot == null)
                {
                    return;
                }

                this.longPressGestureRecognizer = new UILongPressGestureRecognizer(
                    (args) =>
                    {
                        if (args.State == UIGestureRecognizerState.Began)
                        {
                            var pt = this.longPressGestureRecognizer.LocationInView(plot.Model.PlotView as UIView);
                            if (!plot.Model.PlotArea.Contains(pt.X, pt.Y))
                            {
                                return;
                            }

                            plot.LongTappedCommand?.Execute(new Point(pt.X, pt.Y));
                        }
                    });

                this.AddGestureRecognizer(this.longPressGestureRecognizer);
            }
        }
    }

Last bind your LongTappedCommand handler in your view model or ur Xamarin ContentPage class.

    private Command<object> longTappedCommand;

    public Command<object> LongTappedCommand
    {
        get { return this.longTappedCommand ?? (this.longTappedCommand = new Command<object>(this.PlotLongTappedHandler)); }
    }

    private void PlotLongTappedHandler(object e)
    {
        /*
            This code only work for stacked ColumnSeries, as for other series. You will need to modified the below code.
        */

        var pt = e as Point;
        var screenPoint = new ScreenPoint(pt.X, pt.Y);
        var plot = /* Assume you can access your PlotModel from some reference at this point */
        var selectedSeries = plot?.GetSeriesFromPoint(screenPoint);
        var result = selectedSeries?.GetNearestPoint(screenPoint, false);

        if (result == null)
        {
            return;
        }

        var index = (int) result.Index;
        var selectedXAxisLabel = ((CategoryAxis)result.XAxis).ActualLabels[index];
        var selectedSeriesInfo = new List<SeriesInfo>();

        foreach (var series in plot.Series)
        {
            OxyColor color;
            if (series is ColumnSeries)
            {
                color = ((ColumnSeries)series).ActualFillColor;
                selectedSeriesInfo.Add(new SeriesInfo
                    {
                        Color = Color.FromRgba(color.R, color.G, color.B, color.A),
                        SeriesTitle = series.Title,
                        Value = ((ColumnSeries)series).Items[index].Value
                    });

                continue;
            }

            /*
                Your selected series are store in selectedSeriesInfo 
            */
        }
    }
pattisapu01 commented 8 years ago

Thank you so much @hackzai ! that worked.. I have a follow up question.. How do we make the touch point recoznized only if the user clicked on a chart point? Currently, we are getting the "closest" touch point. I want the touch point to be recognized only of the user clicks on the "data point" itself. Can we do this? Thx!

objorke commented 8 years ago

Moved to https://github.com/oxyplot/oxyplot-xamarin/issues/23