vb.net how to solve 2 issues with single and double click on notify icon - vb.net

I have a vb.net (.NET 3.0) app which has a NotifyIcon in the system tray. I would like the single left-click and double left-click events to do different things; .Click should open the app's context menu, and .DoubleClick should take some default action. So this is my code at the moment:
Private Sub showMenu(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) _
Handles Tray.Click
Debug.Print("click")
If e.Button = MouseButtons.Left Then
Dim mi As MethodInfo = GetType(NotifyIcon).GetMethod("ShowContextMenu", BindingFlags.Instance Or BindingFlags.NonPublic)
mi.Invoke(Tray, Nothing)
End If
End Sub
Private Sub defaultAction(ByVal sender As System.Windows.Forms.NotifyIcon, ByVal e As System.EventArgs) _
Handles Tray.DoubleClick
Debug.Print("double click")
doDefaultAction()
End Sub
The first problem is that the .Click handler is fired even for a double-click - it responds to the first click rather than waiting to see if it was actually a double-click. Maybe this is normal behaviour, but is there a 'best practice' way of trapping that occurrence without horrible kludges involving timers? From what I've read on SO, I suspect not. However, that's not the most serious problem...
The second, bigger, problem is that the doDefaultAction() code does various things, one of which is to download an xml file from a specific URL. I'm doing this with this line of code (note not the actual URL:):
Dim reader = XmlReader.Create("http://server.com/genxml.php")
As soon as execution reaches that line, another .Click event is fired, so the debug output looks like this:
click
double click
click
That second click event re-opens the context menu, and because doDefaultAction() goes on to show a modal MessageBox, the menu gets stuck open. I've stepped through in the debugger, and if I 'Step Into' that Dim reader line, I get taken straight to Sub showMenu() above. Very odd. Any ideas what could cause that?

Related

More info on click event in VB

I would like to create a bot that repeats the same action many times. Is there any way I can get more info about a click event. Lets say I click somewhere on the opened file explorer window. Can the program tell that I clicked on a specific area or button in that window? Can I make the program click or type on a specific(opened) window?
Thanks
Yes the program can tell when you click on a specific button. Whatever button it is that you clicked on will have its click event fired, if it exists. In order to manually tell the program to perform this action, you can implement the phrase..
btn.PerformClick()
where "btn" is the name of whatever button you are needing to click. Whenever this phrase is called, the btn_Click event handler will be fired just the same as if you yourself actually clicked the button.
To do the same thing with an actual window or form in your program is trickier because there are no built in methods you can call to trigger a windows form click event. But it is possible. The code that will define a window's event handler and trigger it will be..
Private Sub myWindow_Click() Handles MyBase.Click
'code you want to run on click event here
End Sub
myWindow_Click() 'where you want the click to be triggered
The above would be much simpler to simulate in a method call however.
And lastly to simulate the typing of text into a window, you could simply access the text property of the control you are wanting to alter, and then change that text property to the desired text. But judging from your question, you are wanting this to be done in a similar fashion to how a human would.
This can also be accomplished with some work, using a timer interval that appends to the text of the control.
Lets say for example, you wanted "hello world" to be typed into a text box on screen as a person would. Add a timer control to your form and set its interval to 250 or however fast you want the word to be type and in a method..
Dim str as String = ""
Dim pos as Integer = 0
Public Sub humanType(word as String)
str = word
Timer1.enabled = true
End Sub
public Sub Timer1_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Timer1.Tick
myTextBox.Text = myTextBox.Text + str[pos]
pos = pos + 1
If pos = str.length Then
Timer1.enabled = false
End If
End Sub
If you have never worked with timer intervals before, this tutorial has some good info on them for VB.Net. http://www.techrepublic.com/article/perform-actions-at-set-intervals-with-vbnets-timer-control/
Hope it helps!

Winforms button click mousedown/mouseup

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.

VB.net (2010) Click-through on all controls

