How to add user interaction in charts

TeeChart for Microsoft Visual Studio .NET, Xamarin Studio (Android, iOS & Forms) & Monodevelop.
Post Reply
Amol
Advanced
Posts: 176
Joined: Mon May 29, 2006 12:00 am

How to add user interaction in charts

Post by Amol » Thu Jul 29, 2010 9:37 am

Hi,

We are working on a project that has very extensive use of interactive charts. There are lots of functionality in which user interacts with lines and points on graph. We are using "Tee Chart for .Net v2" and having difficulties to implement the following functionality:
1. Selecting a Line or Point and getting its object when there are a number of lines and points on the graph.
2. Dragging the selected Point Series, Point and Line on the graph using mouse.
3. Cloning selected item at some other location on the graph, which means when user presses mouse on some series or line, a copy of that object is created on the screen at the same position and then user drags the copy to some other position on the screen.
4. Mouse pointer must always be on the selected item while dragging.
5. We should be able to get the End Points in case of line and location in case of Points at any instance.
6. Rotation of line along any point on the line using mouse, i.e. user presses mouse at any point on the line and then, when user moves mouse, line rotates along a specific point on the line.
7. All the actions of dragging and rotation must be very smooth.

We have one more query that whether we can use "TeeChart for .Net v2" in a WPF application. If yes then how?

We are looking forward for your reply.

Best Regards
Yatendra

Yeray
Site Admin
Site Admin
Posts: 9614
Joined: Tue Dec 05, 2006 12:00 am
Location: Girona, Catalonia
Contact:

Re: How to add user interaction in charts

Post by Yeray » Fri Jul 30, 2010 7:24 pm

Hi Amol,
Amol wrote:1. Selecting a Line or Point and getting its object when there are a number of lines and points on the graph.
2. Dragging the selected Point Series, Point and Line on the graph using mouse.
3. Cloning selected item at some other location on the graph, which means when user presses mouse on some series or line, a copy of that object is created on the screen at the same position and then user drags the copy to some other position on the screen.
The following example I think achieves most of the demands on these 3 points.
However, using DragPoint tool, you won't be able to clone a series and select the new series directly, the "source" series is already selected so it is going to be dragged.
Furthermore, I'm not sure if you want to drag the whole series when this clone is created or only a point. If you want to drag the whole series, I'm afraid you shouldn't use the DragPoint tool and do all the work manually with Mouse events.

Code: Select all

        public Form1()
        {
            InitializeComponent();

            CreateChart();
            InitializeChart();
        }

        private Steema.TeeChart.TChart tChart1;
        private void CreateChart()
        {
            tChart1 = new Steema.TeeChart.TChart();
            this.Controls.Add(tChart1);
            tChart1.Dock = DockStyle.Fill;

            Steema.TeeChart.Commander chartController1 = new Steema.TeeChart.Commander();
            this.Controls.Add(chartController1);
            chartController1.Chart = tChart1;
            chartController1.Dock = DockStyle.Top;
        }

        private void InitializeChart()
        {
            tChart1.Aspect.View3D = false;

            for (int i = 0; i < 3; i++)
            {
                new Steema.TeeChart.Styles.Points(tChart1.Chart);
                tChart1[tChart1.Series.Count - 1].FillSampleValues(10);
                new Steema.TeeChart.Styles.Line(tChart1.Chart);
                tChart1[tChart1.Series.Count - 1].FillSampleValues(10);
                ((Steema.TeeChart.Styles.Line)(tChart1[tChart1.Series.Count - 1])).Pointer.Visible = true;
            }

            tChart1.MouseDown += new MouseEventHandler(tChart1_MouseDown);

            new Steema.TeeChart.Tools.DragPoint(tChart1.Chart);
        }

        void tChart1_MouseDown(object sender, MouseEventArgs e)
        {
            Rectangle rect = tChart1.Chart.ChartRect;
            if (rect.Contains(e.X, e.Y))
            {
                int ValueIndex;

                for (int SeriesIndex = 0; SeriesIndex < tChart1.Series.Count; SeriesIndex++)
                {
                    ValueIndex = tChart1[SeriesIndex].Clicked(e.X, e.Y);
                    if (ValueIndex != -1)
                    {
                        tChart1.Header.Text = "SeriesIndex: " + SeriesIndex + ", ValueIndex: " + ValueIndex;
                        Keys modKey = Control.ModifierKeys;
                        if (modKey == Keys.Control)
                        {
                            Steema.TeeChart.Styles.CustomPoint clone = ((Steema.TeeChart.Styles.CustomPoint)tChart1[SeriesIndex].Clone());
                            clone.Pointer.Visible = true;
                        }
                    }
                }
            }
        }
