Mouse cursor vs CursorTool

TeeChart for Microsoft Visual Studio .NET, Xamarin Studio (Android, iOS & Forms) & Monodevelop.
johnyboy
Newbie
Newbie
Posts: 13
Joined: Mon Oct 06, 2008 12:00 am

Mouse Cursor

Post by johnyboy » Tue Nov 04, 2008 12:21 pm

Narcis,

I will have a look at your proposed workaroud.
Please notice that SetCursor() doesn't need to be called in the MouseMove event.
The overall idea here is to set different mouse cursor depending on whether the pointer is over the left, right or bottom axis or labels area or the actual charting area.
Do you have a better idea on how to achieve this without using the MouseMouve event ?

johnyboy
Newbie
Newbie
Posts: 13
Joined: Mon Oct 06, 2008 12:00 am

Mouse Cursor workaround

Post by johnyboy » Wed Nov 12, 2008 3:11 pm

Hi Narcis,

Your sample code does solve the flickering problem.

However, there is a major disadvantages to this solution: destroying/creating cursortool objects is a bad technique in the sense that in brings unnecessary memory allocation/deallocation overhead. Since the mouse cursor is a function of its position on the chart, there could be a lot of cursor changing. There will be many CursorTool as well. That will mean a lot of work for the garbage collector!

On a more general perspective: it seems like a complicated solution to a simple requirement: "control the mouse cursor depending on the chart area it is over"...

So my final conclusion is that this might be acceptable as a temporary workaround but I am not going to use this in production code.

Jean

Christopher
Site Admin
Site Admin
Posts: 1349
Joined: Thu Jan 01, 1970 12:00 am
Location: Riudellots de la Selva, Catalonia
Contact:

Re: Mouse Cursor workaround

Post by Christopher » Thu Nov 13, 2008 9:19 am

johnyboy,
johnyboy wrote: However, there is a major disadvantages to this solution: destroying/creating cursortool objects is a bad technique in the sense that in brings unnecessary memory allocation/deallocation overhead. Since the mouse cursor is a function of its position on the chart, there could be a lot of cursor changing. There will be many CursorTool as well. That will mean a lot of work for the garbage collector!
Please be aware in the code that Narcís sent a new CursorTool object is created only when the tChart.Cursor needs to be changed. New CursorTool objects are not created every time the mouse is moved over the tChart. This is not by any means the same scale of memory overhead that you may have imagined. As an aside, you might also like to bear in mind that memory allocation in .net is much faster than in native win32, as this quote from "CLR via C#" by Jeffrey Richter suggests:

"In a C-runtime heap, allocating memory for an object requires walking through a linked list of data structures. Once a large enough block is found, that block is split, and pointers in the linked-list nodes are modified to keep everything intact. For the managed heap, allocating an object simply means adding a value to a pointer - this is blazingly fast by comparison. In fact, allocating an object from the managed heap is nearly as fast as allocating memory from a thread's stack!"

You may also like to bear in mind that even if a number of CursorTool objects are created in your application due to frequent change of the tChart.Cursor, it is very unlikely that any of them will reach the GC's generation 1, again meaning a much lower memory overhead than one might at first think.
johnyboy wrote: So my final conclusion is that this might be acceptable as a temporary workaround but I am not going to use this in production code.
I disagree that this code cannot be used in production, for the reasons I stated above.
Thank you!

Christopher Ireland (Steema crew)
Please be aware of the newsgroup archives:
http://www.teechart.net/support/search.php
http://groups.google.com
http://codenewsfast.com/

johnyboy
Newbie
Newbie
Posts: 13
Joined: Mon Oct 06, 2008 12:00 am

Re: Mouse Cursor workaround

Post by johnyboy » Thu Nov 13, 2008 12:19 pm

Christopher,
I disagree that this code cannot be used in production, for the reasons I stated above.
That is your opinion. Here is mine:

this may be a good workaround, but it is a weak solution at best. and although I agree with you that the GC will do a good job, the overhead imposed by this solution can -and should- be avoided. It is nonsense that just moving the mouse around in some chart area (thus forcing cursor changes) will hit the cpu in a noticeable way.

I repeat that the original requirement is simple: "set the mouse cursor according to its position".

Christopher
Site Admin
Site Admin
Posts: 1349
Joined: Thu Jan 01, 1970 12:00 am
Location: Riudellots de la Selva, Catalonia
Contact:

Re: Mouse Cursor workaround

Post by Christopher » Thu Nov 13, 2008 2:45 pm

johnyboy,
johnyboy wrote:this may be a good workaround, but it is a weak solution at best. and although I agree with you that the GC will do a good job, the overhead imposed by this solution can -and should- be avoided. It is nonsense that just moving the mouse around in some chart area (thus forcing cursor changes) will hit the cpu in a noticeable way.
Have a look at what the .net framework does. Every time you do something like this:

