Draw an arrow on a custom control inside a picture box

73 Views Asked by At

I am developing an application to capture the user's desktop, this process works well, the desktop capture is sent to a picturebox with the full image, and within this picturebox, I have a custom control that is added, for the user to select the desired area. The problem in question is I can't draw in this control, I have a toolbox where the user can choose between free drawing and an arrow, but I can't make these drawings within the control. Drawings work well outside of control and also in another picturebox that is not linked.

I used the example provided in this post Detect mouse over User Control and all children - C# WinForms, but unfortunately it didn't work either

This is the code for my control, I even made some adaptations to the MouseDown, MouseUp and MouseMove events, but I still haven't been successful.

using System;
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;

namespace ControlSizable
{
    public partial class FrameControl: UserControl
    {
        public FrameControl()
        {
            InitializeComponent();
            WireMouseEvents(this);
            SetStyle(ControlStyles.SupportsTransparentBackColor, true);
            DoubleBuffered = true;
            ResizeRedraw = true;
            BackColor = Color.Transparent;
        }       
        [DefaultValue(true)]
        public bool CanMove { get; set; } = true;

        void WireMouseEvents(Control container)
        {
            foreach (Control c in container.Controls)
            {
                c.MouseDown += (s, e) => OnMouseDown(e);
                c.MouseMove += (s, e) => OnMouseMove(e);
                c.MouseUp += (s, e) => OnMouseUp(e);
                c.Click += (s, e) => OnClick(e);
                c.DoubleClick += (s, e) => OnDoubleClick(e);
                c.MouseHover += (s, e) => OnMouseHover(e);

                c.MouseClick += (s, e) => {
                    var p = PointToThis((Control)s, e.Location);
                    OnMouseClick(new MouseEventArgs(e.Button, e.Clicks, p.X, p.Y, e.Delta));
                };
                c.MouseDoubleClick += (s, e) => {
                    var p = PointToThis((Control)s, e.Location);
                    OnMouseDoubleClick(new MouseEventArgs(e.Button, e.Clicks, p.X, p.Y, e.Delta));
                };

                WireMouseEvents(c);
            };
        }

        Point PointToThis(Control c, Point p)
        {
            return PointToClient(c.PointToScreen(p));
        }
        protected override void OnHandleCreated(EventArgs e)
        {
            base.OnHandleCreated(e);
            WireMouseEvents(this);
        }
        private void FrameControl_MouseDown(object sender, MouseEventArgs e)
        {
            Console.Write("Box1 clicked.");
        }

        protected override void OnPaint(PaintEventArgs e)
        {
            base.OnPaint(e);
            using (var p = new Pen(Color.White , 3))
            {
               p.DashStyle = System.Drawing.Drawing2D.DashStyle.Dash  ;
               e.Graphics.DrawRectangle(p, 0, 0, Width - 1, Height - 1);
            }
        }

        const int WM_NCHITTEST = 0x84;
        const int WM_SETCURSOR = 0x20;
        const int WM_NCLBUTTONDBLCLK = 0xA3;
        protected override void WndProc(ref Message m)
        {
            int borderWidth = 10;
            if (m.Msg == WM_SETCURSOR)  /*Setting cursor to SizeAll*/
            {
                if ((m.LParam.ToInt32() & 0xffff) == 0x2 /*Move*/)
                {
                    //Cursor.Current = Cursors.SizeAll;
                    Cursor.Current = CanMove ? Cursors.SizeAll : Cursors.Arrow;
                    m.Result = (IntPtr)1;
                    return;
                }
                else if ((m.LParam.ToInt32() & 0xffff) == 0x3 /*Move*/)
                {
                    Cursor.Current = Cursors.Arrow ;
                    m.Result = (IntPtr)1;
                    return;
                }
            
            }
            if ((m.Msg == WM_NCLBUTTONDBLCLK)) /*Disable Mazimiz on Double click*/
            {
                m.Result = (IntPtr)1;
                return;
            }
            base.WndProc(ref m);
            if (m.Msg == WM_NCHITTEST)
            {
                var pos = PointToClient(new Point(m.LParam.ToInt32() & 0xffff,
                    m.LParam.ToInt32() >> 16));
                if (pos.X <= ClientRectangle.Left + borderWidth &&
                    pos.Y <= ClientRectangle.Top + borderWidth)
                    m.Result = new IntPtr(13); //TOPLEFT
                else if (pos.X >= ClientRectangle.Right - borderWidth &&
                    pos.Y <= ClientRectangle.Top + borderWidth)
                    m.Result = new IntPtr(14); //TOPRIGHT
                else if (pos.X <= ClientRectangle.Left + borderWidth &&
                    pos.Y >= ClientRectangle.Bottom - borderWidth)
                    m.Result = new IntPtr(16); //BOTTOMLEFT
                else if (pos.X >= ClientRectangle.Right - borderWidth &&
                    pos.Y >= ClientRectangle.Bottom - borderWidth)
                    m.Result = new IntPtr(17); //BOTTOMRIGHT
                else if (pos.X <= ClientRectangle.Left + borderWidth)
                    m.Result = new IntPtr(10); //LEFT
                else if (pos.Y <= ClientRectangle.Top + borderWidth)
                    m.Result = new IntPtr(12); //TOP
                else if (pos.X >= ClientRectangle.Right - borderWidth)
                    m.Result = new IntPtr(11); //RIGHT
                else if (pos.Y >= ClientRectangle.Bottom - borderWidth)
                    m.Result = new IntPtr(15); //Bottom
                else
                    //m.Result = new IntPtr(2); //Move
                    m.Result = CanMove ? new IntPtr(2) : IntPtr.Zero;
            }
            
        }
    }
}

