This is a migrated thread and some comments may be shown as answers.

ChartPanAndZoomBehavior limited to current items source

10 Answers 194 Views
Chart
This is a migrated thread and some comments may be shown as answers.
Michel
Top achievements
Rank 1
Michel asked on 15 Feb 2018, 10:06 AM

The ChartPanAndZoomBehavior is a really nice feature that lets the user zoom and pan once zoomed in. There is one disadvantage, and that is that you are limited to current items source.

I am showing data over time so when you are zoomed out you can not zoom out further then the original items source. Also you cannot pan right or left when zoomed out. I would like to be able disable this behavior and adjust the items source depending on the gesture so you can pan en zoom without limits. How can I achieve this behavior preferably in a MVVM way?

 

Best regards,

Michel Moorlag

10 Answers, 1 is accepted

Sort by
0
Michel
Top achievements
Rank 1
answered on 19 Feb 2018, 08:25 AM

Anyone any ideas?

Thinking this over I would like to be able to bind a command to a pinch event with parameters the give me zoom in or out , the "amount" or level of the pinch and maybe the direction too (horizontal vs vertical). And I would like to be able to bind a command to the pan event giving me direction of the pan en the amount of pan. That would give me control to adjust the items source depending on the users gestures.

But I guess that would be a feature request wouldn't it?

0
Petar Marchev
Telerik team
answered on 19 Feb 2018, 12:33 PM
Hello Michel,

I am not sure that you will be able to achieve these requirements with the built-in features of the chart.

At this moment the chart does not have such events as the ones you are talking about, namely zoom with the level of pinch, and pan with direction. The chart exposes a Zoom and a PanOffset properties, which are the coerced results of a touch operation. If you need access to these native touch operations, I guess you can implement custom renderers and try to intercept these touch events at your own effort and risk.

The way the chart works is that it calculates an axis Min and Max upon data binding. Then zooming only changes the zoom level and pan offset, thus changing the visible range, but never changes the Min and Max.

At this moment we do not have an exposed property for the visible range of the axis. If we implement such a property, would that help you? Could you possibly manually set the Minimum and Maximum of the axis to whatever is your actual min and max dates, and then upon VisibleRange changed notification update the items source? If you think this could work, then we can create a feature request for such a visible range property.

Regards,
Petar Marchev
Progress Telerik
Do you want to have your say when we set our development plans? Do you want to know when a feature you care about is added or when a bug fixed? Explore the Telerik Feedback Portal and vote to affect the priority of the items
0
Michel
Top achievements
Rank 1
answered on 19 Feb 2018, 01:05 PM

Hello Petar,

Thank you for you response.

If this VisibleRange property would be a two way bindable property which can be set from source but also be updated on the pinch/pan gestures of the user then I think that would be a perfect solution. Maybe even a more simple and cleaner solution then events when I think of it.

So yeah, please make the feature request.

0
Petar Marchev
Telerik team
answered on 20 Feb 2018, 12:17 PM
Hi Michel,

I have created a feature request for this in our feedback portal where you can vote and track its status. It is worth mentioning that putting a setter for this property is something that will most likely not happen. Also note that even if there was a setter for this property, you would not be able to set the VisibleRange to be larger than the actual axis range, in other words the visible range will surely be a subset of the actual range.

Regards,
Petar Marchev
Progress Telerik
Do you want to have your say when we set our development plans? Do you want to know when a feature you care about is added or when a bug fixed? Explore the Telerik Feedback Portal and vote to affect the priority of the items
0
Michel
Top achievements
Rank 1
answered on 22 Feb 2018, 08:48 AM

Hi Petar,

I can live with that as long as it can be set form the control depending on the gestures of the user so I can adjust the data set of the item source. It has my vote.

Regards,

Michel.

0
Michel
Top achievements
Rank 1
answered on 23 Feb 2018, 02:00 PM

For those looking for a solution I found a way. First I wanted to implement extra behaviors to the chart but it seems that the chart behaviors are not the same as the default X.F. view behaviors so this was a dead end. So I decided to embed the chart in my own gesturecontainer. Then I added bindable commands and properties so I determine when and how much was swiped/pinched. That I then can use to adjust the itemssource.

