Overview


When posting images online, either in forums or in a presentation such as this, sometimes you want to highlight or draw the reader’s attention to part of the image. Recently I’ve needed to do this more often which led me to write this. It’s an arrow pointer designer which saves the partially transparent arrow pointer image to a file on your desktop (it’s always 000.png, so it’s easy to find in Explorer).



Figure 1. Designer



This enables you to create uniform custom arrow pointers with choices of size, orientation, direction, color, and optional text labels in either black or white text. After a few clicks and some typing you can press F2 and the arrow pointer will be saved onto your desktop.




Figure 2. Paste From (file)


 

To insert the partially transparent arrow pointer into your screenshot in Paint you first need to set Selection Options
àTransparent Selection and then use Paste From and navigate to 000.png (see Figure 2).




The Code

 



These are the API functions and the Constant used to catch the F2 key being pressed.

clipRect is the Rectangle used when copying the image from the screen.


Private Declare Function RegisterHotKey Lib "user32" (ByVal hwnd As IntPtr, ByVal id As Integer, ByVal fsModifiers As Integer, ByVal vk As Integer) As Integer

 

Private Declare Function UnregisterHotKey Lib "user32" (ByVal hwnd As IntPtr, ByVal id As Integer) As Integer

 

Private Const WM_HOTKEY As Integer = &H312

 

Dim clipRect As Rectangle

 



In the Form Load event, the PictureBox Font is set, the ComboBoxes are loaded with options, and the HotKey is setup.

Private Sub Form1_FormClosed(ByVal sender As Object, ByVal e As System.Windows.Forms.FormClosedEventArgs) Handles Me.FormClosed

    UnregisterHotKey(Me.Handle, 0)

