Mattia
Mattia

Reputation: 258

Create a new Form and add event handlers at Run Time

I'm trying to create a new border-less Form at Run-Time and the Event Handlers required to move the Form without its Caption.

The Form is meant to be a Dialog that can show rendered QR Codes, using a specialized Control.
The Form should also resize itself to fit different sizes of QR Codes.

This is what I wrote so far:

Imports System.Drawing
Imports System.Windows.Forms

Public Class Form1

    Dim schermataqrcode As New Form

    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        schermataqrcode.FormBorderStyle = FormBorderStyle.None
        AddHandler schermataqrcode.MouseDown, AddressOf schermataqrcode.MouseDown
        AddHandler schermataqrcode.MouseUp, AddressOf schermataqrcode.MouseUp
        AddHandler schermataqrcode.MouseMove, AddressOf schermataqrcode.MouseMove

        Dim qrcode As New qrcode
        qrcode.Dock = DockStyle.Fill
        schermataqrcode.Controls.Add(qrcode)
        schermataqrcode.StartPosition = FormStartPosition.CenterScreen
        schermataqrcode.Show()
    End Sub

    Public Class schermatacode
        Private IsDraggingForm As Boolean = False
        Private MousePos As New System.Drawing.Point(0, 0)

        Private Sub schermataqrcode_MouseDown(ByVal sender As Object, ByVal e As MouseEventArgs)
            If e.Button = MouseButtons.Left Then
                IsDraggingForm = True
                MousePos = e.Location
            End If
        End Sub

        Private Sub schermataqrcode_MouseUp(ByVal sender As Object, ByVal e As MouseEventArgs)
            If e.Button = MouseButtons.Left Then IsDraggingForm = False
        End Sub

        Private Sub schermataqrcode_MouseMove(ByVal sender As Object, ByVal e As MouseEventArgs)
            If IsDraggingForm Then
                Dim temp As Point = New Point(Me.Location + (e.Location - MousePos))
                Me.Location = temp
                temp = Nothing
            End If
        End Sub
    End Class
End Class

I'm not sure about that as VS errors:

 BC30577    'AddressOf' operand must be the name of a method (without parentheses).
 BC30577    'AddressOf' operand must be the name of a method (without parentheses).
 BC30577    'AddressOf' operand must be the name of a method (without parentheses).
 BC30456    'Location' is not a member of 'Form1.schermatacode'.
 BC30456    'Location' is not a member of 'Form1.schermatacode'.

Upvotes: 1

Views: 277

Answers (1)

Jimi
Jimi

Reputation: 32223

Problems in the current code:

  • Option Strict is Off

  • The schermataqrcode Form's events are supposed to be handled by the schermatacode class object, but the assignment doesn't point to the schermatacode class at all:

    AddHandler schermataqrcode.MouseDown, AddressOf schermataqrcode.MouseDown 
    ' should be:
    AddHandler schermataqrcode.MouseDown, AddressOf schermatacode.schermataqrcode_MouseDown
    

    but you did not create an instance of this class (which is not static and cannot be) and in any case the event handlers are declared Private, so you cannot reach them anyway.

  • Even if the event handlers were setup correctly, the Form's ClientArea is completely filled with a docked Control, so it won't receive mouse events.

  • This: Dim temp As Point = New Point(Me.Location + (e.Location - MousePos)) cannot be used: - Me is not the Form, it's the schermatacode class, which doesn't have a Location property.
    - Option Strict Off hides from you that Me.Location + (e.Location - MousePos) doesn't return a usable Point. See what Point = Point + (Point - Point) actually is when you set Option Strict On.

Here's something you can do:

  • Move all the logic in the SchermataCode class object: you're simply creating a standard Form that contains a single Control (based on the description, but you can add all the controls you want). This class handles the creation of the Form and the QR Control and also sets up all the event handlers needed.

  • Using a single class object for everything, you can just initialize the class and use it whenever you need it simply with, e.g., Dim qrForm = New SchermataCode().

  • The SchermataCode object can then expose public methods and properties that allow to use its functionalities simply calling a method or inspecting a property, as usually happens with the .Net classes.
    In the sample code, SchermataCode exposes a ShowForm() public method that shows the Form as a Dialog. You can pass to this method the string to render as a QR Image. This way, you can use the same class to show more than one Form, you can also use the same class object for the life of your Application.

