ShutterShocked
ShutterShocked

Reputation: 3

How to un-highlight a button after you have pressed it in vb.net?

For my Major Work project for my last year of school, I am creating a Space Invaders game, and I am making it on VB.NET because it is what I am most comfortable with. This is going to be a very long-winded question because there are a lot of factors that may or may not be contributing to this issue I'm having. I have made the shooting system like so:

Private Sub SpaceInvaders_KeyPress(sender As Object, e As KeyPressEventArgs) Handles Me.KeyPress
    If fired = False Then
        If e.KeyChar = "W" Or e.KeyChar = "w" Then
            shotFired = True
            fired = True
        End If
    End If
End Sub

I use this code block to register if the player is attempting to shoot. I would have put this in a KeyDown command, but that allows the player to shoot continuously which is very broken and makes it much harder for me to make a balanced game. shotFired shows that the player has shot this round, and fired makes sure that you can't shoot again until you have taken your finger off of the W key.

Private Sub SpaceInvaders_KeyUp(sender As Object, e As KeyEventArgs) Handles Me.KeyUp
    Select Case e.KeyCode
        Case Keys.W
            fired = False
    End Select
End Sub

All this is intended to do is make sure that the player can fire a shot again once they have released the W key.

Private Sub tmrMovement_Tick(sender As Object, e As EventArgs) Handles tmrMovement.Tick

    If shotFired = True Then
        My.Computer.Audio.Play(My.Resources.Laser_Shoot,
        AudioPlayMode.Background)
        shotFired = False
        bullet(bulletCount).Left = pbPlayer.Left + 25
        bullet(bulletCount).Top = pbPlayer.Top - 25
        bullet(bulletCount).Visible = True
        bulletCount += 1
        If bulletCount = bullet.Length - 1 Then
            bulletCount = 0
        End If
    End If

    For i As Integer = 0 To bullet.Length - 1
        bullet(i).Top -= 5
        If bullet(i).Top < -50 Then
            bullet(i).Visible = False
            bullet(i).Top = Me.Height
        End If
    Next

For my shooting, which is the area in my program that is having the trouble currently, I am making it so that there is an audio queue that plays first, and whilst that continues to play in the background, the shotFired value will immediately reset to false, making it so it doesn't shoot multiple bullets in a short amount of ticks. The way that my bullets work is that they are in an array, currently set at 20. I shoot and my array will move one number further across the line, making sure that the last bullet fired doesn't keep moving back to the same position when I shoot, and I can multiple bullets on the screen at a time. I keep track of this with the bulletCount digit, and once it equals the max amount of bullets in my array, it resets, making it go back to bullet(0), resetting and making sure I don't have to make a new bullet every single time I want to shoot.

The way I make them move is that every tick that they are active, it goes up the form by 5. If it goes above -50, it disappears and goes back to its starting position. It will not affect collision, however, as collision first takes into account if both variables are visible or not.

Regardless, once I created a menu for my game, an issue arose. It's a simple menu, with only a label with the title of the game, and a button to start the first level. Once I start the first level, though, I cannot shoot. This is the code for when the start button is clicked:

Private Sub btnBegin_Click(sender As Object, e As EventArgs) Handles btnBegin.Click
    Me.KeyPreview = True
    lblTitle.Visible = False
    btnBegin.Visible = False
    pbPlayer.Visible = True
    pbGuard1.Visible = True
    pbGuard2.Visible = True
    pbGuard3.Visible = True
    btnBegin.Enabled = False

    If level = 1 Then
        enemy(0).Visible = True
        enemy(0).Left = 234
        enemy(1).Visible = True
        enemy(1).Left = 193
        tmrLevel1.Enabled = True
    End If
End Sub

I need to make the button's enabled value false, as if I keep it as true, it will still technically be highlighted by my game, making it so if the player hits the spacebar, the game will respawn all of the enemies and go back to the start of level 1. This becomes a massive issue, however, because once I disable the button, my player character cannot shoot. I am extremely unsure of what the issue is here, and I can see no correlation between the two values. I have tried to find ways to un-highlight the button, so I don't have to disable it and can therefore still shoot, but I am not sure how I could do it at this moment.

