I missing something pretty fundamental here. I eventually want to send data via a com port while a button is pressed. In the meantime I've been sort of simulating the data transmission by simply trying to increment a value in a textbox while a button is pressed. I can start the count with the button mousedown event, but once the counting has started the button mouseup event is not detected.
'' Remove the following Application.Run code if it used elsewhere in your application.
Application.Run(Form1)
dim shared dn as boolean
dn=false
sub stry()
st:
form1.TextBox1.text = str(val (form1.textbox1.text)+1)
form1.refresh
if dn=true then goto st
END SUB
Function Form1_Button1_MouseDown( ByRef sender As wfxButton, ByRef e As EventArgs) As LRESULT
dn=true
stry()
Function = 0
End Function
''
''
Function Form1_Button1_MouseUp( ByRef sender As wfxButton, ByRef e As EventArgs) As LRESULT
dn=false
form1.textbox1.text=""
Function = 0
End Function
Thanks for any ideas.
You will need to insert a DoEvents in that tight GOTO loop to ensure that Windows messages hit the application's message queue.
sub stry()
st:
form1.TextBox1.text = str(val (form1.textbox1.text)+1)
Application.DoEvents
'form1.refresh
if dn=true then goto st
END SUB
Thanks Paul. In my ignorance, I thought the refresh would do the same as DoEvents, but then I also thought that button down could be detected after button had actually gone down. btw, your WinFBE_VisualDesigner.pdf refers to events as 'OnKeyPress' and the like (using 'On' preceding the event name given in the visual designer). Is there a difference? I think OnKeyPress describes the event better. The same as 'MouseHover' event suggests to me that it occurs while the mouse is hovering over the object, whereas it is a single event triggered as the mouse enters the area of the object. I guess I'm looking for buffers...
Quote from: raymw on August 01, 2018, 09:05:34 AM
...btw, your WinFBE_VisualDesigner.pdf refers to events as 'OnKeyPress' and the like (using 'On' preceding the event name given in the visual designer). Is there a difference? I think OnKeyPress describes the event better.
I have now removed the "On" prefix to avoid confusion.
QuoteThe same as 'MouseHover' event suggests to me that it occurs while the mouse is hovering over the object, whereas it is a single event triggered as the mouse enters the area of the object.
That's not 100% accurate. The MouseEnter event fires when the mouse cursor first touches the control. The MouseHover will fire after the mouse cursor has entered the control and has remained idle/stationary for a short period. The MouseLeave event will fire when the mouse cursor moves off of the control's client area.
Hi Paul, I've added another button to decrement the counter. Sometimes, it seems to me that the mouse down and mouse up does not relate to a specific button. It continues counting even if I click another button on the form.
''
'' Remove the following Application.Run code if it used elsewhere in your application.
Application.Run(jogger)
dim shared xbeg as Long
dim shared xcurr as Long
dim shared millout as double 'this will be x posn from dll?
millout = 500.88
dim shared xdown as Boolean
xdown=false
dim shared xup as boolean
xup=false
xcurr=millout
''
''
Function jogger_Buttonx_Click( ByRef sender As wfxButton, ByRef e As EventArgs) As LRESULT
jogger.startx.Text = str(millout)
Function = 0
End Function
''
''
Function jogger_returnx_Click( ByRef sender As wfxButton, ByRef e As EventArgs) As LRESULT
'stuff here to step back to starting point
jogger.nowx.Text = jogger.startx.text
Function = 0
End Function
''
''
sub xincrement()
stt:
if xup=true then
jogger.nowx.Text=str (val (jogger.nowx.text) +1)
END IF
application.doevents
jogger.refresh
if xup=false then return
goto stt
END SUB
sub xdecrement()
stt:
if xdown=true then
jogger.nowx.Text=str (val (jogger.nowx.text) - 1)
END IF
application.doevents
jogger.refresh
if xdown=false then return
goto stt
END SUB
''
Function jogger_down_MouseDown( ByRef sender As wfxButton, ByRef e As EventArgs) As LRESULT
xdown=true
xdecrement()
Function = 0
End Function
''''
Function jogger_down_MouseUp( ByRef sender As wfxButton, ByRef e As EventArgs) As LRESULT
xdown=false
Function = 0
End Function
''
Function jogger_up_MouseDown( ByRef sender As wfxButton, ByRef e As EventArgs) As LRESULT
xup=true
xincrement()
Function = 0
End Function
Function jogger_up_MouseUp( ByRef sender As wfxButton, ByRef e As EventArgs) As LRESULT
xup=false
Function = 0
End Function
Briefly, it's a couple of buttons, up and down, to add or subtract a value in the nowx text box. Often, pressing another button (even 'buttonx' ) continues the last operation, it does not seem to be detecting the change in mouse up or down related to a specific button. What's the magic word this time?
Best wishes,
Ray
I put together a test program based on your code. It seems like the MouseDown message is only being received when the button has input focus. This means that you need to click on the button and then do mousedown and mouseup. This is obviously not the behaviour you are needing. I expect that this problem is a combination of CXpButton's message handler and WinFBE's subclassing of that button. I will dive into it more closely later tonight. In the meantime, I have attached a copy of the form so others can verify the behaviour that I am seeing.
Thanks Paul.
Hi Ray,
This is a combination problem of how Windows handles messages and how WinFBE handles user input through the Event handlers.
When you press the mouse button down, Windows sends the WM_LBUTTONDOWN message. WinFBE routes that message to the Event handler for you to act on. In this case, we enter a very tight loop that does not allow the Event handler to ever exit until a WM_LBUTTONUP message is posted to the message queue. The problem with that is the WinFBE MouseDown Event handler MUST return otherwise the WM_LBUTTONDOWN message will never get fully processed by Windows because it never gets sent to Windows DefWindowProc. Essentially, we are eating the WM_LBUTTONDOWN message by not letting it fully process by staying that tight loop in your xincrement or xdecrement routines. The side effect of the WM_LBUTTONDOWN message not fully processing means that WM_GOTFOCUS and WM_LOSTFOCUS messages never get posted thereby not changing focus to the other button controls.
You might want to rethink the design of the experiment you're coding. Maybe respond to the Click Events of the buttons rather than the MouseDown and MouseUp.
Thinking out loud, I guess that one solution would be to start a thread during mouse down and do your incrementing/decrementing there and then exit the mouse down event handler. Mouseup event handler would terminate the thread.
Hi Paul, thanks for the explanation. In the meantime, I thought I'd see if I could get what I wanted in Firefox, but there I couldn't find the events similar to mouse down. I started thinking about windows apis, then came across the fact that it seems for a checkbox, you can use FF_Control_GetCheck . Not having used it in a tight loop, but I'd assume that there was a flag set when the check box was checked, and instead of checking for mouse up an down events, you simply inspect the flag. Of course, not sure at what level that flag would be, whether tending towards windows, or the user language. I've noticed that in WinFBE you have the same events for both the checkbox and the button.
Is there a winapi that can access the state of a button? If so how do I use it? For this particular situation, at least the final program, I need a continuous stream of data being sent, while a button is pressed, in fact their will be four groups of buttons, one per axis, and most likely five or six per axis to send data at different speeds, only one being pressed at a time - most likely a worse situation wrt event handling.
For a Checkbox, the visual designer help file lists the following property:
CheckState - Gets or sets the state of the control. Refer to the CheckBoxState enum.
So.... something like this:
If Form1.Check1.CheckState = CheckBoxState.Checked Then
' My checkbox is checked!
End If
To set the checkbox to a checked state:
Form1.Check1.CheckState = CheckBoxState.Checked
I have implemented checkboxes to allow 3-states (refer to the ThreeState property)
CheckBoxState.Checked
CheckBoxState.UnChecked
CheckBoxState.Indeterminate
Not tried it, nor thought too much, but could not the button have a checkstate? Or, if I use a checkbox, will it still give errors due to the event handler and tight loop? A check box is not the interface I want, but if it works, maybe some features could be blended with the button. e.g. checkstate for button down. I'll have a play with checkboxes, but I'm not holding out much hope
nb, just noticed in my previous post I mentioned Firefox, I meant firefly. Never would have happened with netscape...
Maybe you're thinking of he Button "ToggleMode" property? That allows you to have an "Off" and "On" type of state.
To get or set the toggle state use:
Form1.Button1.ToggleState = True/False
Not toggle, since afaik you press once to turn on, and press again to turn off. (a bit like the check box wrt number of presses to change state.) I've just sort of integrated the buttons with check boxes, and that behaves as I want - e.g counts while button pressed. BUT it's doing something nasty at a low level - have to do 'ctrl alt delete' to stop the program as everything locks up when I press the red x.
'' Remove the following Application.Run code if it used elsewhere in your application.
Application.Run(jogger)
dim shared xbeg as Long
dim shared xcurr as Long
dim shared millout as double 'this will be x posn from dll?
millout = 500
dim shared xdown as Boolean
xdown=false
dim shared xup as boolean
xup=false
xcurr=millout
''
''
Function jogger_Buttonx_Click( ByRef sender As wfxButton, ByRef e As EventArgs) As LRESULT
jogger.startx.Text = str(millout)
Function = 0
End Function
''
''
Function jogger_returnx_Click( ByRef sender As wfxButton, ByRef e As EventArgs) As LRESULT
'stuff here to step back to starting point
jogger.nowx.Text = jogger.startx.text
Function = 0
End Function
sub xincrement()
stt:
if jogger.checkup.CheckState = checkboxstate.checked then
jogger.checkdown.CheckState = checkboxstate.unchecked
jogger.nowx.Text=str (val (jogger.nowx.text) +1)
END IF
application.doevents
jogger.refresh
if jogger.checkup.CheckState = checkboxstate.checked then
application.doevents
goto stt
end if
END SUB
'
sub xdecrement()
stt:
if jogger.checkdown.CheckState = checkboxstate.checked then
jogger.checkup.CheckState = checkboxstate.unchecked
jogger.nowx.Text=str (val (jogger.nowx.text) -1)
END IF
application.doevents
jogger.refresh
if jogger.checkdown.CheckState = checkboxstate.checked then
application.doevents
goto stt
end if
END SUB
'
''
Function jogger_Load( ByRef sender As wfxForm, ByRef e As EventArgs) As LRESULT
jogger.show
fff:
xincrement()
xdecrement()
goto fff
Function = 0
End Function
''
''
Function jogger_up_MouseDown( ByRef sender As wfxButton, ByRef e As EventArgs) As LRESULT
jogger.Checkup.CheckState = checkboxstate.checked
Function = 0
End Function
''
''
Function jogger_up_MouseUp( ByRef sender As wfxButton, ByRef e As EventArgs) As LRESULT
jogger.checkup.CheckState = checkboxstate.unchecked
Function = 0
End Function
''
''
Function jogger_down_MouseDown( ByRef sender As wfxButton, ByRef e As EventArgs) As LRESULT
jogger.Checkdown.CheckState = checkboxstate.checked
Function = 0
End Function
''
''
Function jogger_down_MouseUp( ByRef sender As wfxButton, ByRef e As EventArgs) As LRESULT
jogger.Checkdown.CheckState = checkboxstate.unchecked
Function = 0
End Function
(I may be able to get something going in the toggle button mode - dies it have a checkstate like the checkbox?)
Plenty of room for improvement, but it shows, however ugly the code, I've a chance of doing what I want.
The next stage will be trying to get the interfacing to a pre-written dll (in gwbasic, I believe) and sorting out talking via usb port.
I'd be surprised if your code worked because once again an Event handler looks like it's being prevented from being completed ("Form1_Load").
Let me look at using the Thread route and using a global to signal on/off or +/- of the values. That would be the best option by far in this case.
It runs, but I can't stop it. Even ctrl alt del doesn't always get the task manager, sometimes i can change user, but often need to power off pc. Dangerous stuff...
I must be missing something. Why he doesn't use an UpDown control?
Quote from: José Roca on August 02, 2018, 01:40:58 PM
I must be missing something. Why he doesn't use an UpDown control?
Regardless of what control is used he will eventually run into the same problem that keeping the button down will block an Event handler from completing. He doesn't want to use a toggle approach of click On and then click Off.
I will post in a minute a thread version that works perfectly for this scenario.
Here you go. A VERY simple thread example that uses globals to indicate direction for the values (rather than using conditional signals). I am also not using mutexes because it is only one thread that only gets invoked once.
' You should always include a resource file that references a valid manifest.xml
' file otherwise your application will not properly display Windows themed controls.
' Sample resource.rc and manifest.xml files can be found in the WinFormsX download.
' The following WinFBE directive includes the resource in your application. If you
' are using WinFBE's project management features then omit the following line because
' one will be generated automatically.
'#RESOURCE "resource.rc"
''
'' Remove the following Application.Run code if it used elsewhere in your application.
Application.Run(Form1)
type ThreadTYPE
pThread as any ptr
Direction as long ' -1 down, 0 stop, +1 up
bQuit as Boolean
END TYPE
dim shared gtThread as ThreadTYPE
''
''
Sub WorkerThread( ByVal id_ptr As Any Ptr )
dim as long nValue
do until gtThread.bQuit
if gtThread.Direction < 0 then
nValue = val(Form1.TextBox1.text) - 1
Form1.TextBox1.text = str(nValue)
elseif gtThread.Direction > 0 then
nValue = val(Form1.TextBox1.text) + 1
Form1.TextBox1.text = str(nValue)
end if
sleep 10 ' allow timeslice to switch
loop
End Sub
''
''
Function Form1_Load( ByRef sender As wfxForm, ByRef e As EventArgs) As LRESULT
' Create the worker thread
gtThread.pThread = ThreadCreate(@WorkerThread, 0)
if gtThread.pThread = 0 then print "Error creating thread"
Function = 0
End Function
''
''
Function Form1_FormClosed( ByRef sender As wfxForm, ByRef e As EventArgs) As LRESULT
' Set boolean to exit out of thread loop
gtThread.bQuit = true
' Wait for the worker thread to end and release its resources
ThreadWait(gtThread.pThread)
Function = 0
End Function
''
''
Function Form1_btnUp_MouseDown( ByRef sender As wfxButton, ByRef e As EventArgs) As LRESULT
gtThread.Direction = +1
Function = 0
End Function
''
''
Function Form1_btnUp_MouseUp( ByRef sender As wfxButton, ByRef e As EventArgs) As LRESULT
gtThread.Direction = 0
Function = 0
End Function
''
''
Function Form1_btnDown_MouseDown( ByRef sender As wfxButton, ByRef e As EventArgs) As LRESULT
gtThread.Direction = -1
Function = 0
End Function
''
''
Function Form1_btnDown_MouseUp( ByRef sender As wfxButton, ByRef e As EventArgs) As LRESULT
gtThread.Direction = 0
Function = 0
End Function
Thanks Paul, I'll have a look. I've also found that 'jogger.startx.Text = format(millout,"#.0000")' gives an error message - error 42: variable not declared, format
Function jogger_Buttonx_Click( ByRef sender As wfxButton, ByRef e As EventArgs) As LRESULT
jogger.startx.Text = format(millout,"#.0000")
jogger.nowx.Text = jogger.startx.text
mainloop()
Function = 0
End Function
I put another button, to jump out of the up/down loop, and close the form and put the code into a subroutine named mainloop. Works OK, if I make form border style none, else still a chance of trying to close form via the normal window border x.
Per the FB help, you need to include "string.bi" in order to use the Format command.
#include "string.bi"
result = Format[$]( numerical_expression, formatting_expression )
You will need to use the Thread method that I showed above. Any other approach will fail.
Hi Paul, Thanks. even though it's simple, it is still too much to think about... I didn't/don't know much about threads, but looks as if it will be useful. So, if it's a separate core running a user thread, presumably it will not do much of the windows housekeeping, looking for the mouse, waiting for files to load, etc. That means it could be useful for what I thought I may have to use, instead of either using micro processors or a rtos. I guess you can't include it in your visual designer - maybe open a separate properties window?
face slap wrt "string.bi"
Hi Paul, thanks for the threading sample, I've worked with it, and done some more development on my program but I've hit another button problem, perhaps best shown by the code below.
Three buttons, which when you press any one, it's highlighted, and stays highlighted until another of the three buttons are pressed. Using mouse down for each button seems to work as expected, setting the highlight and non-highlight background colours for the three buttons. I have introduced a fourth button but if that is pressed, then the last selected button colour is frozen, not changed by pressing one of the other three. (This is under your winfbe v 1.7.2)
''
'' Remove the following Application.Run code if it used elsewhere in your application.
Application.Run(Form1)
''
Function Form1_Button1_MouseDown( ByRef sender As wfxButton, ByRef e As EventArgs) As LRESULT
form1.button1.BackColor = colors.Azure
form1.button2.BackColor = colors.Azure
form1.button3.BackColor = colors.Azure
form1.button1.BackColor = colors.Aqua
Function = 0
End Function
''
''
Function Form1_Button2_MouseDown( ByRef sender As wfxButton, ByRef e As EventArgs) As LRESULT
form1.button1.BackColor = colors.Azure
form1.button2.BackColor = colors.Azure
form1.button3.BackColor = colors.Azure
form1.button2.BackColor = colors.Aqua
Function = 0
End Function
''
''
Function Form1_Button3_MouseDown( ByRef sender As wfxButton, ByRef e As EventArgs) As LRESULT
form1.button1.BackColor = colors.Azure
form1.button2.BackColor = colors.Azure
form1.button3.BackColor = colors.Azure
form1.button3.BackColor = colors.Aqua
Function = 0
End Function
''
''
Function Form1_Button4_MouseDown( ByRef sender As wfxButton, ByRef e As EventArgs) As LRESULT
Function = 0
End Function
after pressing the fourth button, I would expect pressing any of the other three would continue the highlighting behavior , in exactly the same way as if the fourth had not been pressed.
This would be a button refresh issue. By default, when you change the backcolor of a button the refresh is not called. Maybe by default it should be. To fix your issue call a routine that refreshes your buttons like the following:
Also, why in your MouseDown are you setting the backcolor of a button twice? For example, for button1 mousedown you set it to Azure and then to Aqua all in the MouseDown?
Function Form1_Button1_MouseDown( ByRef sender As wfxButton, ByRef e As EventArgs) As LRESULT
form1.button1.BackColor = colors.Azure
form1.button2.BackColor = colors.Azure
form1.button3.BackColor = colors.Azure
form1.button1.BackColor = colors.Aqua
Function = 0
End Function
' You should always include a resource file that references a valid manifest.xml
' file otherwise your application will not properly display Windows themed controls.
' Sample resource.rc and manifest.xml files can be found in the WinFormsX download.
' The following WinFBE directive includes the resource in your application. If you
' are using WinFBE's project management features then omit the following line because
' one will be generated automatically.
'#RESOURCE "resource.rc"
''
'' Remove the following Application.Run code if it used elsewhere in your application.
Application.Run(Form1)
function RefreshButtons() as Long
form1.button1.Refresh
form1.button2.Refresh
form1.button3.Refresh
function = 0
end function
''
Function Form1_Button1_MouseDown( ByRef sender As wfxButton, ByRef e As EventArgs) As LRESULT
form1.button1.BackColor = colors.Azure
form1.button2.BackColor = colors.Azure
form1.button3.BackColor = colors.Azure
form1.button1.BackColor = colors.Aqua
RefreshButtons
Function = 0
End Function
''
''
Function Form1_Button2_MouseDown( ByRef sender As wfxButton, ByRef e As EventArgs) As LRESULT
form1.button1.BackColor = colors.Azure
form1.button2.BackColor = colors.Azure
form1.button3.BackColor = colors.Azure
form1.button2.BackColor = colors.Aqua
RefreshButtons
Function = 0
End Function
''
''
Function Form1_Button3_MouseDown( ByRef sender As wfxButton, ByRef e As EventArgs) As LRESULT
form1.button1.BackColor = colors.Azure
form1.button2.BackColor = colors.Azure
form1.button3.BackColor = colors.Azure
form1.button3.BackColor = colors.Aqua
RefreshButtons
Function = 0
End Function
''
''
Function Form1_Button4_MouseDown( ByRef sender As wfxButton, ByRef e As EventArgs) As LRESULT
RefreshButtons
Function = 0
End Function
> This would be a button refresh issue.
I warned you. If you want I can add a call to the Refresh method in all these properties.
Thanks Paul, and and Jose. an auto refresh would be nice. I was, for the simplicity of cutting and pasting, setting the background twice. I can't see that should be a problem, but it actually does the colour change to aqua, its the not colour changing for the other buttons that is the snag. I'd actually experimented with dropping form refreshes in each function, I thought a refreshed form refreshed all its components. I'll refresh each button individually.
Best wuishes,
Ray
Now sorted, thanks.
'' Remove the following Application.Run code if it used elsewhere in your application.
Application.Run(Form1)
function refreshbuttons() as long
form1.button1.refresh
form1.button2.refresh
form1.button3.refresh
function=0
END FUNCTION
function setback() as Long
form1.button1.BackColor = colors.Azure
form1.button2.BackColor = colors.Azure
form1.button3.BackColor = colors.Azure
function=0
END FUNCTION
''
Function Form1_Button1_MouseDown( ByRef sender As wfxButton, ByRef e As EventArgs) As LRESULT
setback()
form1.button1.BackColor = colors.Aqua
refreshbuttons()
Function = 0
End Function
''
''
Function Form1_Button2_MouseDown( ByRef sender As wfxButton, ByRef e As EventArgs) As LRESULT
setback()
form1.button2.BackColor = colors.Aqua
refreshbuttons()
Function = 0
End Function
''
''
Function Form1_Button3_MouseDown( ByRef sender As wfxButton, ByRef e As EventArgs) As LRESULT
setback()
form1.button3.BackColor = colors.Aqua
refreshbuttons()
Function = 0
End Function
''
''
Function Form1_Button4_MouseDown( ByRef sender As wfxButton, ByRef e As EventArgs) As LRESULT
Function = 0
End Function
''
''
Function Form1_Load( ByRef sender As wfxForm, ByRef e As EventArgs) As LRESULT
setback()
refreshbuttons()
Function = 0
End Function
(There is a trade off in key presses between cutting and pasting and writing a function and calling lines...)
@Paul,
I have added a call to the Redraw method in the properties.
Thanks Jose - I will integrate it into the code immediately.
Looks good. Everything seems to be working well with the new code.