MadeByVince
MadeByVince

Reputation: 85

Using PictureBox_Paint from ApplicationEvents.vb

I'm currently working on a small single instance application that is called by an external application using command line parameter. I'll try to be clear on that ;) So I have a running application that will execute that application I'm developing. This new application has different options based on a command line parameter and is single instance. So, every time the other application calls the new application it just updates the running instance based on that command line parameter (not easy to explain, I hope it's clear).

Everything is working fine but for one thing. The application Form uses a PictureBox to display an image. One of the command line parameter will fade that image into another one. To do this I'm using a PictureBox1_Paint Private Sub in the Form1.vb but I also need it in the ApplicationEvents.vb but I can't get it to work.

If I try the following code in the ApplicationEvents.vb, I'm getting the error message "BC30506 Handles clause requires a WithEvents variable defined in the containing type or one of its base types."

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

If I remove the "Handles PictureBox1.Paint", no more error message but no redraw of the PictureBox.

Any idea?

Thanks.

============================================================================

Hey, sorry for late reply but I've been quite busy :)

So, I've tried the solution below but can't get it to work... :(

Here is my code so far (with a lot of unneeded stuff removed) for Form1.vb and ApplicationEvents.vb Can you help me on how to apply the solution?

Form1.vb

Public Class Form1

    Dim TickTime As Integer = My.Settings.TransitionTime / 100
    Private _fadeOpacity As Single = 0
    Private CurrentImage As Bitmap
    Private NextImage As Bitmap

    Private Function FadeBitmap(ByVal bmp As Bitmap, ByVal opacity As Single) As Bitmap
        Dim bmp2 As New Bitmap(bmp.Width, bmp.Height, Imaging.PixelFormat.Format32bppArgb)
        opacity = Math.Max(0, Math.Min(opacity, 1.0F))
        Using ia As New Imaging.ImageAttributes
            Dim cm As New Imaging.ColorMatrix
            cm.Matrix33 = opacity
            ia.SetColorMatrix(cm)
            Dim destpoints() As PointF = {New Point(0, 0), New Point(bmp.Width, 0), New Point(0, bmp.Height)}
            Using g As Graphics = Graphics.FromImage(bmp2)
                g.DrawImage(bmp, destpoints,
                New RectangleF(Point.Empty, bmp.Size), GraphicsUnit.Pixel, ia)
            End Using
        End Using
        Return bmp2
    End Function

    Private Sub PictureBox1_Paint(ByVal sender As Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles PictureBox1.Paint
        If _fadeOpacity < 100 AndAlso CurrentImage IsNot Nothing Then
            e.Graphics.DrawImageUnscaled(CurrentImage, Point.Empty)
        End If
        If _fadeOpacity > 0 AndAlso NextImage IsNot Nothing Then
            Using fadedImage As Bitmap = FadeBitmap(NextImage, _fadeOpacity)
                e.Graphics.DrawImageUnscaled(fadedImage, Point.Empty)
            End Using
        End If
    End Sub

    Private Sub StartTransition(ImageName As String)
        ' In settings I have a parameter for the transition type, CUT or DISSOLVE. This sends the new image to load in the approriate function
        Select Case My.Settings.TransitionType
            Case 0
                Transition_CUT(ImageName)
            Case 1
                Transition_Dissolve(ImageName)
        End Select
    End Sub

    Private Sub Transition_CUT(ImageName As String)
        ' Replaced the image in PictureBox1
        CurrentImage = New Bitmap(Image.FromFile(ImageName), Me.PictureBox1.Size)
        Me.PictureBox1.Refresh()
    End Sub

    Private Sub Transition_Dissolve(ImageName As String)
        ' Fade between current image and new image over the defined time in My.Settings (in ms)
        NextImage = New Bitmap(Image.FromFile(ImageName), Me.PictureBox1.Size)
        'fade to newimage
        Dim TickTime As Integer = My.Settings.TransitionTime / 100
        For i = 0 To 1 Step 0.01
            _fadeOpacity = CSng(i)
            Me.PictureBox1.Invalidate()
            wait(TickTime)
        Next
        wait(100)
        CurrentImage = New Bitmap(Image.FromFile(ImageName), Me.PictureBox1.Size)
        _fadeOpacity = CSng(0)
        Me.PictureBox1.Invalidate()
    End Sub

    Private Sub wait(ByVal interval As Integer)
        ' custom timer
        Dim stopW As New Stopwatch
        stopW.Start()
        Do While stopW.ElapsedMilliseconds < interval
            ' Allows UI to remain responsive
            Application.DoEvents()
        Loop
        stopW.Stop()
    End Sub

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

        ' Load default image at startup
        CurrentImage = New Bitmap(Image.FromFile(My.Settings.DefaultBackgroundImage), Me.PictureBox1.Size)
        Me.PictureBox1.Refresh()

        ' For easy communication between form1 and ApplicationEvents, the command line argument has been passed to Label6
        If Label6.Text.StartsWith("/init") Then
            ' Initialize the application and load default background image from xml settings
            StartTransition(My.Settings.DefaultBackgroundImage)
        ElseIf Label6.Text.StartsWith("/load") Then
            ' Check existance and load the specified image
            Dim inputArgument As String = "/load="
            Dim inputName As String = ""
            If Label6.Text.StartsWith(inputArgument) Then
                inputName = Label6.Text.Remove(0, inputArgument.Length)
            End If
            If inputName = "" Then
                ' No image name specified, load the image not specified image
                StartTransition(My.Settings.ImageNotSpecified)
            Else
                If My.Computer.FileSystem.FileExists(My.Settings.ImagesFolder & inputName & ".jpg") Then
                    ' Image has been found and is displayed
                    StartTransition(My.Settings.ImagesFolder & inputName & ".jpg")
                Else
                    ' Image not found, displaying the not found message
                    StartTransition(My.Settings.ImageNotFound)
                End If
            End If
        ElseIf Label6.Text.StartsWith("/quit") Then
            ' Quit application
            Me.Close()
        Else
            ' No parameter sent, showing instructions
        End If
    End If

    End Sub

    Private Function argument() As String
        Throw New NotImplementedException
    End Function

End Class

ApplicationEvents.vb

Namespace My

    ' The following events are available for MyApplication:
    ' 
    ' Startup: Raised when the application starts, before the startup form is created.
    ' Shutdown: Raised after all application forms are closed.  This event is not raised if the application terminates abnormally.
    ' UnhandledException: Raised if the application encounters an unhandled exception.
    ' StartupNextInstance: Raised when launching a single-instance application and the application is already active. 
    ' NetworkAvailabilityChanged: Raised when the network connection is connected or disconnected.
    Partial Friend Class MyApplication

        Public Shared argument As String
        Private _fadeOpacity As Single = 0
        Private CurrentImage As Bitmap
        Private NextImage As Bitmap

        Private Sub StartTransition(ImageName As String)
            Select Case My.Settings.TransitionType
                Case 0
                    Transition_CUT(ImageName)
                Case 1
                    Transition_Dissolve(ImageName)
            End Select
        End Sub

        Private Sub PictureBox1_Paint(ByVal sender As Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles PictureBox1.Paint
            If _fadeOpacity < 100 AndAlso CurrentImage IsNot Nothing Then
                e.Graphics.DrawImageUnscaled(CurrentImage, Point.Empty)
            End If
            If _fadeOpacity > 0 AndAlso NextImage IsNot Nothing Then
                Using fadedImage As Bitmap = FadeBitmap(NextImage, _fadeOpacity)
                    e.Graphics.DrawImageUnscaled(fadedImage, Point.Empty)
                End Using
            End If
        End Sub

        Private Function FadeBitmap(ByVal bmp As Bitmap, ByVal opacity As Single) As Bitmap
            Dim bmp2 As New Bitmap(bmp.Width, bmp.Height, Imaging.PixelFormat.Format32bppArgb)
            opacity = Math.Max(0, Math.Min(opacity, 1.0F))
            Using ia As New Imaging.ImageAttributes
                Dim cm As New Imaging.ColorMatrix
                cm.Matrix33 = opacity
                ia.SetColorMatrix(cm)
                Dim destpoints() As PointF = {New Point(0, 0), New Point(bmp.Width, 0), New Point(0, bmp.Height)}
                Using g As Graphics = Graphics.FromImage(bmp2)
                    g.DrawImage(bmp, destpoints,
                New RectangleF(Point.Empty, bmp.Size), GraphicsUnit.Pixel, ia)
                End Using
            End Using
            Return bmp2
        End Function

        Private Sub Transition_CUT(ImageName As String)
            CurrentImage = New Bitmap(Image.FromFile(ImageName), Form1.PictureBox1.Size)
            _fadeOpacity = CSng(0)
            Form1.PictureBox1.Invalidate()
        End Sub

        Private Sub Transition_Dissolve(ImageName As String)
            NextImage = New Bitmap(Image.FromFile(ImageName), Form1.PictureBox1.Size)
            'fade to newimage
            Dim TickTime As Integer = CInt(Form1.TextBox_Time.Text) / 100
            For i = 0 To 1 Step 0.01
                _fadeOpacity = CSng(i)
                Form1.PictureBox1.Invalidate()
                wait(TickTime)
            Next
            wait(100)
            CurrentImage = New Bitmap(Image.FromFile(ImageName), Form1.PictureBox1.Size)
            _fadeOpacity = CSng(0)
            Form1.PictureBox1.Invalidate()
        End Sub

        Private Sub wait(ByVal interval As Integer)
            Dim stopW As New Stopwatch
            stopW.Start()
            Do While stopW.ElapsedMilliseconds < interval
                ' Allows your UI to remain responsive
                Application.DoEvents()
            Loop
            stopW.Stop()
        End Sub

        Private Sub MyApplication_Startup(
            ByVal sender As Object,
            ByVal e As Microsoft.VisualBasic.ApplicationServices.StartupEventArgs
        ) Handles Me.Startup

            ' Clean the command line parameter container (label6)
            Form1.Label6.Text = "none"

            For Each s As String In e.CommandLine
                Form1.Label6.Text = s.ToLower
            Next
        End Sub

        Private Sub MyApplication_StartupNextInstance(
            ByVal sender As Object,
            ByVal e As Microsoft.VisualBasic.ApplicationServices.StartupNextInstanceEventArgs
        ) Handles Me.StartupNextInstance

            For Each s As String In e.CommandLine
                If s.ToLower.StartsWith("/quit") Then
                    'Quit the application
                    Form1.Close()
                ElseIf s.ToLower.StartsWith("/init") Then
                    'Initialize the application and load default background image from xml settings
                    StartTransition(My.Settings.DefaultBackground)
                ElseIf s.ToLower.StartsWith("/load") Then
                    'Check existance and load the specified image
                    Dim inputArgument As String = "/load="
                    Dim inputName As String = ""
                    For Each i As String In e.CommandLine
                        If i.ToLower.StartsWith(inputArgument) Then
                            inputName = i.Remove(0, inputArgument.Length)
                        End If
                    Next
                    If inputName = "" Then
                        ' No image name specified, load the image not specified image
                        StartTransition(My.Settings.ImageNotSpecified)
                    Else
                        If My.Computer.FileSystem.FileExists(My.Settings.ImagesFolder & inputName & ".jpg") Or My.Computer.FileSystem.FileExists(My.Settings.ImagesFolder & inputName & ".swf") Then
                            ' Image has been found and is displayed
                            StartTransition(My.Settings.ImagesFolder & inputName & ".jpg")
                        Else
                            ' Image not found, displaying the not found message
                            StartTransition(My.Settings.ImageNotFound)
                        End If
                    End If
                ElseIf s.ToLower.StartsWith("") Then
                    'No parameter sent, showing instructions
                End If
            Next
        End Sub
    End Class
End Namespace

Thanks a lot and stay safe !

Upvotes: 0

Views: 71

Answers (1)

jmcilhinney
jmcilhinney

Reputation: 54457

The only place you handle the Paint event of the PictureBox is in the form containing that PictureBox. That event handler will be executed every time a Paint event is raised, so that is what you actually need to do from the other code file. In it's simplest form, that would look like this:

Private Sub MyApplication_StartupNextInstance(sender As Object, e As StartupNextInstanceEventArgs) Handles Me.StartupNextInstance
    MainForm.Refresh()
End Sub

In the MyApplication class, the MainForm property refers to your app's startup form object. By calling its Refresh method, you force it to repaint, which will also cause your PictureBox to repaint. Presumably you would want to do something involving the commandline argument(s) before making that call.

If you want to be a bit more efficient, you can force a repaint of the PictureBox without repainting the rest of the form. To do that, you would start by defining a method in the form that forced a repaint of the PictureBox:

Public Sub RefreshPictureBox()
    PictureBox1.Refresh()
End Sub

You would then call that method from the MyApplication class:

Private Sub MyApplication_StartupNextInstance(sender As Object, e As StartupNextInstanceEventArgs) Handles Me.StartupNextInstance
    DirectCast(MainForm, Form1).RefreshPictureBox()
End Sub

Note that, in this case, you have to cast as the actual type of the startup form because you're calling a member of that type. MainForm is type Form and Refresh is a member of Form, so that's fine without a cast.

It's also worth noting that you could call Invalidate rather than Refresh. If you call Invalidate then the Paint event will be raised when the UI thread is next free while, internally, Refresh calls Invalidate and then Update, to force an immediate repaint.

Upvotes: 1

Related Questions