Winforms button click mousedown/mouseup - vb.net

I experienced some strange thing in someone else's application that I am making myself familiar with: A button that on a click starts or stops a plc (TwinCat from Beckhoff) like this:
(Just a remark: the hbool1 variable is just an integer handle so that the plc knows which internal variable I want to change: here: switch on/off)
Private Sub Button1_MouseDown(sender As Object, e As MouseEventArgs) Handles Button1.MouseDown
'Switch off
adsClient.WriteAny(hbool1, False)
End Sub
Private Sub Button1_MouseUp(sender As Object, e As MouseEventArgs) Handles Button1.MouseUp
'Switch on
adsClient.WriteAny(hbool1, True)
End Sub
I wasn't familiar with MouseUp/MouseDowns on a button and expected just a Click.
So out of curiosity I tried it with the Click Event:
Dim state_isOn As Bool
Private Sub Button1_Click(sender As Object, e As MouseEventArgs) Handles Button1.Click
If state_isOn Then
'Switch off
adsClient.WriteAny(hbool1, False)
Else
'Switch on
adsClient.WriteAny(hbool1, True)
End If
End Sub
The variable state_isOn is set to true or false when the plc responds with an answer.
I do not understand 2 things:
Why does the 1st code snippet work? On the first click it switches on, on the next click it switches off. As far as I see, both events are called in a row: first mouseDown, afterwards mouseUp. So the Plc should be switched on always, but I can switch it off as well.
Why does my own idea not work? Isn't it more logical?!
What I did not try yet is to check the MouseClick Event. Then perhaps my own idea would work, but still there is the question, why the 1st code does work at all.
EDIT:
I figured out another thing:
Consider the first snippet, how it was done initially (working): I put 2 message boxes in the code and noticed a different behaviour: With the message boxes, it does not work any more. When I click the button, I only get to the point showing "Switch off", and no following "Switch on". While this might be related to the interrupting nature of a message box, still it is interesting.
Private Sub Button1_MouseDown(sender As Object, e As MouseEventArgs) Handles Button1.MouseDown
'Switch off
adsClient.WriteAny(hbool1, False)
MsgBox("Switch off")
End Sub
Private Sub Button1_MouseUp(sender As Object, e As MouseEventArgs) Handles Button1.MouseUp
'Switch on
adsClient.WriteAny(hbool1, True)
MsgBox("Switch on")
End Sub
Another note: With the MouseClick-event it does not work, either.
I have the feeling there is something wrong in the code of the plc and the former author figured out this mousebuttonup/down hack somehow to make it work, instead of fixing the plc. Is this possible?

To really start or stop the plc with the TcAdsClient you should use the WriteControl Methods available like:
tcClient.WriteControl(newStateInfo(AdsState.Run,tcClient.ReadState().DeviceState));
tcClient.WriteControl(newStateInfo(AdsState.Stop,tcClient.ReadState().DeviceState));
You are probably not really stopping the PLC. Instead you are just writing to a BOOL variable on your plc, which happens to change the control flow of your PLC program. It is often normal programming style on a PLC that you will trigger an action whith a rising or falling edge on a BOOL variable. To create this edge it makes perfect sense to use the MouseUp/MouseDown event. But there is the possibility that when you click too fast that the rising edge is not recognized. This can happen if the variable gets updated to FALSE and then TRUE in the same PLC cycle. You should probably use just the ClickEvent to set to FALSE and trigger a timer to set the variable to TRUE again so that you ensure that you are not in the same PLC cycle.

Related

Disable password TextBox Caps-Lock is On warning

I am creating a login thing and I have this problem that every time I click on this "Show Password" Button and the Caps-Lock is activated, a Warning pops up and won't leave (at least I think it won't, which for the end-user would be even worse)
I would like to get rid of this warning completely.
Before redirecting my question to this question:
How to disable system's caps-lock notification on Textbox
I have already tried that.
Private Sub ShowPassword_MouseDown(sender As Object, e As MouseEventArgs) Handles ShowPassword.MouseDown
If txt_Password.Text <> "" Then
txt_Password.UseSystemPasswordChar = False
End If
End Sub
Private Sub ShowPassword_MouseUp(sender As Object, e As MouseEventArgs) Handles ShowPassword.MouseUp
If txt_Password.Text <> "" Then
txt_Password.UseSystemPasswordChar = True
End If
End Sub
The warning should disappear.
Hope this question can help other people other than just me.
Edit: Thanks Jimi. :D
When you do this:
[TextBox].UseSystemPasswordChar = [Bool Value]
the handle of the control is recreated each time (.Net source code)
The baloon tooltip will be shown for the new control handle, as soon as the TextBox.Text value changes: the tooltips will pile up.
A simple workaround - since disabling the warning may not be a good choice here - is to set the PasswordChar property to Char.MinValue or Nothing in the MouseDown handler of your Show Password Button, then set it back to the previous value on MouseUp. This won't recreate the handle (.Net Source code) and the balloon tooltip can be disabled pressing the Caps-Lock key.
Set UseSystemPasswordChar to False in the Designer.
In Form.Load or txt_Password.Enter: txt_Password.PasswordChar = ChrW(&H25CF)
Private Sub ShowPassword_MouseDown(sender As Object, e As MouseEventArgs) Handles ShowPassword.MouseDown
txt_Password.PasswordChar = Char.MinValue
End Sub
Private Sub ShowPassword_MouseUp(sender As Object, e As MouseEventArgs) Handles ShowPassword.MouseUp
txt_Password.PasswordChar = ChrW(&H25CF)
End Sub

