Playertrac
asked on
Draw rectangle to select controls on panel
I have panel that has multiple pictureboxes which are added dynamically at runtime. The pictureboxes can be moved around the panel individually or as a user defined group. I want the user to be able to drag a mouse across the screen to "define" which pictureboxes are selected.
Basically I want to duplicate the VB6 or VS.Net design environment with regards to controls on a design time form. You can select multiple controls by dragging a rectangle around them then you can move the selected controls by either dragging them of using arrow keys. Of course my program will be doing this at runtime instead of design time.
I have every part working except Drawing the rectangle and based on that rectangle determining which picture boxes to select.
MOST IMPORT (and essential for full credit)
Tell me how to draw the dotted line rectangle so that is shows on top of the controls on the form while it is being drawn (mouse drag). It MUST be transparent so that you can see which pictureboxes will be selected.
SECONDLY
Tell me any neat tricks you know for determing which controls to select based on the rectangle drawn.
Thanks in advance!
Scott
Basically I want to duplicate the VB6 or VS.Net design environment with regards to controls on a design time form. You can select multiple controls by dragging a rectangle around them then you can move the selected controls by either dragging them of using arrow keys. Of course my program will be doing this at runtime instead of design time.
I have every part working except Drawing the rectangle and based on that rectangle determining which picture boxes to select.
MOST IMPORT (and essential for full credit)
Tell me how to draw the dotted line rectangle so that is shows on top of the controls on the form while it is being drawn (mouse drag). It MUST be transparent so that you can see which pictureboxes will be selected.
SECONDLY
Tell me any neat tricks you know for determing which controls to select based on the rectangle drawn.
Thanks in advance!
Scott
ASKER
planocz
Thanks for the quick reply.
I would rather not use an API call if possible. If I'm not mistaken the program you outlined lets the user draw a rectangle anywhere on the screen instead of limiting it to the confines of the panel. Also, it doesn't work using the mouse drag which is the way I need my program to work (Just like in VS.Net). Lastly it forces the program to determine the panel's position on the screen and convert the cordinates to corispond with those of the panel so that it can determine which controls to select inside the panel. These issues could probably be programmed around but I am hoping for a cleaner solution which better meets the criteria outlined above.
Thanks again!
Scott
Thanks for the quick reply.
I would rather not use an API call if possible. If I'm not mistaken the program you outlined lets the user draw a rectangle anywhere on the screen instead of limiting it to the confines of the panel. Also, it doesn't work using the mouse drag which is the way I need my program to work (Just like in VS.Net). Lastly it forces the program to determine the panel's position on the screen and convert the cordinates to corispond with those of the panel so that it can determine which controls to select inside the panel. These issues could probably be programmed around but I am hoping for a cleaner solution which better meets the criteria outlined above.
Thanks again!
Scott
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
ASKER
planocz
Excellent!
That is what I was looking for. There is only one more small item that would make it Perfect.
To work exactly like VS.net the code needs to select any picturebox that even touches any side of the rubberband not only those controls that are completely inside the rubberband.
Could you post that one small change if you get a chance?
Thanks a bunch!
Scott
Excellent!
That is what I was looking for. There is only one more small item that would make it Perfect.
To work exactly like VS.net the code needs to select any picturebox that even touches any side of the rubberband not only those controls that are completely inside the rubberband.
Could you post that one small change if you get a chance?
Thanks a bunch!
Scott
Just change the If statement from And to Or:
If selectionArea.Contains(c.L eft, c.Top) Or selectionArea.Contains(c.L eft + c.Width, c.Top + c.Height) Then
MsgBox(c.Name & " has been selected")
End If
~IM
If selectionArea.Contains(c.L
MsgBox(c.Name & " has been selected")
End If
~IM
Scratch that last comment...
Two changes:
1) Normalize the selection rectangle so it works no matter which direction you drag in.
2) Use IntersectsWith() instead of Contains().
Private Sub Panel1_MouseUp(ByVal sender As System.Object, ByVal e As System.Windows.Forms.Mouse EventArgs) Handles Panel1.MouseUp
If rubberbanding And e.Button = MouseButtons.Left Then
Cursor.Clip = New Rectangle(0, 0, Screen.GetBounds(New Point(0, 0)).Width, Screen.GetBounds(New Point(0, 0)).Height)
rubberbanding = False
If firstBoxDrawn Then
Dim r As Rectangle = New Rectangle(Math.Min(startX, lastX), Math.Min(startY, lastY), Math.Abs(lastX - startX), Math.Abs(lastY - startY))
ControlPaint.DrawReversibl eFrame(r, Color.Black, FrameStyle.Dashed)
r = Panel1.RectangleToClient(r )
RectangleSelected(r)
End If
End If
End Sub
Private Sub RectangleSelected(ByVal selectionArea As Rectangle)
Dim c As Control
For Each c In Panel1.Controls
If selectionArea.IntersectsWi th(New Rectangle(c.Left, c.Top, c.Width, c.Height)) Then
MsgBox(c.Name & " has been selected")
End If
Next
End Sub
Two changes:
1) Normalize the selection rectangle so it works no matter which direction you drag in.
2) Use IntersectsWith() instead of Contains().
Private Sub Panel1_MouseUp(ByVal sender As System.Object, ByVal e As System.Windows.Forms.Mouse
If rubberbanding And e.Button = MouseButtons.Left Then
Cursor.Clip = New Rectangle(0, 0, Screen.GetBounds(New Point(0, 0)).Width, Screen.GetBounds(New Point(0, 0)).Height)
rubberbanding = False
If firstBoxDrawn Then
Dim r As Rectangle = New Rectangle(Math.Min(startX,
ControlPaint.DrawReversibl
r = Panel1.RectangleToClient(r
RectangleSelected(r)
End If
End If
End Sub
Private Sub RectangleSelected(ByVal selectionArea As Rectangle)
Dim c As Control
For Each c In Panel1.Controls
If selectionArea.IntersectsWi
MsgBox(c.Name & " has been selected")
End If
Next
End Sub
ASKER
Thank you!
I'll give that a try!
Scott
I'll give that a try!
Scott
ASKER
Works Perfect!
Thanks for your help.
Scott
Thanks for your help.
Scott
Public Class Form1
Inherits System.Windows.Forms.Form
'Here is a complete screen capture demo I have written.
'It draws a rubberband box directly on the desktop and
'uses very fast API calls to copy the selected area to
' a picturebox on the form. To select an area position
' your mouse where you want to start the selection and
' press F9. Now move your mouse to select an area and
' push F9 again. There are no mouse clicks involved in
' the process. I registered F9 as a system-wide hotkey
' so the app doesn't need to be in focus for this to work.
'If you set the Icon property of the NotifyIcon1 control
' to a valid icon then the application will minimize to
'the tray instead of the taskbar.
#Region " Windows Form Designer generated code "
Public Sub New()
MyBase.New()
'This call is required by the Windows Form Designer.
InitializeComponent()
'Add any initialization after the InitializeComponent() call
End Sub
'Form overrides dispose to clean up the component list.
Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean)
If disposing Then
If Not (components Is Nothing) Then
components.Dispose()
End If
End If
MyBase.Dispose(disposing)
End Sub
'Required by the Windows Form Designer
Private components As System.ComponentModel.ICon
'NOTE: The following procedure is required by the Windows Form Designer
'It can be modified using the Windows Form Designer.
'Do not modify it using the code editor.
Friend WithEvents Panel1 As System.Windows.Forms.Panel
Friend WithEvents PictureBox1 As System.Windows.Forms.Pictu
Friend WithEvents Label1 As System.Windows.Forms.Label
Friend WithEvents Timer1 As System.Windows.Forms.Timer
Friend WithEvents NotifyIcon1 As System.Windows.Forms.Notif
<System.Diagnostics.Debugg
Me.components = New System.ComponentModel.Cont
Me.Panel1 = New System.Windows.Forms.Panel
Me.PictureBox1 = New System.Windows.Forms.Pictu
Me.Label1 = New System.Windows.Forms.Label
Me.Timer1 = New System.Windows.Forms.Timer
Me.NotifyIcon1 = New System.Windows.Forms.Notif
Me.Panel1.SuspendLayout()
Me.SuspendLayout()
'
'Panel1
'
Me.Panel1.Anchor = CType((((System.Windows.Fo
Or System.Windows.Forms.Ancho
Or System.Windows.Forms.Ancho
Me.Panel1.AutoScroll = True
Me.Panel1.BackColor = System.Drawing.Color.Light
Me.Panel1.BorderStyle = System.Windows.Forms.Borde
Me.Panel1.Controls.Add(Me.
Me.Panel1.Location = New System.Drawing.Point(8, 40)
Me.Panel1.Name = "Panel1"
Me.Panel1.Size = New System.Drawing.Size(292, 276)
Me.Panel1.TabIndex = 1
'
'PictureBox1
'
Me.PictureBox1.Location = New System.Drawing.Point(0, 0)
Me.PictureBox1.Name = "PictureBox1"
Me.PictureBox1.Size = New System.Drawing.Size(184, 128)
Me.PictureBox1.TabIndex = 0
Me.PictureBox1.TabStop = False
'
'Label1
'
Me.Label1.Location = New System.Drawing.Point(8, 8)
Me.Label1.Name = "Label1"
Me.Label1.Size = New System.Drawing.Size(296, 24)
Me.Label1.TabIndex = 2
Me.Label1.Text = "Label1"
Me.Label1.TextAlign = System.Drawing.ContentAlig
'
'Timer1
'
Me.Timer1.Interval = 25
'
'NotifyIcon1
'
Me.NotifyIcon1.Text = "RubberBand Screen Capture Demo"
'
'Form1
'
Me.AutoScaleBaseSize = New System.Drawing.Size(5, 13)
Me.ClientSize = New System.Drawing.Size(312, 326)
Me.Controls.Add(Me.Label1)
Me.Controls.Add(Me.Panel1)
Me.Name = "Form1"
Me.Text = "RubberBand Screen Capture Demo"
Me.Panel1.ResumeLayout(Fal
Me.ResumeLayout(False)
End Sub
#End Region
Private Enum HotKeyModifiers
None = &H0
Alt = &H1
Control = &H2
Shift = &H4
Windows = &H8
End Enum
Private Const SRCCOPY = &HCC0020
Private Declare Function CreateDC Lib "gdi32" Alias "CreateDCA" (ByVal lpDriverName As String, ByVal lpDeviceName As String, ByVal lpOutput As String, ByVal lpInitData As String) As Integer
Private Declare Function CreateCompatibleDC Lib "GDI32" (ByVal hDC As Integer) As Integer
Private Declare Function CreateCompatibleBitmap Lib "GDI32" (ByVal hDC As Integer, ByVal nWidth As Integer, ByVal nHeight As Integer) As Integer
Private Declare Function SelectObject Lib "GDI32" (ByVal hDC As Integer, ByVal hObject As Integer) As Integer
Private Declare Function BitBlt Lib "GDI32" (ByVal hDestDC As Integer, ByVal X As Integer, ByVal Y As Integer, ByVal nWidth As Integer, ByVal nHeight As Integer, ByVal hSrcDC As Integer, ByVal SrcX As Integer, ByVal SrcY As Integer, ByVal Rop As Integer) As Integer
Private Declare Function DeleteObject Lib "GDI32" (ByVal hObj As Integer) As Integer
Private Declare Function DeleteDC Lib "GDI32" (ByVal hDC As Integer) As Integer
Private Declare Function RegisterHotKey Lib "user32" (ByVal hWnd As IntPtr, ByVal id As Integer, ByVal fsModifiers As Integer, ByVal vk As Integer) As Boolean
Private Declare Function UnregisterHotKey Lib "user32" (ByVal hWnd As IntPtr, ByVal id As Integer) As Boolean
Private Declare Function ExtractIcon Lib "shell32.dll" Alias "ExtractIconA" (ByVal hWnd As IntPtr, ByVal lpszExeFileName As String, ByVal nIconIndex As Long) As IntPtr
Private Declare Function GetSystemDirectory Lib "kernel32.dll" Alias "GetSystemDirectoryA" (ByVal lpBuffer As String, ByVal nSize As Long) As Integer
Private startX As Integer
Private startY As Integer
Private lastX As Integer
Private lastY As Integer
Private curX As Integer
Private curY As Integer
Private boxDrawn As Boolean = False
Private hotKeySet As Boolean = False
Private capturing As Boolean = False
Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Load
RegisterHotKey(HotKeyModif
If Not hotKeySet Then
Label1.Text = "Unable to Set Hotkey F9"
MsgBox("Unable to Set Hotkey F9")
Else
Label1.Text = "Press F9 to Start/Stop area selection"
End If
End Sub
Private Function GetScreen(ByVal X As Integer, ByVal Y As Integer, ByVal Width As Integer, ByVal Height As Integer) As Bitmap
Dim hDeskDC As Integer
Dim hTempDC As Integer
Dim hBitmap As Integer
Dim hTempBmp As Integer
Dim desktopArea As Bitmap
hDeskDC = CreateDC("DISPLAY", "", "", "")
If hDeskDC Then
hTempDC = CreateCompatibleDC(hDeskDC
If hTempDC Then
hBitmap = CreateCompatibleBitmap(hDe
If hBitmap Then
hTempBmp = SelectObject(hTempDC, hBitmap)
BitBlt(hTempDC, 0, 0, Width, Height, hDeskDC, X, Y, SRCCOPY)
desktopArea = Bitmap.FromHbitmap(New IntPtr(hBitmap))
DeleteObject(SelectObject(
End If
DeleteDC(hTempDC)
End If
DeleteDC(hDeskDC)
End If
Return desktopArea
End Function
Public Sub RegisterHotKey(ByVal mods As Integer, ByVal key As Integer)
If hotKeySet Then
UnregisterHotKey()
End If
hotKeySet = RegisterHotKey(Me.Handle, 0, mods, key)
End Sub
Public Sub UnRegisterHotkey()
If hotKeySet Then
hotKeySet = Not UnregisterHotKey(Me.Handle
End If
End Sub
Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Messa
Const WM_HOTKEY As Integer = &H312
Select Case m.Msg
Case WM_HOTKEY
hotKeyPressed()
End Select
MyBase.WndProc(m)
End Sub
Private Sub hotKeyPressed()
If Not capturing Then
capturing = True
boxDrawn = False
startX = Cursor.Position.X
startY = Cursor.Position.Y
lastX = startX
lastY = startY
Timer1.Enabled = True
Else
capturing = False
Timer1.Enabled = False
If boxDrawn Then
ControlPaint.DrawReversibl
PictureBox1.Image = GetScreen(Math.Min(startX,
PictureBox1.Width = PictureBox1.Image.Width
PictureBox1.Height = PictureBox1.Image.Height
End If
End If
End Sub
Private Sub Timer1_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Timer1.Tick
If capturing Then
curX = Cursor.Position.X
curY = Cursor.Position.Y
If curX <> lastX Or curY <> lastY Then
If Not boxDrawn Then
boxDrawn = True
ControlPaint.DrawReversibl
Else
ControlPaint.DrawReversibl
ControlPaint.DrawReversibl
End If
lastX = curX
lastY = curY
End If
End If
End Sub
Private Sub Form1_SizeChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.SizeChanged
If Me.WindowState = FormWindowState.Minimized Then
If Not (NotifyIcon1.Icon Is Nothing) Then
Me.Hide()
NotifyIcon1.Visible = True
End If
End If
End Sub
Private Sub NotifyIcon1_DoubleClick(By
NotifyIcon1.Visible = False
Me.Show()
Me.WindowState = FormWindowState.Normal
End Sub
Private Sub Form1_Closing(ByVal sender As Object, ByVal e As System.ComponentModel.Canc
UnregisterHotKey()
End Sub
End Class