Recent

Author Topic: TCheckListBox with OnClick & OnClickCheck  (Read 13120 times)

andyH

  • Jr. Member
  • **
  • Posts: 99
Re: TCheckListBox with OnClick & OnClickCheck
« Reply #15 on: May 18, 2021, 07:03:24 pm »
Well I've done a bit more testing, now have event handlers for both OnClick and OnClickCheck so I can track what is happening. The OnClick handler is as my previous, with writeln('box3 clicked on item ',Box3.ItemIndex); as the first statement.

The OnClickCheck handler is:
Code: Pascal  [Select][+][-]
  1. procedure TRestore.Box3CheckCK(Sender: TObject);
  2. begin
  3.   //do nothing
  4.   writeln('checkbox3 clicked on item ',Box3.ItemIndex);
  5. end;

I was relying on this post https://forum.lazarus.freepascal.org/index.php/topic,42425.msg296186.html#msg296186 for how the onclick hooks work.

Blaazen stated:
Quote
What widgetset? I tested on Qt and GTk2 and:

OnClick is fired when any part of the Control is clicked.
OnClickCheck is fired when the check box is clicked.
OnItemClick is also fired when the check box is clicked - duplicate of OnClickCheck.
Nearly correct, I have found that (GTK2):
  • Clicking on the item always fires the OnClick hander. It does not fire the OnClickCheck handler (as expected).
  • Clicking on the checkbox fires OnClickCheck (always) and also fires OnClick (unfortunately not always).

Commented terminal output:
Code: Pascal  [Select][+][-]
  1. //First click on the checkbox:
  2. box3 clicked on item 0 //both are fired, expected and desired behaviour
  3. checkbox3 clicked on item 0
  4.  
  5. //Second click on the checkbox:
  6. checkbox3 clicked on item 0 //onclickcheck fired, onclick has NOT been fired
  7.  
  8. //Third (and subsequent clicks) on the checkbox:
  9. checkbox3 clicked on item 0 //both are fired, but note change of order, onclickcheck first?
  10. box3 clicked on item 0
At least I know what is happening now and can do something about it, previously I had thought that second click was not being picked up.

Is this a bug or a timing/race issue?

I do have a custom ondraw handler on the TCheckList box, it bolds the text if there is an operating system on the partition listed in the TCheckList box. Can't think of a reason why this would cause the behaviour above?




dseligo

  • Hero Member
  • *****
  • Posts: 1180
Re: TCheckListBox with OnClick & OnClickCheck
« Reply #16 on: May 18, 2021, 07:38:31 pm »
Is this a bug or a timing/race issue?

I do have a custom ondraw handler on the TCheckList box, it bolds the text if there is an operating system on the partition listed in the TCheckList box. Can't think of a reason why this would cause the behaviour above?

You see how you are revealing details which could be important? And we should guess what else you have in your application.
I repeat to you again: make a small test project where your issue is shown.

andyH

  • Jr. Member
  • **
  • Posts: 99