How do i detect Alt + Tab Key and then close the form?

I want to close my form when Alt + Tab is pressed. However, The form is somehow not registering the key combination.
i have tried to use the Me.KeyUp event to detect the keys
Private Sub Menu_KeyUp(sender As Object, e As KeyEventArgs) Handles Me.KeyUp
Select Case e.KeyData
Case (Keys.Alt + Keys.Tab)
Close()
End Select
End Sub
How can it be done ?
Try this: As in my comment Alt+Tab would not work. So try something else like Alt+Q. Put this code under the Form's keydown event.
Private Sub Form1_KeyDown(sender As Object, e As KeyEventArgs) Handles MyBase.KeyDown
If (e.KeyCode = Keys.Q AndAlso e.Modifiers = Keys.Alt) Then
Me.Close()
End If
End Sub
You need to set the form's Set KeyPreview to True for it to respond to the key events, but since Alt + Tab is a special windows combination the key events will not be fired.
Try in this way
Dim myval As Integer 'this is global variable declare
Private Sub Menu_KeyUp(sender As Object, e As KeyEventArgs) Handles Me.KeyUp
Select Case e.KeyData
Case (Keys.Alt)
myval = 1
Case (Keys.Tab)
If myval = 1 Then
Me.Close()
End If
End Select
End Sub
May be my syntax in not correct, please edit it if anything is notok.
I managed to work around this problem using this method-
That's usually the ESC key. One key press. A common one. Maybe you want to handle the Form's deactivate event, instead. – Jimi
I used the forms deactivate event to close it, since Alt+Tab is basically deactivating the form!
(A workaround because Alt+Tab as keystrokes were not detected by any means, as mentioned by
the last two answers.)
My final code looks like this -
Private Sub Menu_Deactivate(sender As Object, e As EventArgs) Handles Me.Deactivate
If loaded = true Then
Close()
End If
End Sub
I had to check if loaded (a boolean i declared to be set to true when everything is loaded)
to be true because my form opens one other form while loading, which inadvertently deactivated the main form and closed it!
Thank you so much for helping me everyone!!
Edit: I needed to clarify that the second form is always behind my main form so it is never really clicked on. I did this as a workaround to have aero blur in my application without a bug i experienced while applying it to the main form - it is too light, so text is unreadable. So i set another form to show aero blur and always stay behind my main form, whose opacity I have set to 0.88. This gives me a lot of control over the look of the blur.

Making sure async tasks complete before vb.net application terminates