Amol wrote:4. Mouse pointer must always be on the selected item while dragging.
I'm not sure to understand it. If the code above isn't doing it, could you please give me more details?
Amol wrote:5. We should be able to get the End Points in case of line and location in case of Points at any instance.
I'm not sure to understand the either. You can get the last point of a series with tChart1[SeriesIndex].YValue[tChart1[SeriesIndex].count-1] and the same for the XValue.
Amol wrote:6. Rotation of line along any point on the line using mouse, i.e. user presses mouse at any point on the line and then, when user moves mouse, line rotates along a specific point on the line.
The series are drawn respectively to their axes, and all the axes are all in the same panel. The panel can be rotated (you can use the rotate tool to do that) but then all the series will rotate at the same time.
If you want to rotate a single series, tell us how exactly, and we will add the feature request in the wish list to be implemented in future releases.
Amol wrote:7. All the actions of dragging and rotation must be very smooth.
Feel free to check the demo above and tell us if it isn't smooth enough. Of course, as much series or points you'll have in your chart, slower will be the repaint process.
Amol wrote:We have one more query that whether we can use "TeeChart for .Net v2" in a WPF application. If yes then how?
I'm afraid that TeeChart.WPF.dll was introduced in TeeChart for .NET v3 so you should upgrade it.
Best Regards,
ImageYeray Alonso
Development & Support
Steema Software
Av. Montilivi 33, 17003 Girona, Catalonia (SP)
Image Image Image Image Image Image Please read our Bug Fixing Policy

Amol
Advanced
Posts: 176
Joined: Mon May 29, 2006 12:00 am

Re: How to add user interaction in charts

Post by Amol » Tue Aug 03, 2010 9:39 am

Hi Yeray,
Thank You very much for your reply. Your suggestions are valuable.

1. You provided the code to drag a single point of series and told that dragging of a series have to be done manually using mouse events. It will be very helpful if you can provide the code for that.

2. As you said that there is no feature for rotating a single series in TeeChart currently. I am wondering whether it can be manually done with mouse events. If yes then, would you please provide a little code snippet to achieve that too.