I'm having a application with quite a few tabpages. One function of my program is that if the user leaves the application for more than 3 minutes, I display the main tabpage where the most relevant information is shown (this is done by a timer called "back_to_main_tab"). So far so good. However in a few tabpages you can enter text and "take a pause" for more than these 3 minutes, and if that happens, the user is taken back to the main tab, without his/her consent, erasing his/her entered text.
I realize that this could be solved by enabling/disabling the back_to_main_tab-timer at strategic places, but I would like to solve it by resetting the timer each time a click is registered in the application regardless of what is is clicked. The reason for this is that the problem isn't unique for a certain tabpage, so I would like to have a "all-purpose"-fix for all tabpages.
Is this possible?
Thanks in advance
Jonas
You can write an Event Handler for each element where user can write.
For example
Private Sub TextBox1_KeyDown(ByVal sender As System.Object, ByVal e As System.Windows.Forms.KeyEventArgs) Handles TextBox1.KeyDown
'Reset timer
End Sub
(and you could try KeyUp or KeyPress too).
So you can reset timer each time the user write something somewhere.
Btw if the 3mins pass (after last KeyPress), the back_to_main_tab erase all.
EDIT
Ok so you could make one function to Manage the CLICK event. And attach the function to all items form.
To attacch an event handler to alla element you could try something like this
Private Sub AddGenericEventHandler()
For Each Ctrl As Item In Form.Items
AddHandler Ctrl.Click, AddressOf MyClickHandler
Next
End Sub
And write the function that reset your timer
Public Sub MyClickHandler(ByVal sender As System.Object, ByVal e As System.EventArgs)
'Timer Reset
End Sub
This is an Idea. You have to write correctly the FOR EACH to catch all form items do you need.

vb.net webbrowser documentcompleted event - page not loading

I have searched the other questions surrounding the WebBrowser DocumentCompleted event, but no-one seems to have the exact problem that I am having.
I am trying to automate searching flights on an airline website. The first url I use is the same every time except for the date part so it's easy enough to get the WebBrowser to go the URL by combining strings. However, that page is a disclaimer page that has a 'proceed' button that needs to be clicked before prices are shown. If I use a series of buttons on a form, I can get to the first URL by clicking button1, and then click the proceed button by clicking button2. It works fine.
However, I wanted to remove the need to click button2 so attempted to use the WebBrowser DocumentCompleted event. The problem that I am having is that the first page never seems to fully load in the webbrowser and so the button is never clicked.
This is the code that I am using for the two buttons and the DocumentCompleted event
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
TextBox1.Text = fullURL
WebBrowser1.Navigate(fullURL)
End Sub
Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
Dim allElements As HtmlElementCollection = WebBrowser1.Document.All
For Each webpageelement As HtmlElement In allElements
If webpageelement.GetAttribute("src") = proceedbuttonattribute Then
webpageelement.InvokeMember("click")
End If
Next
End Sub
Private Sub WebBrowser1_DocumentCompleted(ByVal sender As System.Object, ByVal e As System.Windows.Forms.WebBrowserDocumentCompletedEventArgs) Handles WebBrowser1.DocumentCompleted
If TextBox1.Text.StartsWith(firstURL) = True Then 'make sure that button is only clicked after first webpage loads
Dim allElements As HtmlElementCollection = WebBrowser1.Document.All
'Click 'Proceed to Booking' button
For Each webpageelement As HtmlElement In allElements
If webpageelement.GetAttribute("src") = proceedbuttonattribute Then
webpageelement.InvokeMember("click")
End If
Next
End If
End Sub
Thanks!
WebBrowser1.Navigate(fullURL)
Do While wb.ReadyState <> WebBrowserReadyState.Complete
Application.DoEvents()
Loop
;)
When you say DocumentCompleted is never triggered, do you mean it isn't triggered at all or are you saying that maybe the If statement isn't returning True, and therefore the contents (and click action) are not run?
Also, are there any frames or iframes on this page? Because, if there are, DocumentCompleted will not run until each and every single frame is loaded and completed, and if its an ajaxed frame, then that will introduce mroe problems. All it takes is for one of the frames to not load properly or remain in "Interactive" mode (where .readystate = 3) instead of it being fully and properly loaded (where .readystate = 4) and this will prevent the DocumentCompleted event from getting triggered.
Also, how long are you waiting for the DC event to trigger?
There is a better way around this, all you need to do is run a Do/While loop, with the exact same code as in your DC event, and it will just sit there (after the .Navigate2 is called) and just wait until that button shows up in the DOM, and as soon as the following returns True, you can use .InvokeMember and click on the button.
If webpageelement.GetAttribute("src") = proceedbuttonattribute Then
So, in this case, you will create another function named "WaitUntilButtonFound" and perhaps place a 100 millisecond Sleep (wait) between each loop, and a .DoEvents (found in .Threading namespace) right after or before the Sleep method (also found in .Threading I think).
This way, as soon as the button of relevance appears in the Document Object Model, you can click it, and if you want, as soon as its found, you can wait another 2 - 3 seconds (if you want, no real need) and then click it. Because finding that button in the DOM is an indicator that the page has either loaded, or partially loaded (where the relevant or necessary part has completed loading), so that you can resume the action you wanted to take on that button (which is, to click it), right after it appears. In fact, it'll be the quickest way to move forward as well.
What do you think? Let me know how you go and if you need more help or guidance. Also, if you could let us know if the DC event is absolutely not being triggered or if it's just your IF statement that is blocking the DC from running the code inside the DC event, that would be helpful, because if the DC event is being triggered but code inside isn't running because of the If statements inside it, that is something entirely different to the DC even not being triggered at all.
Let us know, thanks.