So this is my gesturecontainer:

    public class GestureContainer : ContentView
    {
        public static readonly BindableProperty PanCommandProperty = BindableProperty.Create("PanCommand", typeof(ICommand), typeof(GestureContainer), null);
        public static readonly BindableProperty PinchCommandProperty = BindableProperty.Create("PinchCommand", typeof(ICommand), typeof(GestureContainer), null);
        public static readonly BindableProperty HorizontalShiftProperty = BindableProperty.Create("HorizontalShift", typeof(double), typeof(GestureContainer), 0.0);
        public static readonly BindableProperty HorizontalZoomProperty = BindableProperty.Create("HorizontalZoom", typeof(double), typeof(GestureContainer), 0.0);


        public GestureContainer()
        {
            // Set PanGestureRecognizer.TouchPoints to control the
            // number of touch points needed to pan
            var panGesture = new PanGestureRecognizer();
            panGesture.PanUpdated += OnPanUpdated;
            GestureRecognizers.Add(panGesture);
            var pinchGesture = new PinchGestureRecognizer();
            pinchGesture.PinchUpdated += OnPinchUpdated;
            GestureRecognizers.Add(pinchGesture);
        }

        public void UnsubcribeContainer()
        {
            if(GestureRecognizers[0] is PanGestureRecognizer)
                (GestureRecognizers[0] as PanGestureRecognizer).PanUpdated -= OnPanUpdated;
            if (GestureRecognizers[1] is PinchGestureRecognizer)
                (GestureRecognizers[1] as PinchGestureRecognizer).PinchUpdated -= OnPinchUpdated;
        }

        void OnPanUpdated(object sender, PanUpdatedEventArgs e)
        {
            switch (e.StatusType)
            {
                case GestureStatus.Running:

                    if (PanCommand == null)
                    {
                        return;
                    }

                    if (PanCommand.CanExecute(e))
                    {
                        HorizontalShift = e.TotalX;
                        PanCommand.Execute(e);
                    }
                    break;

                case GestureStatus.Completed:

                    break;
            }
        }

        void OnPinchUpdated(object sender, PinchGestureUpdatedEventArgs e)
        {
            switch (e.Status)
            {
                case GestureStatus.Running:

                    if (PinchCommand == null)
                    {
                        return;
                    }

                    if (PinchCommand.CanExecute(e))
                    {
                        HorizontalZoom = e.Scale;
                        PinchCommand.Execute(e);
                    }
                    break;

                case GestureStatus.Completed:

                    break;
            }
        }

        public ICommand PanCommand
        {
            get { return (ICommand)GetValue(PanCommandProperty); }
            set { SetValue(PanCommandProperty, value); }
        }

        public ICommand PinchCommand
        {
            get { return (ICommand)GetValue(PinchCommandProperty); }
            set { SetValue(PinchCommandProperty, value); }
        }

        public double HorizontalShift
        {
            get { return (double)GetValue(HorizontalShiftProperty); }
            set { SetValue(HorizontalShiftProperty, value); }
        }

        public double HorizontalZoom
        {
            get { return (double)GetValue(HorizontalZoomProperty); }
            set { SetValue(HorizontalZoomProperty, value); }
        }
    }