I'm creating a vb.net desktop application. This application includes some asynchronous functions. When the user closes the application via the red X in the upper-right corner, there is some logic to possibly run one or more of these async functions. The problem is, the program terminates before they are complete. I figured using "Await" in my call would do that, but apparently not.
I found this thread that talks about using ManualResetEvent, but I'm having trouble understanding all of it, especially since the question is in the context of a console app, and the MSDN documentation the answer links to is about specifying threads, not simply using async tasks. As an attempt at using it anyway, I tried adding this to my main form:
Public resetEvent As ManualResetEvent = New ManualResetEvent(False)
And immediately after the call to one of these functions, I added this (quote includes the call):
Await activeCount.SerializeAsync(activeCount)
resetEvent.WaitOne()
And at the end of my async function itself, before returning the Task, added this:
frmMain.resetEvent.Set()
I don't think I'm using that right, though. The program still terminates before it's complete anyway.
Even before that, I figured the best place for such a thing would be in ApplicationEvents MyApplication_Shutdown, but I'm not sure how to know if such a function is still running at that point.
So what is the best way to make sure all my async functions complete before the application terminates in this situation?
Thank you!
UPDATE AFTER ACCEPTED ANSWER:
Though F0r3v3r-A-N00b's answer worked, I realized I need to use a dialog in certain cases. I couldn't call that within the background worker because the dialog is on the GUI thread, not the background thread. I tried moving things around so I'd call the dialog first, then make the background worker and all that, but for whatever reason I couldn't get it to work.
Long story short, I got around it by simply making a synchronous version of my functions, and so I could say 'if the user terminated the program and I need to call any of these functions before closing, call the synchronous versions instead'. That works. Thanks!
Try this. Create a new project. Add 1 label and backgroundworker to your form. Paste this in your form's code area:
Public Class Form1
Dim taskCompleted As Boolean = False
Dim taskIsrunning As Boolean = False
Private Sub BackgroundWorker1_DoWork(sender As System.Object, e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
Threading.Thread.Sleep(5000)
End Sub
Private Sub BackgroundWorker1_RunWorkerCompleted(sender As Object, e As System.ComponentModel.RunWorkerCompletedEventArgs) Handles BackgroundWorker1.RunWorkerCompleted
taskCompleted = True
taskIsRunning = False
Label1.Text = "Background task completed."
Me.Close()
End Sub
Private Sub Form1_FormClosing(sender As Object, e As System.Windows.Forms.FormClosingEventArgs) Handles Me.FormClosing
If taskIsRunning Then
e.Cancel = True
Exit Sub
End If
If Not taskCompleted Then
taskIsRunning = True
Label1.Text = "Starting background task."
BackgroundWorker1.RunWorkerAsync()
Label1.Text = "Background task is running."
e.Cancel = True
End If
End Sub
End Class

How to make a KeyDown work twice

I am working on a little story type thing in visual basic. Nothing too complicated but i have run into a problem. I am trying to make a pause feature. I have it all working to when i press escape, the form goes into a menu type thing. The only problem is that it only works once. The first press works, but if i hit continue, it continues from where i left off. But if i press it again, nothing happens. Here is the code i am using:
Private Sub Story_KeyDown(sender As Object, e As KeyEventArgs) Handles Me.KeyDown
If e.KeyCode = Keys.Escape Then
Text.Hide()
Background.Hide
ContinueButton.Show
OptionsButton.Show
End If
End Sub
Is there any way for me to make this work more than just once? I am using a KeyDown event in a private sub.
Hope this made sense, but thanks for any help guys!
You're using Form.KeyPreview = true to capture the form keystrokes. I think Text.Hide() or one of Show operations could be the problem such as getting the TabStop's out of order or leaving a gap in the tab sequence.
Try resetting the controls or using SendKeys to reset the tab sequence index (to the approriate a control to have focus), For example, Tab forward and Alt Tab back:
SendKeys.Send("{TAB}");
or
SendKeys.Send("%{TAB}");
Ref:
http://msdn.microsoft.com/en-us/library/system.windows.forms.sendkeys.send(v=vs.110).aspx
Using a class level boolean as toggle should work:
Dim Hide As Boolean = False
Private Sub Story_KeyDown(sender As Object, e As KeyEventArgs) Handles Me.KeyDown
If e.KeyCode = Keys.Escape Then
Hide = Not Hide
If Hide Then
Text.Hide()
Background.Hide
ContinueButton.Show
OptionsButton.Show
Else
Text.Show()
Background.Show
ContinueButton.Hide
OptionsButton.Hide
End If
End Sub
If nothing else helps, you can always have a private class-level variable like _processingKeyDown, set to True at handler start, and then to False when done. The first line in the handler would be:
If _processingKeyDown Then Return

Not checking for control key pressed properly

I got this timer tick function:
Private Sub controlTick(ByVal sender As Object, ByVal e As EventArgs)
Label2.Text = (Control.ModifierKeys = Keys.Control)
End Sub
That is supposed to make my label say "True" if I am currently holding down the Control key, and "False" if I am not.
But, how come my label is always "False"? What is interesting is that if I press the Control key at lighting speed a bunch of times I can see for a fraction of a second "True", but immediately turns to "False".
Timer ticks every 50ms.
I do not understand.... any ideas?
I can't reproduce the behavior you describe... I tried creating a new WinForms project, placed a Label control on the middle of the form, and added a Timer control.
Whenever I press the Ctrl key, the label reads True. Otherwise, it reads False. Exactly the behavior you would expect to see. I don't have to press anything at lightning speed.
(Edit: It doesn't break when more controls are placed on the form either. What are you doing differently?)
My code looks like this:
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles Me.Load
' Start the timer
Timer1.Enabled = True
End Sub
Private Sub Timer1_Tick(sender As Object, e As EventArgs) Handles Timer1.Tick
' Update the label
Label1.Text = (Control.ModifierKeys = Keys.Control).ToString
End Sub
Only difference is that you're apparently compiling without type checking enabled (Option Strict Off).
I always prefer to code in VB.NET with this turned on (check your project's Properties window), in which case you have to explicitly convert the boolean type to a string type using ToString.
I have created a winform application to prove this.. I am using the form and I have set the "KeyPreview" property to true and for every key pressed I get the code correctly.
Please check again using the way I mentioned and let me know if it resolves.
private void Form1_KeyDown(object sender, KeyEventArgs e)
{
MessageBox.Show(e.KeyCode.ToString());
}
Also for Control key the code is (e.KeyCode == Keys.ControlKey)....
I'm not sure this will help, but try using HasFlag, because maybe there is some other flag in ModifierKeys which is also on:
http://msdn.microsoft.com/en-us/library/system.enum.hasflag.aspx