Code: Select all

System.Windows.Forms.Panel myPanel = new System.Windows.Forms.Panel();
myPanel.Cursor = Cursors.HSplit;
this is what Cursors.HSplit is doing:

Code: Select all

public static Cursor get_HSplit()
{
    if (hSplit == null)
    {
        hSplit = new Cursor("hsplit.cur", 0);
    }
    return hSplit;
}
So, following this "native .net" way of working, we could do the following:

Code: Select all

  public partial class Form3 : Form
  {
    private Steema.TeeChart.Styles.FastLine fastLine1;
    private Steema.TeeChart.Tools.CursorTool cursorTool1;

    private int CurrentCursor = 0;
    public Form3()
    {
      InitializeComponent();
      tChart1.Series.Add(fastLine1 = new FastLine());
      fastLine1.FillSampleValues(1000);
    }

    private void SetCursor()
    {
      if (cursorTool1 != null)
      {
        tmpX = cursorTool1.XValue;
        tmpY = cursorTool1.YValue;
        tChart1.Tools.Remove(cursorTool1);
      }

      switch (CurrentCursor)
      {
        case 0:
          tChart1.Cursor = Cursors.Arrow;
          cursorTool1 = TeeCursors.TeeArrow;
          tChart1.Tools.Add(cursorTool1);
          break;
        case 1:
          tChart1.Cursor = Cursors.Cross;
          cursorTool1 = TeeCursors.TeeCross;
          tChart1.Tools.Add(cursorTool1);
          break;
        case 2:
          tChart1.Cursor = Cursors.Hand;
          cursorTool1 = TeeCursors.TeeHand;
          tChart1.Tools.Add(cursorTool1);
          break;
        case 3:
          tChart1.Cursor = Cursors.No;
          cursorTool1 = TeeCursors.TeeNo;
          tChart1.Tools.Add(cursorTool1);
          break;
      }
     
      cursorTool1.Series = fastLine1;
      cursorTool1.Pen.Color = Color.Yellow;
      cursorTool1.YValue = tmpY;
      cursorTool1.XValue = tmpX;
      cursorTool1.YValue = tmpY;
      cursorTool1.Active = checkBox1.Checked;
    }

    private double tmpX, tmpY;

    private void button1_Click(object sender, EventArgs e)
    {
      CurrentCursor = ++CurrentCursor % 4;
      SetCursor();
    }

    private void checkBox1_CheckedChanged(object sender, EventArgs e)
    {
      SetCursor();
      cursorTool1.Active = checkBox1.Checked;
    }
  }

  public static class TeeCursors
  {
    private static CursorTool _teeArrow;
    public static CursorTool TeeArrow
    {
      get
      {
        if (_teeArrow == null)
        {
          _teeArrow = new CursorTool();
        }
        return _teeArrow;
      }
    }

    private static CursorTool _teeCross;
    public static CursorTool TeeCross
    {
      get
      {
        if (_teeCross == null)
        {
          _teeCross = new CursorTool();
        }
        return _teeCross;
      }
    }
    private static CursorTool _teeHand;
    public static CursorTool TeeHand
    {
      get
      {
        if (_teeHand == null)
        {
          _teeHand = new CursorTool();
        }
        return _teeHand;
      }
    }
    private static CursorTool _teeNo;
    public static CursorTool TeeNo
    {
      get
      {
        if (_teeNo == null)
        {
          _teeNo = new CursorTool();
        }
        return _teeNo;
      }
    }
  }
Only one CursorTool object created for each Cursor type, reusable as many times as you like without any extra memory overhead.
Thank you!

Christopher Ireland (Steema crew)
Please be aware of the newsgroup archives:
http://www.teechart.net/support/search.php
http://groups.google.com
http://codenewsfast.com/

johnyboy
Newbie
Newbie
Posts: 13
Joined: Mon Oct 06, 2008 12:00 am

Suggestions

Post by johnyboy » Tue Nov 18, 2008 4:18 pm

I understand what you are trying to do with your "CursorTool Pool" concept, but I have many CursorTools active at the same time, and I think this will get exponentially hard to maintain.

While this may work, I think you could find a much simpler (and better) solution.
Please consider these alternatives:

1) You could expose the CursorTool.originalCursor variable so that I could simply set it to the appropriate value when Chart.Cursor changes.

OR

2) before overriding the Cursor, the CursorTool should check that Chart.Cursor has not changed and if it did, set its private originalCursor accordingly. Currently, the CursorTool does that only once after its creation: it simply does not expect the Chart.Cursor to be changed by anything other than itself.

