0

Background: I'm new to vb, coming from javascript.

My assignment is to use the txtBox.LostFocus event to do some validation. The problem is I need to cancel the validation step if the user intends to press either two of three buttons.

Illustration:

Project Instructions and Outline

Code

Dim lblInputBox As Label
lblInputBox.Text = "Name:"

Dim txtInputBox As TextBox

Dim btnClear As Button
btnClear.Text = "Clear"

Dim btnSayName As Button
btnSayName.Text = "Say Name"

Dim btnExit As Button 
btnExit.Text = "Exit"

' Some boolean to determine what the next action is
Dim UserIntentDetected = False

' When the user moves focus away from the textbox
Private Sub txtInputBox_LostFocus(sender As Object, e As EventArgs) _
  Handles txtIputBox.LostFocus

  ' I want to be able to detect the next focus, but this is where I'm going wrong
  If btnExit.GotFocus Or btnClear.GotFocus Then
    UserIntentDetected = True
    Else
      UserIntentDetected = False
  End If

  ' Only call validate if there is no intent to quit or clear
  If Not UserIntentDetected Then
    Call validate()
  End If

  ' Reset the intent boolean
  UserIntentDetected = False
End Sub

' Validate subroutine
Private Sub validate()
  ' **Fixed description** 
  ' User moved focus away from txtbox and doesn't intend to clear or exit
  Console.WriteLine("You're NOT INTENDING to clear or exit")
End Sub

I've tried to add the two button's GotFocus event to the input box's LostFocus event handler, but there were bugs with the event firing multiple times in a loop.

Eg:

Private Sub txtInputBox_LostFocus(sender As Object, e As EventArgs) _
      Handles txtIputBox.LostFocus, btnExit.GotFocus, _
      btnClear.GotFocus
      ... (code follows)

These attempts are entirely wrong, but from a javascript background, although also entirely wrong, I could hack something like this..

... (assume same element structure from vb)
var eventQueue = [];
var userIntentDetected = false;

txtInputBox.addEventListener("blur", function(event){

  // Set a timeout to manually trigger the last event's click method in the eventQueue
  setTimeout(function(){
    if (eventQueue.length > 0 && userIntetDetected)
      runIntendedHandler(eventQueue[eventQueue.length -1]);
  }, 500);
})

// Both event listeners listen for click and stop default actions,
// set intent boolean to true, and add it's event object to a queue
btnExit.addEventListener("click", function(event){
  event.preventDefault();
  userIntentDetected = true;
  eventQueue.push(event);
});

btn.addEventListener("click", function(event){
  event.preventDefault();
  userIntentDetected = true;
  eventQueue.push(event);
});

// Validation should occur only if the user moves focus to an element 
// that IS NOT either the btnExit or btnClear
function runIntendedHandler(event){
  if (event.target.id = "btnExit")
    // run exit functions
    code...
  else if (event.target.id = "btnClear")
    // run clear functions
    code..

  userIntentDetected = false;
}

What is the proper way to work with events in vb and how would I go about detecting the next event in the queue before triggering an action? could the RaiseEvent statement help?

UPDATE 3: The answer was a lot easier than I made it seem. Apparently, you can use the btn.Focused property to check the next focus of an element from within the txtInputBox.LostFocus event handler... Go figure!

UPDATE 2: There's been a lot of confusion as to what exactly was needed, and a lot of that was my fault in describing the validation subroutine. I've changed some of the element names and added an image to sum up all of the information that was given to me by my instructor.

UPDATE 1: @TnTinMn has provided the closest working answer that can be used with a minor alteration. Example follows:

Private LastActiveControl As Control = Me   ' initialize on form creation

Protected Overrides Sub UpdateDefaultButton()
    ' Just added an IsNot condition to stay inline with the requirements
    If (LastActiveControl Is txtNumberOfDrinks) AndAlso
        ((ActiveControl Is btnClear) OrElse (ActiveControl Is btnExit)) Then

        Console.WriteLine("Your intent to press either btnClear or btnExit has been heard...")

    ' Validation happens only if the user didn't intend to click btnClear or btnExit 
    ElseIf (LastActiveControl Is txtNumberOfDrinks) AndAlso
    ((ActiveControl IsNot btnClear) OrElse (ActiveControl IsNot btnExit)) Then

        Console.WriteLine("You didn't press either btnClear or btnExit.. moving to validation")

        validateForm()
    End If

    LastActiveControl = ActiveControl   ' Store this for the next time Focus changes

End Sub

Thank you all!