Re: TCheckListBox with OnClick & OnClickCheck
« Reply #17 on: May 19, 2021, 08:24:29 pm »
Well I put together a small project - cut down/modified version of the application. I've attached it, but it doesn't help - it works as it should  :(

It has the principal attributes of the main application:
  • main form with several tabs
  • a panel added to the tab
  • a custom tchecklist box in the panel
  • tchecklistbox has event handlers for custom hints, lbownerdraw and onclick and onclickcheck.
Every time you click on the checkbox for any item it fires both onclick and onclickcheck unlike the behaviour reported above.

I can't replicate the 'real' event handlers as they are calling procedures/functions that effectively pull in the rest of the application.






VTwin

  • Hero Member
  • *****
  • Posts: 1215
  • Former Turbo Pascal 3 user
Re: TCheckListBox with OnClick & OnClickCheck
« Reply #18 on: May 19, 2021, 10:04:43 pm »
Well I put together a small project - cut down/modified version of the application. I've attached it, but it doesn't help - it works as it should  :(

It does help. It shows that the problem is elsewhere.
“Talk is cheap. Show me the code.” -Linus Torvalds

Free Pascal Compiler 3.2.2
macOS 12.1: Lazarus 2.2.6 (64 bit Cocoa M1)
Ubuntu 18.04.3: Lazarus 2.2.6 (64 bit on VBox)
Windows 7 Pro SP1: Lazarus 2.2.6 (64 bit on VBox)

andyH

  • Jr. Member
  • **
  • Posts: 99
Re: TCheckListBox with OnClick & OnClickCheck
« Reply #19 on: May 19, 2021, 10:31:00 pm »
I think it's a timing issue, but as I know how it behaves I can deal with it.

andyH

  • Jr. Member
  • **
  • Posts: 99
Re: TCheckListBox with OnClick & OnClickCheck
« Reply #20 on: May 23, 2021, 08:32:29 pm »
jamie, thanks for that, I'll have a look. Not been able to get near this since my last post, but vaguely remember doing something similar with a boolean flag instead of Tag - can't remember the outcome.

I did have a quick play earlier today before I saw your post:

Event handlers for both onclick and onclickcheck, with the onclickcheck doing nothing more that reporting it had fired. I found that if I set selected:= false in the onclick handler, clicking on the checkbox would always fire both the onclickcheck and onclick handlers (as normal), but onclickcheck would always report the itemindex = -1, whereas onclick would report the correct itemindex. No idea if this is expected behaviour.

Not finished bottoming this out, more work to do.
« Last Edit: May 23, 2021, 08:36:02 pm by andyH »

andyH

  • Jr. Member
  • **
  • Posts: 99
Re: TCheckListBox with OnClick & OnClickCheck
« Reply #21 on: May 25, 2021, 06:22:46 pm »
To try and get my head around clicking in TCheckList boxes, I'll summarise what I've learned, some of which is from other forum posts.
  • Clicking on the item in a TCheckListBox fires the onclick handler.
     
  • Clicking on the checkbox fires both the onclick and onclickcheck handlers.
This is not good, there is not not a unique handler for each action*. This is made worse by the fact that:
  • Clicking on an item does NOT change the state of the checkbox.
     
  • Clicking on the checkbox does change the state of the checkbox, more importantly it changes the state of the checkbox before calling any event handler. I have not found any way of stopping this.
This means that a single onclick handler cannot be used. If your handler runs a series of checks to determine if the user can change the state (mine do), then a simple Checked:= not Checked; at the end cannot be used to toggle. The onclick handler does not know whether it was fired from an item click or checkbox click (where the state has already been changed). Nor can you use a simple onclickcheck handler that just flips the state, i.e. Checked:= not Checked;. You cannot GUARANTEE the firing order, onclick > onclickcheck or onclickcheck > onclick.

I had previously tried and abandoned Jamie's idea - using onclickcheck to toggle a flag. This doesn't work because of the firing order issue. I have found the first click on the checkbox is onclick > onclickcheck, subsequent clicks reverse this.

The solution I adopted was a custom TCheckListBox - adding CheckState : array of boolean, where CheckState maintains the state of the CheckBoxes. The onclickcheck handler simply resets the checkbox from CheckState, i.e. Checked[j]:= CheckState[j]; so onclick can do the grunt work and is responsible for maintaining CheckState. Downside - increased complexity.

I also (by accident) looked at setting Selected:= false in the onclick handler. This results in:
  • calls to the onclickcheck handler ALWAYS being passed an itemindex = -1, onclick does get passed the correct itemindex
     
  • clicking on the checkbox still flips the state before calling any handler
     
  • in theory selected:= false inside the onclick handler should create an infinite loop, in practice it doesn't, it fires twice. I can only assume that there must be some internal code to trap this. Still not a good idea and it doesn't help.
Life would be a LOT simpler if there were unique handlers for each action, or that clicking on the checkbox did not flip state before calling the handler. I've spent so much time on this that I've kind of lost sight of the original objective.

* There is also onitemclick, but as I understand, this is fired by both clicking on the item or the checkbox, same as onclick, I can't see the point of it. Neither does dephi.
« Last Edit: May 25, 2021, 06:24:17 pm by andyH »

dseligo

  • Hero Member
  • *****
  • Posts: 1180
Re: TCheckListBox with OnClick & OnClickCheck
« Reply #22 on: May 26, 2021, 02:28:34 am »
Can you try if example in attachment works for you?

andyH

  • Jr. Member
  • **
  • Posts: 99
Re: TCheckListBox with OnClick & OnClickCheck
« Reply #23 on: May 26, 2021, 02:58:18 pm »
Thank you for taking the time to do that. I've had a quick look and it works. I note that you have used  onclick and onitemclick (which I'd rubbished in my previous  :(). I'd been using onclick and onclickcheck. I will go back and re-check my understanding of onitemclick.

Behaviour is that you have to click (checkbox or item) first to select the item before you can then click again to change state. Not something I had thought about and was trying to get the first click to do what I wanted. Who cares if it is robust  :).

I also like the simplicity. So you have changed my afternoon, instead of doing what I was planning to do, I'll now pull apart what you've done to understand exactly how it works with a view to incorporating it in my code. Thank you again.

andyH

  • Jr. Member
  • **
  • Posts: 99
Re: TCheckListBox with OnClick & OnClickCheck
« Reply #24 on: May 26, 2021, 04:29:44 pm »
I think you are exploiting a bug in lazarus, and an old one at that :), just found:
https://forum.lazarus.freepascal.org/index.php?topic=12319.0

Added a bit of debug code to yours and:
  • Clicking on the checkbox fires onclick and onitemclick (the order reverses between the first and subsequent clicks). It would also fire onclickcheck if that was being monitored.
     
  • Whereas clicking on the item only fires onclick.
The (limited) documentation states "OnItemClick - event handler for a mouse click on the data item associated with the checkbox". No it doesn't, exactly the opposite - only fired when clicking on the checkbox. So want an event handler that is unique to the checkbox, use onitemclick, completely counter-intuitive.

Part of the learning experience...   :'(

 

TinyPortal © 2005-2018