OR

3) Instead of using a private originalCursor Variable in every CursorTool,
Add a "DefaultCursor" to the Chart object. This way, the CursorTool would not need to save the old mouse cursor to its own originalCursor: it would simply set Chart.Cursor to either DefaultCursor or to the HSplit or whatever it needs. This way, if my code changes the Chart.DefaultCursor, the CursorTools and my code will work in harmony.

The fact is that I have to fight against the CursorTool to set the Chart.Cursor to what I want. That should simply not be an issue.

By the way, I found other tools such as the AxixScroll tool that exhibits the same behavior when changing mouse cursors.

Christopher
Site Admin
Site Admin
Posts: 1349
Joined: Thu Jan 01, 1970 12:00 am
Location: Riudellots de la Selva, Catalonia
Contact:

Re: Suggestions

Post by Christopher » Wed Nov 19, 2008 11:45 am

johnyboy,
johnyboy wrote: 1) You could expose the CursorTool.originalCursor variable so that I could simply set it to the appropriate value when Chart.Cursor changes.
Ok, I've wrapped the originalCursor field in a public property called OriginalCursor and have given it a go as in the following code:

Code: Select all

  public partial class Form3 : Form
  {
    private Steema.TeeChart.Styles.FastLine fastLine1;
    private Steema.TeeChart.Tools.CursorTool cursorTool1;

    private int CurrentCursor = 0;
    public Form3()
    {
      InitializeComponent();
      tChart1.Series.Add(fastLine1 = new FastLine());
      tChart1.Tools.Add(cursorTool1 = new CursorTool());
      cursorTool1.Active = false;
      fastLine1.FillSampleValues(1000);
    }

    private void SetCursor()
    {
      tmpX = cursorTool1.XValue;
      tmpY = cursorTool1.YValue;

      switch (CurrentCursor)
      {
        case 0:
          tChart1.Cursor = Cursors.Arrow;
          cursorTool1.OriginalCursor = Cursors.Arrow;
          break;
        case 1:
          tChart1.Cursor = Cursors.Cross;
          cursorTool1.OriginalCursor = Cursors.Cross;
          break;
        case 2:
          tChart1.Cursor = Cursors.Hand;
          cursorTool1.OriginalCursor = Cursors.Hand;
          break;
        case 3:
          tChart1.Cursor = Cursors.No;
          cursorTool1.OriginalCursor = Cursors.No;
          break;
      }

      cursorTool1.Series = fastLine1;
      cursorTool1.Pen.Color = Color.Yellow;
      cursorTool1.YValue = tmpY;
      cursorTool1.XValue = tmpX;
      cursorTool1.YValue = tmpY;
      cursorTool1.Active = checkBox1.Checked;
    }

    private double tmpX, tmpY;

    private void button1_Click(object sender, EventArgs e)
    {
      CurrentCursor = ++CurrentCursor % 4;
      SetCursor();
    }

    private void checkBox1_CheckedChanged(object sender, EventArgs e)
    {
      SetCursor();
      cursorTool1.Active = checkBox1.Checked;
    }
  }
This seems to work fine. If you're happier working this way, then I don't mind leaving this property in the code for the next maintenance release, due out at the beginning of week 51. Please let me know if you want me to do so!
Thank you!

Christopher Ireland (Steema crew)
Please be aware of the newsgroup archives:
http://www.teechart.net/support/search.php
http://groups.google.com
http://codenewsfast.com/

Alain Bolduc
Newbie
Newbie
Posts: 30
Joined: Mon Feb 18, 2008 12:00 am

Post by Alain Bolduc » Wed Nov 19, 2008 1:58 pm

I actually have the same issue as johnyboy, and having OriginalCursor would be great for us too.

thanks

Narcís
Site Admin
Site Admin
Posts: 14730
Joined: Mon Jun 09, 2003 4:00 am
Location: Banyoles, Catalonia
Contact:

Post by Narcís » Thu Nov 20, 2008 11:07 am

Hello everyone,

Thanks for your feedback.

OriginalCursor property has been added to AxisScroll, Cursor and DrawLine Tools for next maintenance release.
Best Regards,
Narcís Calvet / Development & Support
Steema Software
Avinguda Montilivi 33, 17003 Girona, Catalonia
Tel: 34 972 218 797
http://www.steema.com
Image Image Image Image Image Image
Instructions - How to post in this forum

johnyboy
Newbie
Newbie
Posts: 13
Joined: Mon Oct 06, 2008 12:00 am

Thanks...

Post by johnyboy » Fri Nov 21, 2008 12:55 pm

...Narcis, Christopher ... and Qwerty

This will work for me too.

Post Reply