► It's important to notice that SchermataCode also expose a Dispose() method: this method allows to dispose of the Form, the QR Control and remove all the handlers added to these controls. It's very important that you call this method - as usual in .Net - to release the resources that these objects are allocating when you don't need SchermataCode anymore.
Of course, you can create another instance after, if needed.


NOTE: Since the QR Control occupies the whole ClientArea of the Form, the border-less Form movement is handled by the QR Code Control, using its MouseDown and MouseMove events instead of those of the Form (since the Form won't receive mouse events).
I'm not sure whether the QR Control has some specific functionality related to these events (if you click it, something happens. Let me know if that's the case).
Also, double clicking the QR Control, causes the Form to close.


For example, create a SchermataCode instance and show two Forms in sequence, assigning the Images that ShowForm() returns to a local variable in one case and to a PictureBox control in the other (I'm not sure whether the QR Control returns the rendered Image, or even if you need it, it's meant to show how this class works).

Dim qrForm = New SchermataCode()

' Shown the Dialog using the default Size 
Dim result As Image = qrForm.ShowForm("<Some QR String>")

qrForm.QRImageSize = New Size(200, 200)
PictureBox1.Image = qrForm.ShowForm("<Some Other QR String>")
qrForm.Dispose()

The QRImageSize public Property allow to change the Size of the QR Control before showing a new Dialog. The default Size is (500, 500).


Modified SchermataCode class:

Public Class SchermataCode
    Implements IDisposable

    Private qrForm As Form = Nothing
    Private qr As qrcode = Nothing
    Private startPosition As Point = Point.Empty

    Public Sub New()
        qrForm = New Form() With {
            .FormBorderStyle = FormBorderStyle.None,
            .StartPosition = FormStartPosition.CenterScreen
        }

        qr = New qrcode() With {.Size = QRImageSize}
        AddHandler qr.MouseDown, AddressOf qrCode_MouseDown
        AddHandler qr.MouseMove, AddressOf qrCode_MouseMove
        AddHandler qr.DoubleClick, AddressOf qrCode_OnDoubleClick
        qrForm.Controls.Add(qr)
    End Sub

    Public Property QRImageSize As Size = New Size(500, 500)

    Public Function ShowForm(qrString As String) As Image
        If QRImageSize <> qr.Size Then qr.Size = QRImageSize
        qrForm.ClientSize = qr.Size
        qr.Text = qrString
        qrForm.ShowDialog()
        Return qr.Image
    End Function


    Private Sub qrCode_MouseDown(ByVal sender As Object, ByVal e As MouseEventArgs)
        If e.Button = MouseButtons.Left Then
            startPosition = e.Location
        End If
    End Sub

    Private Sub qrCode_MouseMove(ByVal sender As Object, ByVal e As MouseEventArgs)
        If e.Button = MouseButtons.Left Then
            qrForm.Location = New Point(
                qrForm.Left + (e.X - startPosition.X),
                qrForm.Top + (e.Y - startPosition.Y))
        End If
    End Sub

    Private Sub qrCode_OnDoubleClick(ByVal sender As Object, ByVal e As EventArgs)
        qrForm.Close()
    End Sub

    Public Sub Dispose() Implements IDisposable.Dispose
        Dispose(True)
        GC.SuppressFinalize(Me)
    End Sub

    Protected Overridable Sub Dispose(disposing As Boolean)
        If disposing Then
            If qr IsNot Nothing Then
                RemoveHandler qr.MouseDown, AddressOf qrCode_MouseDown
                RemoveHandler qr.MouseMove, AddressOf qrCode_MouseMove
                RemoveHandler qr.DoubleClick, AddressOf qrCode_OnDoubleClick
            End If
            qr?.Dispose()
            qrForm?.Dispose()
        End If
    End Sub
End Class

The null conditional operator is used here to avoid checking for null when disposing of some objects (e.g, qrForm?.Dispose()).

If your VB.Net version doesn't allow it, change it in, e.g:

If qrForm IsNot Nothing Then qrForm.Dispose()

Upvotes: 1

Related Questions