Right click: menu options

there is a feature I'd like to implement in my application:
The user right clicks on my picturebox object. Good.
When that happens, some code of mine executes and will generate a list of options.
Then a menu appears where the mouse right-clicked, made up of these options.
When the user clicks on one of these options, the menu is deleted and some code is run given the option index as parameter.
My two problems:
How can I tell when the user right clicks? I can see an event handler for "click", but that includes left clicks....
How do I create one of these menus? I mean, go ahead and right click something. That's the kind of menu I'm looking for.
You need to implement the picturebox' MouseUp event. Check if the right button was clicked, then create a ContextMenuStrip with the menu items you want. You can use, say, the Tag property of the items you add to help identify them so you can give them a common Click event handler. Like this:
Private Sub PictureBox1_MouseUp(ByVal sender As System.Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles PictureBox1.MouseUp
If e.Button <> Windows.Forms.MouseButtons.Right Then Return
Dim cms = New ContextMenuStrip
Dim item1 = cms.Items.Add("foo")
item1.Tag = 1
AddHandler item1.Click, AddressOf menuChoice
Dim item2 = cms.Items.Add("bar")
item2.Tag = 2
AddHandler item2.Click, AddressOf menuChoice
'-- etc
'..
cms.Show(PictureBox1, e.Location)
End Sub
Private Sub menuChoice(ByVal sender As Object, ByVal e As EventArgs)
Dim item = CType(sender, ToolStripMenuItem)
Dim selection = CInt(item.Tag)
'-- etc
End Sub
To your first question: you actually handle just the "click" event, there is no separate event for right-click. But look at the EventArgs object you get passed for the event: it includes information of which button was pressed (and would give you more info if a mouse click had anything beyond that). So you check the button within an if block, and you're good to go.
To your second question: http://msdn.microsoft.com/en-us/library/system.windows.forms.contextmenustrip.aspx. If your menu is pre-defined, just look for that component on the Designer and prepare the menu from there and call its Show() method from the click handler. If you need to decide the menu entries on the fly, the linked documentation page actually includes an example on that ;)
PS: oops, I just noticed Jon's comment on the question. The answer I gave you is for Windows Forms. If you are on WPF let us know and I'll update with the details (although the concepts ain't too different).
There is actually an easier way to do this. Double-click on the control you wish to be able to right click. Now go to the top of the page and it should say in comboboxes; 'Control' and 'Click' Click on the 'click' combobox and look for: Right-Click. Use a ContextMenuStrip for your right click menu.
Now you can choose which function you want.
Private Sub PictureBox1_RightClick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button3.RightClick
ContextMenuStrip1.Show()
MsgBox("Content Activated.", MsgBoxStyle.Information, "Success!")
End Sub
Hope I could help. :)
Coridex73