End Sub

 

Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load

    PictureBox1.Font = New Font(PictureBox1.Font.Name, 12)

    ComboBox1.DataSource = New String() {"Horizontal", "Vertical", "Diagonal /", "Diagonal \"}

    ComboBox2.DataSource = New String() {"Red", "LimeGreen", "LightBlue", "Yellow"}

    ComboBox3.DataSource = New String() {"Black", "White"}

    RegisterHotKey(Me.Handle, 0, 0, Keys.F2)

End Sub



This is where most of the code takes place. This looks quite complex, and it is. Essentially it is just calculating the size and rotation of the arrow pointer and the text label, and drawing it on the picturebox, using the user specified options. Also clipRect is set, which contains the smallest possible bounding rectangle for copying and saving the image to file.

Private Sub PictureBox1_Paint(ByVal sender As Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles PictureBox1.Paint

    Dim leftOfCentre As Integer = 10 - TrackBar1.Value

    Dim rightOfCentre As Integer = TrackBar2.Value

    Dim aboveCentre As Integer = TrackBar4.Value

    Dim belowCentre As Integer = 10 - TrackBar3.Value

 

    Dim length As Integer = (13 + (20 * leftOfCentre)) + (13 + (20 * rightOfCentre))

    Dim height As Integer = (13 + (15 * aboveCentre)) + (13 + (15 * belowCentre))

 

    Dim sf As New StringFormat(Drawing.StringFormat.GenericTypographic)

    sf.Alignment = StringAlignment.Center

    sf.LineAlignment = StringAlignment.Center

 

    Dim arrowHeight As Integer

    Dim arrowWidth As Integer

    Dim shaftWidth As Integer

    Dim shaftLength As Integer

 

    Dim diameter As Integer = -1

    Dim clipSize As Size

    Dim angle As Integer

 

    Dim rf As RotateFlipType = RotateFlipType.RotateNoneFlipNone

 

    Select Case ComboBox1.SelectedIndex

        Case 0

            angle = If(CheckBox1.Checked, 270, 90)

            rf = If(CheckBox1.Checked, RotateFlipType.Rotate90FlipNone, RotateFlipType.Rotate270FlipNone)

            arrowHeight = CInt(length * 0.45)

            arrowWidth = height

            shaftWidth = height \ 2

            shaftLength = CInt(length * 0.55)

            clipSize = New Size(shaftLength + arrowHeight, arrowWidth)

        Case 1

            angle = If(CheckBox1.Checked, 0, 180)

            rf = If(Not CheckBox1.Checked, RotateFlipType.Rotate90FlipNone, RotateFlipType.Rotate270FlipNone)

            arrowHeight = CInt(height * 0.45)

            arrowWidth = length

            shaftWidth = length \ 2

            shaftLength = CInt(height * 0.55)

            clipSize = New Size(arrowWidth, shaftLength + arrowHeight)

        Case 2

            angle = If(Not CheckBox1.Checked, 45, 225)

            rf = If(Not CheckBox1.Checked, RotateFlipType.Rotate270FlipNone, RotateFlipType.Rotate90FlipNone)

            arrowHeight = CInt(height * 0.45)

            arrowWidth = length

            shaftWidth = length \ 2

            shaftLength = CInt(height * 0.55)

            diameter = Math.Max(arrowWidth, shaftLength + arrowHeight)

        Case 3

            angle = If(CheckBox1.Checked, 315, 135)

            rf = If(CheckBox1.Checked, RotateFlipType.Rotate90FlipNone, RotateFlipType.Rotate270FlipNone)

            arrowHeight = CInt(height * 0.45)

            arrowWidth = length

            shaftWidth = length \ 2

            shaftLength = CInt(height * 0.55)

            diameter = Math.Max(arrowWidth, shaftLength + arrowHeight)

    End Select

 

    If diameter = -1 Then

        clipRect = New Rectangle(225 - (clipSize.Width \ 2), 175 - (clipSize.Height \ 2), clipSize.Width, clipSize.Height)

    Else

        clipRect = New Rectangle(225 - (diameter \ 2), 175 - (diameter \ 2), diameter, diameter)

    End If

 

    e.Graphics.TranslateTransform(225, 175)

    e.Graphics.RotateTransform(angle)

 

    Dim points() As Point = {New Point(-(arrowWidth \ 2), -CInt((arrowHeight + shaftLength) * 0.05)), New Point(0, -((arrowHeight + shaftLength) \ 2)), New Point((arrowWidth \ 2), -CInt((arrowHeight + shaftLength) * 0.05))}

    Dim r As New Rectangle(-(shaftWidth \ 2), -CInt((arrowHeight + shaftLength) * 0.05 + 5), shaftWidth, shaftLength)

    e.Graphics.FillRectangle(New SolidBrush(Color.FromName(ComboBox2.Text)), r)

    e.Graphics.FillPolygon(New SolidBrush(Color.FromName(ComboBox2.Text)), points)

 

    If TextBox1.Text.Trim <> "" Then

        Dim r2 As New Rectangle(-(shaftLength \ 2 + 10), -(shaftWidth \ 2 - 2), shaftLength - 10, shaftWidth - 4)

        Dim ts As SizeF = e.Graphics.MeasureString(TextBox1.Text.Trim, PictureBox1.Font, r2.Size, sf)

        Dim textImage As New Bitmap(r2.Width, r2.Height)

        Dim gr As Graphics = Graphics.FromImage(textImage)

        gr.Clear(Color.FromName(ComboBox2.Text))

 

        gr.DrawString(TextBox1.Text.Trim, PictureBox1.Font, New SolidBrush(Color.FromName(ComboBox3.Text)), New Rectangle(Point.Empty, r2.Size), sf)

        textImage.RotateFlip(rf)

 

        e.Graphics.DrawImage(textImage, r.Location)

    End If

 

End Sub



This forces a repaint after a design value is changed, which changes the arrow pointer drawn in the PictureBox.


Private
Sub allControls_ValueChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles TrackBar4.ValueChanged, TrackBar3.ValueChanged, TrackBar2.ValueChanged, TrackBar1.ValueChanged, ComboBox3.SelectedIndexChanged, ComboBox2.SelectedIndexChanged, ComboBox1.SelectedIndexChanged, TextBox1.TextChanged, CheckBox1.CheckedChanged

    PictureBox1.Invalidate()

End Sub

 



This is where the pressed HotKey is processed. Here the image of the arrow pointer is copied from the picturebox, transparency applied, and the image is saved to your desktop.


Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)

    If m.Msg = WM_HOTKEY AndAlso m.WParam.ToInt32 = 0 Then

        'hotkey pressed

 

        Me.BringToFront()

 

        Dim img As New Bitmap(clipRect.Width, clipRect.Height)

        Dim gr As Graphics = Graphics.FromImage(img)

        gr.Clear(SystemColors.Control)

        Dim p As Point = PictureBox1.PointToScreen(Point.Empty)

 

        p.Offset(New Point(clipRect.Left, clipRect.Top))

        gr.CopyFromScreen(p, Point.Empty, img.Size)

 

        img.MakeTransparent(SystemColors.Control)

  img.Save(IO.Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "000.png"), Drawing.Imaging.ImageFormat.Png)               

 

    End If

    MyBase.WndProc(m)

End Sub



Download

Download project here...


See Also

​Image balloonTips