How could I un-highlight the button once the level starts? Is there a certain value I need to disable? And if possible, does anyone know what the correlation here between the button and the ability to shoot is, and how I could fix that? Any help would be greatly appreciated.

Upvotes: 0

Views: 111

Answers (1)

jmcilhinney
jmcilhinney

Reputation: 54457

When you click a Button, it receives the input focus. If you want to remove that focus then you can either focus a different control by calling its Select method or not focus any control at all by setting the form's ActiveControl property to Nothing.

If you're felling adventurous then you can actually create a custom Button control that won't receive focus in the first place. Here is a class I wrote almost 14 years ago to do just that:

''' <summary>
''' A button control that cannot receive focus.
''' </summary>
Public Class UnselectableButton
    Inherits System.Windows.Forms.Button
 
#Region " Constructors "
 
    ''' <summary>
    ''' Initialises a new instance of the <see cref="UnselectableButton"/> class.
    ''' </summary>
    Public Sub New()
        MyBase.New()
 
        'The button cannot receive focus.
        Me.SetStyle(ControlStyles.Selectable, False)
    End Sub
 
#End Region 'Constructors
 
#Region " Variables "
 
    ''' <summary>
    ''' Corresponds to the MA_NOACTIVATE window process reply.
    ''' </summary>
    Private Shared ReadOnly noActivate As New IntPtr(NativeConstants.MA_NOACTIVATE)
 
#End Region 'Variables
 
#Region " Methods "
 
    ''' <summary>
    ''' Processes Windows messages.
    ''' </summary>
    ''' <remarks>
    ''' Informs Windows not to activate the window when it is clicked with the mouse.  Note that this behaviour applies to the client area of the window only.
    ''' </remarks>
    Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)
        MyBase.WndProc(m)
 
        If m.Msg = NativeConstants.WM_MOUSEACTIVATE Then
            'Tell Windows not to activate the window on a mouse click.
            m.Result = noActivate
        End If
    End Sub
 
    Protected Overrides Sub OnClick(ByVal e As System.EventArgs)
        MyBase.OnClick(e)
    End Sub
 
#End Region 'Methods
 
End Class

If you add that class to your project and build, the custom control will appear at the top of the Toolbox and you can use it instead of a regular Button.

Here's a class that includes the constants used above:

''' <summary>
''' Contains Win32 constant declarations.
''' </summary>
Friend NotInheritable Class NativeConstants
 
    ''' <summary>
    ''' Specifies that a window cannot be activated.
    ''' </summary>
    ''' <remarks>
    ''' If a child window has this style, clicking it does not cause its top-level parent to activate. Although a window that has this style will still receive mouse events, neither it nor its child windows can get the focus.
    ''' </remarks>
    Public Const WS_EX_NOACTIVATE As Integer = &H8000000
 
    ''' <summary>
    ''' This message is sent when the cursor is in an inactive window and the user presses a mouse button.
    ''' </summary>
    Public Const WM_MOUSEACTIVATE As Integer = &H21
 
    ''' <summary>
    ''' Does not activate the window, and does not discard the mouse message.
    ''' </summary>
    ''' <remarks>
    ''' This is one of the possible return values when the <see cref="WM_MOUSEACTIVATE"/> notification is received.
    ''' </remarks>
    Public Const MA_NOACTIVATE As Integer = 3
 
    ''' <summary>
    ''' Displays a window in its most recent size and position. This value is similar to <b>SW_SHOWNORMAL</b>, except the window is not actived.
    ''' </summary>
    ''' <remarks>
    ''' This is one of the possible values for the <b>nCmdShow</b> argument of the <see cref="NativeMethods.ShowWindow">ShowWindow</see> method.
    ''' </remarks>
    Public Const SW_SHOWNOACTIVATE As Integer = 4
 
End Class

You can use that class as is, remove the constants you don't need or just copy the constants you do need to the UnselectableButton class. My original code included a number of other classes that made use of those constants.

Upvotes: 1

Related Questions