This is the code I use to make the drawings and also the screenshot. My control is written in C# and created a Dll and all the rest of my code is in VB.net

Imports System.Drawing.Drawing2D

Public Class frm_transp_form
    Private imgInput As Image
    Private EndLocation As Point
    Private StartLocation As Point
    Private rect As Rectangle
    Private IsMouseDown As Boolean = False
    Private m_Drawing As Boolean
    Private m_StartX, m_StartY, m_CurX, m_CurY As Single
    Dim MyLineArrows As New List(Of LineList)

    Private Sub frm_transp_form_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        Me.Size = New Size(Screen.AllScreens.Sum(Function(s As Screen) s.Bounds.Width), Screen.AllScreens.Max(Function(s As Screen) s.Bounds.Height))
        pb_screen.Dock = DockStyle.Fill
        Dim bmp As Bitmap = New Bitmap(Screen.AllScreens.Sum(Function(s As Screen) s.Bounds.Width), Screen.AllScreens.Max(Function(s As Screen) s.Bounds.Height))
        Using g As Graphics = Graphics.FromImage(bmp)
            g.SmoothingMode = Drawing2D.SmoothingMode.HighQuality
            g.CompositingQuality = Drawing2D.CompositingQuality.HighQuality
            g.CopyFromScreen(SystemInformation.VirtualScreen.X, SystemInformation.VirtualScreen.Y, 0, 0, SystemInformation.VirtualScreen.Size, CopyPixelOperation.SourceCopy)
            'Me.BackgroundImage = bmp
            pb_screen.Image = bmp
        End Using
        Me.Location = New Point(0, 0)
        Me.Cursor = Cursors.Arrow
        ctrlResize.Size = New Size(0, 0)
        ctrlResize.Location = New Point(0, 0)

        pb_screen.Controls.Add(ctrlResize)

        AddHandler ctrlResize.MouseDown, New MouseEventHandler(AddressOf controlResize_MouseDown)
        AddHandler ctrlResize.MouseUp, New MouseEventHandler(AddressOf controlResize_MouseUP)
        AddHandler ctrlResize.MouseMove, New MouseEventHandler(AddressOf controlResize_MouseMove)
        AddHandler ctrlResize.MouseClick, New MouseEventHandler(AddressOf controlResize_MouseClick)
        AddHandler ctrlResize.Paint, New PaintEventHandler(AddressOf controlResize_Paint)

    End Sub

    Private Sub pb_screen_MouseDown(sender As Object, e As MouseEventArgs) Handles pb_screen.MouseDown
        IsMouseDown = True
        StartLocation = e.Location
        ctrlResize.CanMove = True
        pnl_menu.Visible = False
        Console.WriteLine("Startlocation: " & StartLocation.ToString)
    End Sub

    Private Sub pb_screen_MouseMove(sender As Object, e As MouseEventArgs) Handles pb_screen.MouseMove
        If IsMouseDown = True Then
            EndLocation = e.Location
            GetRectangle()
            Application.DoEvents()
            lbl_position.Text = rect.Width & "x" & rect.Height
            ctrlResize.Size = New Size(rect.Size)
            ctrlResize.Location = New Point((StartLocation.X), (StartLocation.Y))
            pnl_menu.Location = New Point(EndLocation.X + 5, EndLocation.Y - pnl_menu.Height)
            Application.DoEvents()
            pb_screen.Invalidate()
        End If
    End Sub

    Private Sub pb_screen_Paint(sender As Object, e As PaintEventArgs) Handles pb_screen.Paint
        e.Graphics.ExcludeClip(ctrlResize.Bounds)
        Using b = New SolidBrush(Color.FromArgb(100, Color.Black))
            e.Graphics.FillRectangle(b, pb_screen.ClientRectangle)
        End Using

    End Sub

    Private Sub pb_screen_MouseUp(sender As Object, e As MouseEventArgs) Handles pb_screen.MouseUp
        If IsMouseDown = True Then
            EndLocation = e.Location
            IsMouseDown = False

            Console.WriteLine("Retangulo: " & rect.Size.ToString)
            pnl_menu.Visible = True
        End If
    End Sub
    Private Sub UseDoubleBuffer()
        Me.SetStyle(ControlStyles.AllPaintingInWmPaint Or
                     ControlStyles.UserPaint Or
                     ControlStyles.OptimizedDoubleBuffer, True)
    End Sub


    Private Sub controlResize_Paint(sender As Object, e As PaintEventArgs)
        Dim i, x1, y1, x2, y2 As Integer

        For i = 0 To MyLineArrows.Count - 1
            Dim Arrow1 As New Pen(Color.FromArgb(255, 0, 0, 255), 10)
            Arrow1.EndCap = LineCap.Custom
            Arrow1.CustomEndCap = New AdjustableArrowCap(3, 3, False)
            x1 = MyLineArrows(i).X1 : x2 = MyLineArrows(i).X2
            y1 = MyLineArrows(i).Y1 : y2 = MyLineArrows(i).Y2
            e.Graphics.DrawLine(Arrow1, x1, y1, x2, y2)
        Next

        If m_Drawing Then
            Dim dashed_pen As New Pen(Color.Green, 1)
            Dim Arrow As New Pen(Color.FromArgb(255, 0, 0, 255), 10)
            Arrow.EndCap = LineCap.Custom
            Arrow.CustomEndCap = New AdjustableArrowCap(3, 3, False)
            e.Graphics.DrawLine(Arrow, m_StartX, m_StartY, m_CurX, m_CurY)
        End If
    End Sub
    Private Sub controlResize_MouseClick(sender As Object, e As MouseEventArgs)
        MessageBox.Show("Foi Clicado dentro do Controle")
        Console.WriteLine("Foi Clicado dentro do Controle")
    End Sub

    Private Sub controlResize_MouseDown(sender As Object, e As MouseEventArgs)

        m_Drawing = True
        m_StartX = e.X
        m_StartY = e.Y
        m_CurX = e.X
        m_CurY = e.Y

        Console.WriteLine("Valores do MouseDown: " & m_Drawing & " - " & m_StartX & " - " & m_StartY & " - " & m_CurX & " - " & m_CurY)
    End Sub

    Private Sub controlResize_MouseUP(sender As Object, e As MouseEventArgs)
        m_Drawing = False
        Dim DrawLine As New LineList
        DrawLine.X1 = m_StartX
        DrawLine.Y1 = m_StartY
        DrawLine.X2 = m_CurX
        DrawLine.Y2 = m_CurY
        MyLineArrows.Add(DrawLine)

    End Sub

    Private Sub controlResize_MouseMove(sender As Object, e As MouseEventArgs)
        Dim g As Graphics = ctrlResize.CreateGraphics
        Dim dashed_pen As New Pen(Color.Green, 1)
        dashed_pen.DashStyle = DashStyle.Dash
        m_CurX = e.X
        m_CurY = e.Y
        ctrlResize.Invalidate()

    End Sub
    Private Sub btn_arrow_Click(sender As Object, e As EventArgs) Handles btn_arrow.Click
        ctrlResize.CanMove = False
    End Sub


    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        End
    End Sub

    Private Function GetRectangle() As Rectangle
        rect = New Rectangle With {
            .X = Math.Min(StartLocation.X, EndLocation.X),
            .Y = Math.Min(StartLocation.Y, EndLocation.Y),
            .Width = Math.Abs(StartLocation.X - EndLocation.X),
            .Height = Math.Abs(StartLocation.Y - EndLocation.Y)
        }

        Return rect
    End Function

    Protected Overrides ReadOnly Property CreateParams As CreateParams
        Get
            Dim cp As CreateParams = MyBase.CreateParams
            cp.ExStyle = cp.ExStyle Or &H2000000
            Return cp
        End Get
    End Property
End Class

This is an example of capturing the desktop, with the toolbox and also the control in the center of the image. screenshot with custom control Any ideas what might have happened that I can't get these mouse events on my control?

0

There are 0 best solutions below