How to draw a window at Mouse coordination in OnMouseMove

noppanon since: 17 May 2017;

  27 Feb 2021, 13:39
How to draw a window at Mouse coordination in OnMouseMove

Hi there,

   I try to draw a small window similar to Market Snapshot window when user select the cursor. I'd like to my custom data. However, when I draw, the window is drawn a bit off from my mouse pointer. See the sample.

The blue dot is my mouse pointer. 

Here is my code. The DataForm is just a plain WinForm.

using System;
using cAlgo.API;
using cAlgo.API.Internals;
using cAlgo.API.Indicators;
using cAlgo.Indicators;

namespace cAlgo
{
    [Indicator(IsOverlay = false, TimeZone = TimeZones.UTC, AccessRights = AccessRights.FullAccess)]
    public class CustomData : Indicator
    {
        [Parameter(DefaultValue = 0.0)]
        public double Parameter { get; set; }

        [Output("Main")]
        public IndicatorDataSeries Result { get; set; }

        protected DataForm DataTip;

        protected override void Initialize()
        {
            Chart.MouseMove += OnMouseMove;
            Chart.MouseLeave += OnMouseLeave;

            DataTip = new DataForm();
            DataTip.Show();
            DataTip.Visible = false;
        }

        public override void Calculate(int index)
        {
            // Calculate value at specified index
            // Result[index] = ...
        }

        protected void OnMouseMove(ChartMouseEventArgs obj)
        {
            Chart chart = obj.Chart;

            if (chart != null)
            {
                if (!DataTip.Visible)
                {
                    DataTip.Visible = true;
                    DataTip.Show();
                    DataTip.TopMost = true;
                }
                var x = (int)Math.Round(obj.MouseX, 0);
                var y = (int)Math.Round(obj.MouseY, 0);

                DataTip.Location = new System.Drawing.Point(x, y);

                Print("mouse X {0}   mouse Y {1}", obj.MouseX, obj.MouseY);

            }
        }

        protected void OnMouseLeave(ChartMouseEventArgs obj)
        {
            if (obj.Chart != null)
            {
                DataTip.Visible = false;
            }
        }

    }
}

Thanks in advance

Noppanon

amusleh's avatar

amusleh since: 01 Mar 2021;

  01 Mar 2021, 11:30

The issue with your code is that you are using mouse position on chart not screen, the window form location (x,y) is based on your screen coordinates not chart.

To solve the issue you have two option, either use the chart controls or use Windows API to get the location of mouse on your screen.

Here is an example of how you can show a tooltip with chart controls:

using cAlgo.API;

namespace cAlgo
{
    [Indicator(IsOverlay = true, TimeZone = TimeZones.UTC, AccessRights = AccessRights.FullAccess)]
    public class CustomData : Indicator
    {
        [Parameter(DefaultValue = 0.0)]
        public double Parameter { get; set; }

        [Output("Main")]
        public IndicatorDataSeries Result { get; set; }

        private Tooltip _tooltip;

        protected override void Initialize()
        {
            var content = new TextBlock { Text = "Here you can put any control", Margin = 5 };

            _tooltip = new Tooltip(content)
            {
                VerticalAlignment = VerticalAlignment.Top,
                HorizontalAlignment = HorizontalAlignment.Left,
                IsVisible = false,
                Width = 160,
                Height = 40
            };

            Chart.AddControl(_tooltip);

            Chart.MouseMove += OnMouseMove;
            Chart.MouseLeave += OnMouseLeave;
            Chart.MouseEnter += Chart_MouseEnter;
        }

        private void Chart_MouseEnter(ChartMouseEventArgs obj)
        {
            _tooltip.IsVisible = true;
        }

        public override void Calculate(int index)
        {
            // Calculate value at specified index
            // Result[index] = ...
        }

        protected void OnMouseMove(ChartMouseEventArgs obj)
        {
            var extraDelta = 10;
            var width = _tooltip.Width;
            var height = _tooltip.Height;
            var left = Chart.Width - obj.MouseX > width + extraDelta ? obj.MouseX + extraDelta : obj.MouseX - width - extraDelta;
            var right = Chart.Height - obj.MouseY > height + extraDelta ? obj.MouseY + extraDelta : obj.MouseY - height - extraDelta;

            _tooltip.Margin = new Thickness(left, right, 0, 0);
        }

        protected void OnMouseLeave(ChartMouseEventArgs obj)
        {
            _tooltip.IsVisible = false;
        }
    }

    public class Tooltip : CustomControl
    {
        public Tooltip(ControlBase content)
        {
            var border = new Border
            {
                BackgroundColor = "#3F3F3F",
                BorderColor = "#969696",
                BorderThickness = 1,
                CornerRadius = 5
            };

            border.Child = content;

            AddChild(border);
        }
    }
}

If you want to use a Windows form then you have to use the Windows API to get the mouse location on screen.

For using Windows API you can use my GlobalHook library: 

Example:

using cAlgo.API;
using Gma.UserActivityMonitor;
using System;
using System.Threading;

namespace cAlgo
{
    [Indicator(IsOverlay = true, TimeZone = TimeZones.UTC, AccessRights = AccessRights.FullAccess)]
    public class CustomData : Indicator
    {
        private DataForm _dataForm = new DataForm();

        private Thread _appThread;

        protected override void Initialize()
        {
            Chart.MouseLeave += OnMouseLeave;
            Chart.MouseEnter += Chart_MouseEnter;

            RunApplicationThread();
        }

        public override void Calculate(int index)
        {
        }

        private void HookManager_MouseMove(object sender, System.Windows.Forms.MouseEventArgs e)
        {
            _dataForm.Location = e.Location;
        }

        private void Chart_MouseEnter(ChartMouseEventArgs obj)
        {
            _dataForm.Visible = true;

            _dataForm.TopMost = true;
        }

        protected void OnMouseLeave(ChartMouseEventArgs obj)
        {
            _dataForm.Visible = false;
        }

        private void RunApplicationThread()
        {
            _appThread = new Thread(() =>
            {
                try
                {
                    HookManager.MouseMove += HookManager_MouseMove;

                    // Without a running application loop the global hook will not work!
                    System.Windows.Forms.Application.Run(_dataForm);

                    _dataForm.Visible = false;
                }
                catch (Exception ex)
                {
                    Print(ex);
                }
            });

            _appThread.SetApartmentState(ApartmentState.STA);
            _appThread.DisableComObjectEagerCleanup();

            _appThread.Start();
        }
    }
}

If you use a windows form as a tooltip and move it alongside mouse cursor it will use too much resource so I recommend you to use chart controls instead.

Community Developer | Spotware

noppanon since: 17 May 2017;

  05 Mar 2021, 13:50

Thanks amusleh. It works even better than cTrader market snapshot.

Noppanon