4UmNinja
  • 510
  • 4
  • 14
  • Typically in winforms land, you _don't_ cancel validation. You make it happen so fast that the user isn't delayed, or you don't validate until the user actually attempts an action. Intent doesn't matter. – Joel Coehoorn Jan 21 '17 at 23:34
  • I'm not sure why the nature of this assignment is that way, but the directions were clear, so this is the rabbit hole I've dug into. – 4UmNinja Jan 21 '17 at 23:40
  • In the top declaration you are making 3 buttons yet trying to assign them as a String - turn on Option Strict on and quickly see the error. Typically these should be built in the designer. – OneFineDay Jan 21 '17 at 23:41
  • @OneFineDay, the example is mostly pseudo code. The elements have been placed on the form through the designer, so the example is just short forming what's going on. I'll try and be more explicit in the example. – 4UmNinja Jan 21 '17 at 23:44
  • `the example is mostly pseudo code` are you looking for a pseudo answer? – Ňɏssa Pøngjǣrdenlarp Jan 21 '17 at 23:47
  • i could copy the entire code here, but i'm not sure that's recommended. any valid, helpful direction whether pseudo or direct would be great @Plutonix – 4UmNinja Jan 21 '17 at 23:51

2 Answers2

1

Winform's event order is confusing at best. For a summary, see Order of Events in Windows Forms.

Assuming I have interpreted your goal correctly, I would not respond to the txtInputBox.LostFocus event but rather override a little known Form method called UpdateDefaultButton. This method is called when the Form.ActiveControl property changes and effectively gives you an ActiveControl changed pseudo-event. If the newly ActiveControl is a Button, this method also executes before the Button.Click event is raised.

Since you want to call your validation code only when the focus changes from txtInputBox to either btnPromptForName or btnExit, you can accomplish that with something like this.

Public Class Form1

    Private LastActiveControl As Control = Me   ' initialize on form creation

    Protected Overrides Sub UpdateDefaultButton()
        If (LastActiveControl Is tbInputBox) AndAlso
            ((ActiveControl Is btnPromptForName) OrElse (ActiveControl Is btnExit)) Then
            ValidateInput()
        End If

        LastActiveControl = ActiveControl   ' Store this for the next time Focus changes

    End Sub

    Private Sub ValidateInput()
      Console.WriteLine("You're either intending to prompt for name or exit")
    End Sub

    Private Sub btnPromptForName_Click(sender As Object, e As EventArgs) Handles btnPromptForName.Click
        Console.WriteLine("btnPromptForName_Click clicked")
    End Sub

    Private Sub btnExit_Click(sender As Object, e As EventArgs) Handles btnExit.Click
        Console.WriteLine("btnExit clicked")
    End Sub

End Class

Edit: I forgot to mention, that since UpdateDefaultButton runs before the Button.Click event is raised, it is possible wire-up the click event handler only if validation succeeds. You would then remove the event handler as part of the Button.Click handler code.

TnTinMn
  • 11,522
  • 3
  • 18
  • 39
  • This is near exactly the solution I'm looking for with a [minor change](https://gist.github.com/phreelyfe/b2850d0cc6637a5289bfffc9d22c7832). Also when I run this, when tabbing away from the textbox, it tries to validate twice. – 4UmNinja Jan 22 '17 at 05:29
  • 1
    I have no idea why you are seeing the validation code run twice. UpdateDefaultButton runs only once on the change of the ActiveControl and is the entire premise this approach. Your linked coded shows a second condition that is the opposite of the intent I inferred from your question. Since many others had problems understanding your intent, I am not surprised by that. If this technique solves your problem, consider marking it as the answer so that others do not waste their time trying to solve your issue when you have a satisfactory solution. – TnTinMn Jan 22 '17 at 14:46
  • thanks for your reply. I'm sorry the directions were unclear, but i tried to be as clear as possible. the original post does mention that i need to cancel the validation upon the user clicking either of two buttons, so not sure where the difficulty comes from. The reason I was also waiting for another answer is originally I asked for a solution that uses txtBox.LostFocus to do exactly what you provided, but your solution does work very well. With the example pseudo code I thought that would be clear enough, but I don't know enough about this language to put in words exactly what I want. thx! – 4UmNinja Jan 22 '17 at 18:10
0

It looks like the solution was a lot easier than I thought. The intended (instructor's) way to do this is by checking for alternate "btn.Focused" properties within the txtInputBox.LostFocus event handler eg:

' When the user moves focus away from the textbox
Private Sub txtInputBox_LostFocus(sender As Object, e As EventArgs) _
  Handles txtIputBox.LostFocus

    ' Check if the clear or exit button is focused before validating
    ' Validate only if btnClear and also btnExit IS NOT focused

    If Not btnExit.Focused AndAlso Not btnClear.Focused Then
        Call validateForm()
    End If
End Sub

Thank you @TnTinMn for the UpdateDefaultButton and showing me an alternate way to track events. This was very helpful too.

Community
  • 1
  • 1
4UmNinja
  • 510
  • 4
  • 14