So in my page I embedded the chart in the container like this:

 

                                <hc:GestureContainer  x:Name="gesturecontainer"
                                                      PanCommand="{Binding PanCommand}" HorizontalShift="{Binding GraphHorizontalShift, Mode=TwoWay}"
                                                      PinchCommand="{Binding PinchCommand}" HorizontalZoom="{Binding GraphHorizontalZoom, Mode=TwoWay}">
                                    <telerikChart:RadCartesianChart PaletteName="Light" SelectionPaletteName="Light" x:Name="chart" 
                                                                VerticalOptions="FillAndExpand"
                                                                BackgroundColor="Transparent"
                                                                >
                                        <telerikChart:RadCartesianChart.HorizontalAxis>
                                            <telerikChart:DateTimeContinuousAxis LabelFormat="dd-MM" GapLength="0.3" PlotMode="BetweenTicks" MajorStepUnit="Day" MajorStep="{Binding MajorStepSize}" LabelFitMode="Rotate" />
                                        </telerikChart:RadCartesianChart.HorizontalAxis>
                                        <telerikChart:RadCartesianChart.VerticalAxis>
                                            <telerikChart:NumericalAxis Minimum="0"/>
                                        </telerikChart:RadCartesianChart.VerticalAxis>
                                        <telerikChart:RadCartesianChart.Grid>
                                            <telerikChart:CartesianChartGrid MajorLinesVisibility="Y" MajorLineThickness="1" />
                                        </telerikChart:RadCartesianChart.Grid>
                                        <telerikChart:RadCartesianChart.Series>
                                            <telerikChart:AreaSeries CategoryBinding="Date" ValueBinding="Value" ItemsSource="{Binding Serie1}" DisplayName="{Binding Legend1}" />
                                            <telerikChart:LineSeries CategoryBinding="Date" ValueBinding="Value" ItemsSource="{Binding Serie2}" DisplayName="{Binding Legend2}" />
                                        </telerikChart:RadCartesianChart.Series>
                                    </telerikChart:RadCartesianChart>
                                </hc:GestureContainer>

hc Is the Helperclasses namespace of my gesturecontainer

In my viewmodel I bind to the commands to determine when there was a pan or pinch gesture and I can read the GraphHorizontalShift and GraphHorizontalZoom properties to determine how much was panned or pinched.

I am not sure If it is needed but before I leave the page I unsubcribe the pan and pinch events by overloading the OnDisappearing method in code behind of the page like this:

    protected override void OnDisappearing()
    {
            gesturecontainer.UnsubcribeContainer();
            base.OnDisappearing();            
    }

Works like a carm and the user can now swipe and zoom without limits.

0
Stefan Nenchev
Telerik team
answered on 27 Feb 2018, 08:14 AM
Hi, Michel,

Thank you for sharing the approach so that other users might use it if needed. I have added some points to your account as a token of gratitude.

Have a great rest of the week.

Regards,
Stefan Nenchev
Progress Telerik
Do you want to have your say when we set our development plans? Do you want to know when a feature you care about is added or when a bug fixed? Explore the Telerik Feedback Portal and vote to affect the priority of the items
0
Michel
Top achievements
Rank 1
answered on 06 Mar 2018, 08:33 AM

Hi Stefan,

No problem, I hope it can help others. 

The next thing is that I would like to do is set the Axis Majorstep property depending on the zoom level. I have bound the MajorStep property on a calculated value depending on the zoom level but it does never get read. It turns out that axis value can't be bound? See https://feedback.telerik.com/Project/168/Feedback/Details/211875-chart-issue-with-bindingcontext-of-chart-axes-and-grid . For full MVVM support all properties should be bindable all the way. It's almost one year ago since this bug is reported but nothing happened since then. It would be really really nice if this could be fixed soon because these kind of issues make it really hard to make full use of these very nice controls.

 

0
Petar Marchev
Telerik team
answered on 07 Mar 2018, 07:56 AM
Hi,

Your last question is not directly related to the original question about the pan-zoom behavior being limited to the current items source. We prefer to keep questions separated into different threads because this makes it much easier to follow a conversation and for people to find answers. This is why I will ask that you open a new thread about each separate question you may have. Thank you for understanding.

Regards,
Petar Marchev
Progress Telerik
Do you want to have your say when we set our development plans? Do you want to know when a feature you care about is added or when a bug fixed? Explore the Telerik Feedback Portal and vote to affect the priority of the items
0
Michel
Top achievements
Rank 1
answered on 07 Mar 2018, 07:59 AM

Hi Petar,

I understand, no problem.

Best Regards,

Michel Moorlag

Tags
Chart
Asked by
Michel
Top achievements
Rank 1
Answers by
Michel
Top achievements
Rank 1
Petar Marchev
Telerik team
Stefan Nenchev
Telerik team
Share this question
or