I have downloaded “TeeChart for .Net v4 evaluation”. I am failing to add the TeeChart control in the Toolbox of Visual Studio 2008, so that I can drag and drop it to a WPF window. I also tried to add TeeChart control to WPF window manually (through xaml as well as C#) but it is not displayed on the window when I run it. Please suggest some way to do that.
For your information I did not forget to add the required assembly to my project.
Thank you once again.
Waiting for your reply….

Yeray
Site Admin
Site Admin
Posts: 9614
Joined: Tue Dec 05, 2006 12:00 am
Location: Girona, Catalonia
Contact:

Re: How to add user interaction in charts

Post by Yeray » Wed Aug 04, 2010 1:54 pm

Hi Amol,
Amol wrote:1. You provided the code to drag a single point of series and told that dragging of a series have to be done manually using mouse events. It will be very helpful if you can provide the code for that.
Here it is an example:

Code: Select all

        public Window1()
        {
            InitializeComponent();
            InitializeChart();
        }

        private Point MouseDwn;
        private int draggingSeries;
        private void InitializeChart()
        {
            tChart1.Aspect.View3D = false;

            for (int i = 0; i < 4; i++)
            {
                new Steema.TeeChart.WPF.Styles.FastLine(tChart1.Chart);
                tChart1[i].FillSampleValues();
            }

            MouseDwn = new Point(-1, -1);
            draggingSeries = -1;

            tChart1.Zoom.Allow = false;

            tChart1.MouseDown += new MouseButtonEventHandler(tChart1_MouseDown);
            tChart1.MouseMove += new MouseEventHandler(tChart1_MouseMove);
            tChart1.MouseUp += new MouseButtonEventHandler(tChart1_MouseUp);
        }

        
        void tChart1_MouseDown(object sender, MouseButtonEventArgs e)
        {
            MouseDwn = e.GetPosition(e.MouseDevice.Target);
            
            int tmp;
            for (int i = 0; i < tChart1.Series.Count; i++)
			{
                tmp = tChart1[i].Clicked(MouseDwn);
                if (tmp != -1)
                {
                    draggingSeries = i;
                    break;
                }
			}
        }

        void tChart1_MouseMove(object sender, MouseEventArgs e)
        {
            Point ActualMouse = e.GetPosition(e.MouseDevice.Target);
            if (draggingSeries != -1)
            {
                if ((ActualMouse.X != MouseDwn.X) || (ActualMouse.Y != MouseDwn.Y))
                {
                    double diffX = tChart1[draggingSeries].XScreenToValue(MouseDwn.X) - tChart1[draggingSeries].XScreenToValue(ActualMouse.X);
                    double diffY = tChart1[draggingSeries].YScreenToValue(MouseDwn.Y) - tChart1[draggingSeries].YScreenToValue(ActualMouse.Y);
                    for (int i = 0; i < tChart1[draggingSeries].Count; i++)
                    {
                        tChart1[draggingSeries].XValues[i] = tChart1[draggingSeries].XValues[i] - diffX;
                        tChart1[draggingSeries].YValues[i] = tChart1[draggingSeries].YValues[i] - diffY;
                    }
                }
                tChart1.Invalidate();
                MouseDwn = ActualMouse;
            }
        }

        void tChart1_MouseUp(object sender, MouseButtonEventArgs e)
        {
            MouseDwn = new Point(-1, -1);
            draggingSeries = -1;
        }
Amol wrote:2. As you said that there is no feature for rotating a single series in TeeChart currently. I am wondering whether it can be manually done with mouse events. If yes then, would you please provide a little code snippet to achieve that too.
I'm not sure on what exact "rotation axis" would you like to use to rotate the selected series.
Anyway, taking the example above, where I modified all the points for the selected series with the same diffX and diffY values, the rotation you want to do will probably have to calculate a different diffX and diffY for each point in the series.
Amol wrote:I am failing to add the TeeChart control in the Toolbox of Visual Studio 2008, so that I can drag and drop it to a WPF window. I also tried to add TeeChart control to WPF window manually (through xaml as well as C#) but it is not displayed on the window when I run it. Please suggest some way to do that.
I'm afraid TeeChart.WPF.dll doesn't support designtime edition. You can't drop it from the toolbox but you can edit your .xaml as follows:

Code: Select all

<Window x:Class="WpfNetTest.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Window1" Height="358" Width="707"
    xmlns:teechart="clr-namespace:Steema.TeeChart.WPF;assembly=TeeChart.WPF">
    <Grid>
        <teechart:TChart x:Name="tChart1"/>
    </Grid>
</Window>
Best Regards,
ImageYeray Alonso
Development & Support
Steema Software
Av. Montilivi 33, 17003 Girona, Catalonia (SP)
Image Image Image Image Image Image Please read our Bug Fixing Policy

Amol
Advanced
Posts: 176
Joined: Mon May 29, 2006 12:00 am

Re: How to add user interaction in charts

Post by Amol » Thu Aug 05, 2010 11:41 am

Hi Yeray,
Thank you for the suggestions.
But I still see that there are few issues that are not resolved yet.

1. As I told you that I am using “TeeChart for .Net v4 evaluation”. I do not see any assembly named “TeeChart.Wpf.dll” in that package. Which version of TeeChart did you use for the xaml example you provided? I am still failing to add chart to the window while using version4. I am using following xaml for adding TeeChart to window-

Code: Select all

<Window x:Class="TChart2010InWPF.Window2"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:t="clr-namespace:Steema.TeeChart;assembly=TeeChart"
    Title="Window2" Height="300" Width="300">
	<Grid>
    		<t:TChart x:Name="tChart1">
	</Grid>
 </Window>

2. The rotation I required on series is exactly as I am explaining here. There will be exactly two visible pointers on the line. User can drag any of these points, anywhere on the line. To rotate the line user presses the mouse on either of these two points and then when he moves the mouse the line rotates along the other point.

3. So kind of you for providing the code to drag the series but my requirement is slightly different. You are trying to keep the whole line in the view while dragging and changing the properties of grid lines and axes. What I need is that, do not change the maximum and minimum of axes, just crop the portion of the line that goes beyond limits of axes that means we will be seeing only a portion of line if it goes beyond the axes limits and if it is dragged back in the view the whole line will again be visible.

Best Regards.
Waiting for your reply….

Yeray
Site Admin
Site Admin
Posts: 9614
Joined: Tue Dec 05, 2006 12:00 am
Location: Girona, Catalonia
Contact:

Re: How to add user interaction in charts

Post by Yeray » Fri Aug 06, 2010 3:13 pm

Hi Amol,
Amol wrote:1. As I told you that I am using “TeeChart for .Net v4 evaluation”. I do not see any assembly named “TeeChart.Wpf.dll” in that package. Which version of TeeChart did you use for the xaml example you provided?
The installers that include the TeeChart.WPF.dll are the TeeChart for .NET v3 and up for Visual Studio .NET 2008 and up. Please check that you haven't installed the TeeChart .NET v4 for Visual Studio 2005 or 2003.
The dll should be in the installation folder, by default TeeChart for .NET 2010 EVAL or TeeChart for .NET 2010
Amol wrote:2. The rotation I required on series is exactly as I am explaining here. There will be exactly two visible pointers on the line. User can drag any of these points, anywhere on the line. To rotate the line user presses the mouse on either of these two points and then when he moves the mouse the line rotates along the other point.
Then, you should do something similar to what I tried to explain. Knowing the center of rotation point and the mouse position at MouseDown, you can calculate the initial angle. You should also calculate the same angle for the "actual" mouse position at OnMouseMove and this will give you the angle of rotation you have to apply to all the points. Just some trigonometry calculations.
Amol wrote:3. So kind of you for providing the code to drag the series but my requirement is slightly different. You are trying to keep the whole line in the view while dragging and changing the properties of grid lines and axes. What I need is that, do not change the maximum and minimum of axes, just crop the portion of the line that goes beyond limits of axes that means we will be seeing only a portion of line if it goes beyond the axes limits and if it is dragged back in the view the whole line will again be visible.
I haven't changed the axes properties as you can see in the code above. I just changed the lines values. What happens is that, by default, TeeChart axes are set to scale automatically to fill all the series visible.
What you could do is to force the axes not to be automatic any more, once they have been calculated the first time. So you could add the following at, for example, OnMouseDown event:

Code: Select all

            tChart1.Axes.Bottom.Automatic = false;
            tChart1.Axes.Left.Automatic = false;
Best Regards,
ImageYeray Alonso
Development & Support
Steema Software
Av. Montilivi 33, 17003 Girona, Catalonia (SP)
Image Image Image Image Image Image Please read our Bug Fixing Policy

Amol
Advanced
Posts: 176
Joined: Mon May 29, 2006 12:00 am

Re: How to add user interaction in charts

Post by Amol » Mon Aug 09, 2010 12:43 pm

Hi Yeray,
Thank you very much for your ideas and suggestions.

The code you provided for dragging of line works fine now, I am really thankful to you for that, yet there is some optimization needed to make the dragging faster, but I will try to sort it out myself.

The idea you given for the rotation of line is looking like that it will work fine and I will definitely try to implement it. But it will be so kind of you and of course very helpful for me if you can provide a little code example for that.

For your kind information I am using "TeeChart for .NET 2010 EVAL" and I rechecked the installation directory, but I could not find any assembly named"TeeChart.WPF.dll". The only relevant dll I found there is "TeeChart.dll" and I am still failing to use it with WPF.

I really appreciate your quick response on my queries.
Best Regards for you...

Yeray
Site Admin
Site Admin
Posts: 9614
Joined: Tue Dec 05, 2006 12:00 am
Location: Girona, Catalonia
Contact:

Re: How to add user interaction in charts

Post by Yeray » Wed Aug 11, 2010 11:46 am

Hi Amol,
Yeray wrote:The installers that include the TeeChart.WPF.dll are the TeeChart for .NET v3 and up for Visual Studio .NET 2008 and up
First of all, I said that TeeChart.WPF.dll is included in the installers for Visual Studio .NET 2008 and up, but there is a TeeChart.WPF.dll for Visual Studio .NET 2005 too. But note that it is given in a separate installer because its possible to build WPF projects in Visual Studio .NET 2005 installing the Visual Studio 2005 extensions

Here it is the list of the latest evaluation installers:
download.png
download.png (28.48 KiB) Viewed 10805 times
All them, by default, install the library in the "TeeChart for .NET 2010 EVAL" folder, but only the following contain the TeeChart.WPF.dll:
- TeeChartNET2010VSNET2010Eval_4.1.2010.08047.exe
- TeeChartNET2010VSNET2005WPFEval_4.1.2010.08045.exe
- TeeChartNET2010VSNET2008Eval_4.1.2010.08046.exe
Amol wrote:The idea you given for the rotation of line is looking like that it will work fine and I will definitely try to implement it. But it will be so kind of you and of course very helpful for me if you can provide a little code example for that.
Here it is:

Code: Select all

        private Point MouseDwn;
        private int draggingSeries;
        private int centerRotationPoint;

        private class CustomLine : Steema.TeeChart.WPF.Styles.Line
        {
            public int VisibleIndex1;
            public int VisibleIndex2;

            public CustomLine(Steema.TeeChart.WPF.Chart c) : base(c) 
            {
            }
        }

        private void InitializeChart()
        {
            tChart1.Aspect.View3D = false;

            for (int i = 0; i < 4; i++)
            {
                CustomLine series = new CustomLine(tChart1.Chart);
                series.FillSampleValues();
                series.Pointer.Visible = true;
                series.VisibleIndex1 = 4;
                series.VisibleIndex2 = 19;
                series.GetPointerStyle += new Steema.TeeChart.WPF.Styles.CustomPoint.GetPointerStyleEventHandler(series_GetPointerStyle);
                series.ClickPointer += new Steema.TeeChart.WPF.Styles.CustomPoint.ClickPointerEventHandler(series_ClickPointer);
            }

            MouseDwn = new Point(-1, -1);
            draggingSeries = -1;
            centerRotationPoint = -1;

            tChart1.Zoom.Allow = false;

            tChart1.MouseDown += new MouseButtonEventHandler(tChart1_MouseDown);
            tChart1.MouseMove += new MouseEventHandler(tChart1_MouseMove);
            tChart1.MouseUp += new MouseButtonEventHandler(tChart1_MouseUp);
        }

        void series_ClickPointer(Steema.TeeChart.WPF.Styles.CustomPoint series, int valueIndex, double x, double y)
        {
            CustomLine s = series as CustomLine;
            if (valueIndex == s.VisibleIndex1) centerRotationPoint = s.VisibleIndex2;
            else if (valueIndex == s.VisibleIndex2) centerRotationPoint = s.VisibleIndex1;
            else centerRotationPoint = -1;
        }

        void series_GetPointerStyle(Steema.TeeChart.WPF.Styles.CustomPoint series, Steema.TeeChart.WPF.Styles.GetPointerStyleEventArgs e)
        {
            CustomLine s = series as CustomLine;
            if ((e.ValueIndex == s.VisibleIndex1) || (e.ValueIndex == s.VisibleIndex2)) e.Style = Steema.TeeChart.WPF.Styles.PointerStyles.Circle;
            else e.Style = Steema.TeeChart.WPF.Styles.PointerStyles.Nothing;
        }

        void tChart1_MouseDown(object sender, MouseButtonEventArgs e)
        {
            tChart1.Axes.Bottom.Automatic = false;
            tChart1.Axes.Left.Automatic = false;

            MouseDwn = e.GetPosition(e.MouseDevice.Target);

            int tmp;
            for (int i = 0; i < tChart1.Series.Count; i++)
            {
                tmp = tChart1[i].Clicked(MouseDwn);
                if (tmp != -1)
                {
                    draggingSeries = i;
                    break;
                }
            }
        }

        void tChart1_MouseMove(object sender, MouseEventArgs e)
        {
            Point ActualMouse = e.GetPosition(e.MouseDevice.Target);
            if (centerRotationPoint != -1)
            {
                if ((ActualMouse.X != MouseDwn.X) || (ActualMouse.Y != MouseDwn.Y))
                {
                    double X1 = ActualMouse.X - tChart1[draggingSeries].CalcXPos(centerRotationPoint);
                    double Y1 = ActualMouse.Y - tChart1[draggingSeries].CalcYPos(centerRotationPoint);
                    double a1 = Math.Atan2(Y1, X1);// *(180.0 / Math.PI);

                    double X2 = MouseDwn.X - tChart1[draggingSeries].CalcXPos(centerRotationPoint);
                    double Y2 = MouseDwn.Y - tChart1[draggingSeries].CalcYPos(centerRotationPoint);
                    double a2 = Math.Atan2(Y2, X2);

                    for (int i = 0; i < tChart1[draggingSeries].Count; i++)
                    {
                        if (i != centerRotationPoint)
                        {
                            double diffX = tChart1[draggingSeries].CalcXPos(i) - tChart1[draggingSeries].CalcXPos(centerRotationPoint);
                            double diffY = tChart1[draggingSeries].CalcYPos(i) - tChart1[draggingSeries].CalcYPos(centerRotationPoint);
                            double mod = Math.Sqrt(Math.Pow(diffX, 2) + Math.Pow(diffY, 2));

                            double a3 = Math.Atan2(diffY, diffX);
                            double diffA = a3  - a2 + a1;
                            tChart1[draggingSeries].XValues[i] = tChart1[draggingSeries].XScreenToValue(tChart1[draggingSeries].CalcXPos(centerRotationPoint) + Math.Cos(diffA) * mod);
                            tChart1[draggingSeries].YValues[i] = tChart1[draggingSeries].YScreenToValue(tChart1[draggingSeries].CalcYPos(centerRotationPoint) + Math.Sin(diffA) * mod);
                        }
                    }
                    tChart1.Invalidate();
                    MouseDwn = ActualMouse;
                }
            }
            else
            {
                if (draggingSeries != -1)
                {
                    if ((ActualMouse.X != MouseDwn.X) || (ActualMouse.Y != MouseDwn.Y))
                    {
                        double diffX = tChart1[draggingSeries].XScreenToValue(MouseDwn.X) - tChart1[draggingSeries].XScreenToValue(ActualMouse.X);
                        double diffY = tChart1[draggingSeries].YScreenToValue(MouseDwn.Y) - tChart1[draggingSeries].YScreenToValue(ActualMouse.Y);
                        for (int i = 0; i < tChart1[draggingSeries].Count; i++)
                        {
                            tChart1[draggingSeries].XValues[i] = tChart1[draggingSeries].XValues[i] - diffX;
                            tChart1[draggingSeries].YValues[i] = tChart1[draggingSeries].YValues[i] - diffY;
                        }
                        tChart1.Invalidate();
                        MouseDwn = ActualMouse;
                    }
                }
            }
        }

        void tChart1_MouseUp(object sender, MouseButtonEventArgs e)
        {
            MouseDwn = new Point(-1, -1);
            draggingSeries = -1;
            centerRotationPoint = -1;
        }
Best Regards,
ImageYeray Alonso
Development & Support
Steema Software
Av. Montilivi 33, 17003 Girona, Catalonia (SP)
Image Image Image Image Image Image Please read our Bug Fixing Policy

Post Reply