I'm here with another dilemma and I need your help. Let's go.... I have a custom control that you can draw on it, arrow, square and freehand, I use it together with a picturebox to capture the user's entire desktop. This process is now over, I can capture the work area, let the user select the area they want with my custom control, the user can draw freely on the control, however, when I save the image that the user selected, the drawings that the user made, does not come with the selected image. What I want to do is merge the drawings made in my control, together with the area selected by the user, and save or copy to the clipboard (this is not a problem), the problem is to merge the two information.
Example of capturing the work area with the drawings made:
This is the code for my custom control:
Imports System.ComponentModel
Imports System.Drawing.Drawing2D
Partial Public Class FrameControl
Inherits UserControl
<DefaultValue(True)>
Public Property CanMove As Boolean = True
<DefaultValue(False)>
Public Property DrawArrow As Boolean = False
<DefaultValue(False)>
Public Property DrawHand As Boolean = False
<DefaultValue(False)>
Public Property DrawRect As Boolean = False
Public Property DrawingcColor As Color = Color.LightGreen
Public Property PenSize As Single = 3.0F
Const WM_NCHITTEST As Integer = 132
Const WM_SETCURSOR As Integer = 32
Const WM_MOUSEMOVE As Integer = 32
Const WM_NCLBUTTONDBLCLK As Integer = 163
Private isMouseDown As Boolean
Private m_StartX, m_StartY, m_CurX, m_CurY As Single
Dim MyLineArrows As New List(Of LineList)
Private SelectedColor As Color = Color.LightGreen
Private drawingRects As List(Of DrawingRectangle) = New List(Of DrawingRectangle)()
Private linefreehand As New GraphicsPath()
Public Sub New()
InitializeComponent()
SetStyle(ControlStyles.SupportsTransparentBackColor, True)
DoubleBuffered = True
ResizeRedraw = True
BackColor = Color.Transparent
Timer1.Enabled = True
End Sub
Protected Overrides Sub OnPaint(e As PaintEventArgs)
MyBase.OnPaint(e)
Try
Using p = New Pen(Color.White, 3)
p.DashStyle = Drawing2D.DashStyle.Dash
e.Graphics.DrawRectangle(p, 0, 0, Width - 1, Height - 1)
End Using
Catch ex As Exception
Throw ex
End Try
End Sub
Protected Overrides Sub WndProc(ByRef m As Message)
Try
Dim borderWidth As Integer = 10
If m.Msg = WM_SETCURSOR Then
If (m.LParam.ToInt32() And 65535) = 2 Then
Cursor.Current = If(CanMove, Cursors.SizeAll, Cursors.Arrow)
m.Result = CType(1, IntPtr)
Return
End If
End If
If m.Msg = WM_MOUSEMOVE Then
If (m.LParam.ToInt32() And 65535) = 2 Then
Cursor.Current = Cursors.Arrow
m.Result = CType(1, IntPtr)
Return
End If
End If
If m.Msg = WM_NCLBUTTONDBLCLK Then
m.Result = CType(1, IntPtr)
Return
End If
MyBase.WndProc(m)
If m.Msg = WM_NCHITTEST Then
Dim pos = PointToClient(New Point(m.LParam.ToInt32() And 65535, m.LParam.ToInt32() >> 16))
If (pos.X <= (ClientRectangle.Left + borderWidth)) AndAlso (pos.Y <= (ClientRectangle.Top + borderWidth)) Then
m.Result = New IntPtr(13)
Else
If (pos.X >= (ClientRectangle.Right - borderWidth)) AndAlso (pos.Y <= (ClientRectangle.Top + borderWidth)) Then
m.Result = New IntPtr(14)
Else
If (pos.X <= (ClientRectangle.Left + borderWidth)) AndAlso (pos.Y >= (ClientRectangle.Bottom - borderWidth)) Then
m.Result = New IntPtr(16)
Else
If (pos.X >= (ClientRectangle.Right - borderWidth)) AndAlso (pos.Y >= (ClientRectangle.Bottom - borderWidth)) Then
m.Result = New IntPtr(17)
Else
If pos.X <= (ClientRectangle.Left + borderWidth) Then
m.Result = New IntPtr(10)
Else
If pos.Y <= (ClientRectangle.Top + borderWidth) Then
m.Result = New IntPtr(12)
Else
If pos.X >= (ClientRectangle.Right - borderWidth) Then
m.Result = New IntPtr(11)
Else
If pos.Y >= (ClientRectangle.Bottom - borderWidth) Then
m.Result = New IntPtr(15)
Else
m.Result = (If(CanMove, New IntPtr(2), IntPtr.Zero))
End If
End If
End If
End If
End If
End If
End If
End If
End If
Catch ex As Exception
Throw ex
End Try
End Sub
Private Sub PictureBox1_MouseDown(sender As Object, e As MouseEventArgs) Handles PictureBox1.MouseDown
Try
isMouseDown = True
If DrawArrow Then
m_StartX = e.X
m_StartY = e.Y
m_CurX = e.X
m_CurY = e.Y
ElseIf DrawRect Then
drawingRects.Add(New DrawingRectangle() With {
.Location = e.Location,
.Size = Size.Empty,
.StartPosition = e.Location
})
ElseIf DrawHand Then
linefreehand.StartFigure()
End If
Catch ex As Exception
Throw ex
End Try
End Sub
Private Sub PictureBox1_MouseMove(sender As Object, e As MouseEventArgs) Handles PictureBox1.MouseMove
Try
If isMouseDown Then
If DrawArrow Then
m_CurX = e.X
m_CurY = e.Y
ElseIf DrawRect Then
Dim dr = drawingRects(drawingRects.Count - 1)
If e.Y < dr.StartPosition.Y Then
dr.Location = New Point(dr.Rect.Location.X, e.Y)
End If
If e.X < dr.StartPosition.X Then
dr.Location = New Point(e.X, dr.Rect.Location.Y)
End If
dr.Size = New Size(Math.Abs(dr.StartPosition.X - e.X), Math.Abs(dr.StartPosition.Y - e.Y))
ElseIf DrawHand Then
linefreehand.AddLine(e.X, e.Y, e.X, e.Y)
End If
PictureBox1.Invalidate()
End If
Catch ex As Exception
Throw ex
End Try
End Sub
Private Sub FrameControl_Load(sender As Object, e As EventArgs) Handles MyBase.Load
UseDoubleBuffer()
End Sub
Private Sub PictureBox1_MouseUp(sender As Object, e As MouseEventArgs) Handles PictureBox1.MouseUp
Try
isMouseDown = False
If DrawArrow Then
Dim DrawLine As New LineList
DrawLine.X1 = m_StartX
DrawLine.Y1 = m_StartY
DrawLine.X2 = m_CurX
DrawLine.Y2 = m_CurY
MyLineArrows.Add(DrawLine)
ElseIf DrawRect Then
Dim dr = drawingRects.Last()
End If
Catch ex As Exception
Throw ex
End Try
End Sub
Private Sub Timer1_Tick(sender As Object, e As EventArgs) Handles Timer1.Tick
Try
If CanMove Then
linefreehand.Reset()
drawingRects.Clear()
MyLineArrows.Clear()
PictureBox1.Invalidate()
PictureBox1.Visible = False
Else
PictureBox1.Invalidate()
PictureBox1.Dock = DockStyle.Fill
PictureBox1.BringToFront()
PictureBox1.Visible = True
End If
Catch ex As Exception
Throw ex
End Try
End Sub
Private Sub PictureBox1_Paint(sender As Object, e As PaintEventArgs) Handles PictureBox1.Paint
DrawShapes(e.Graphics)
End Sub
Private Sub DrawShapes(ByVal g As Graphics)
Try
g.SmoothingMode = SmoothingMode.AntiAlias
g.SmoothingMode = SmoothingMode.HighQuality
g.SmoothingMode = SmoothingMode.HighSpeed
For Each dr In drawingRects
Using pen As Pen = New Pen(DrawingcColor, PenSize)
g.DrawRectangle(pen, dr.Rect)
End Using
Next
Dim x1, y1, x2, y2 As Integer
Dim linecolor As Color = DrawingcColor
Dim linepen As Single = PenSize
For Each line In MyLineArrows
x1 = line.X1 : x2 = line.X2
y1 = line.Y1 : y2 = line.Y2
Using Arrow1 As New Pen(linecolor, linepen)
Arrow1.EndCap = LineCap.Custom
Arrow1.CustomEndCap = New AdjustableArrowCap(2, 2, False)
g.DrawLine(Arrow1, x1, y1, x2, y2)
End Using
Next
If isMouseDown Then
Using Arrow As Pen = New Pen(linecolor, linepen)
Arrow.EndCap = LineCap.Custom
Arrow.CustomEndCap = New AdjustableArrowCap(2, 2, False)
g.DrawLine(Arrow, m_StartX, m_StartY, m_CurX, m_CurY)
End Using
End If
Using handpen As Pen = New Pen(linecolor, linepen)
g.DrawPath(handpen, linefreehand)
End Using
Catch ex As Exception
Throw ex
End Try
End Sub
Private Sub UseDoubleBuffer()
Me.SetStyle(ControlStyles.AllPaintingInWmPaint Or
ControlStyles.UserPaint Or
ControlStyles.OptimizedDoubleBuffer, True)
End Sub
End Class
This is the code I use to capture the desktop:
Imports System.Drawing.Drawing2D
Imports Controle_Customizado_PictureBox
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 ctrlResize As New FrameControl()
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)
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)
ctrlResize.DrawingcColor = pb_color.BackColor
pb_screen.Controls.Add(ctrlResize)
End Sub
Private Sub pb_screen_MouseDown(sender As Object, e As MouseEventArgs) Handles pb_screen.MouseDown
IsMouseDown = True
StartLocation = e.Location
With ctrlResize
.CanMove = True
.DrawArrow = False
.DrawHand = False
.DrawRect = False
End With
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 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
Private Sub pb_color1_Click(sender As Object, e As EventArgs) Handles pb_color.Click
'ColorDialog1.ShowDialog()
If ColorDialog1.ShowDialog <> DialogResult.Cancel Then
pb_color.BackColor = ColorDialog1.Color
ctrlResize.DrawingcColor = ColorDialog1.Color
End If
End Sub
Private Sub btn_arrow_Click(sender As Object, e As EventArgs) Handles btn_arrow.Click
With ctrlResize
.CanMove = False
.DrawArrow = True
.DrawHand = False
.DrawRect = False
End With
End Sub
Private Sub btn_freehand_Click(sender As Object, e As EventArgs) Handles btn_freehand.Click
With ctrlResize
.CanMove = False
.DrawArrow = False
.DrawHand = True
.DrawRect = False
End With
End Sub
Private Sub btn_rect_Click(sender As Object, e As EventArgs) Handles btn_rect.Click
With ctrlResize
.CanMove = False
.DrawArrow = False
.DrawHand = False
.DrawRect = True
End With
End Sub
Private Sub btn_copy_Click(sender As Object, e As EventArgs) Handles btn_copy.Click
Dim selectedRectangle = New Rectangle(StartLocation.X, StartLocation.Y, ctrlResize.ClientRectangle.Width, ctrlResize.ClientRectangle.Height)
Dim result = GetRectangeOnImage(pb_screen, selectedRectangle)
Console.WriteLine("VALORES DO RETANGULO: " & selectedRectangle.ToString)
Using bm = New Bitmap(CInt(result.Width), CInt(result.Height))
Using g = Graphics.FromImage(bm)
g.SmoothingMode = SmoothingMode.HighQuality
g.CompositingQuality = CompositingQuality.HighQuality
g.DrawImage(pb_screen.Image, 0, 0, result, GraphicsUnit.Pixel)
End Using
Dim img As Image = CType(bm.Clone(), Image)
PictureBox1.Size = New Size(ctrlResize.Size)
PictureBox1.Image = img
PictureBox1.Visible = False
End Using
End Sub
Public Function GetRectangeOnImage(ByVal p As PictureBox, ByVal selectionRect As Rectangle) As RectangleF
Dim method = GetType(PictureBox).GetMethod("ImageRectangleFromSizeMode", System.Reflection.BindingFlags.NonPublic Or System.Reflection.BindingFlags.Instance)
Dim imageRect = CType(method.Invoke(p, New Object() {p.SizeMode}), Rectangle)
If p.Image Is Nothing Then Return selectionRect
Dim cx = CSng(p.Image.Width) / CSng(imageRect.Width)
Dim cy = CSng(p.Image.Height) / CSng(imageRect.Height)
Dim r2 = Rectangle.Intersect(imageRect, selectionRect)
r2.Offset(-imageRect.X, -imageRect.Y)
Return New RectangleF(r2.X * cx, r2.Y * cy, r2.Width * cx, r2.Height * cy)
End Function
End Class
I accept suggestions in C# or VB.net, that's not a problem.