Recent

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

andyH

  • Jr. Member
  • **
  • Posts: 99
TCheckListBox with OnClick & OnClickCheck
« on: May 15, 2021, 01:49:00 pm »
I suspect this is expected behaviour, but it is unwanted...

Have a TCheckListBox, and in the constructor:
Code: Pascal  [Select][+][-]
  1.   Box3.OnClick:= @Box3CK;

And in Box3CK:
Code: Pascal  [Select][+][-]
  1. Source:= Box3.ItemIndex;
  2. CheckStatus:= Checked[Source];
  3. writeln('box3 clicked on item ',Source);
  4. //do some checks on the item, if it passes
  5. Checked[Source]:= not CheckStatus;

The problem is that:
  • if I click on the item text in the checklistbox the event handler is run once = desired behaviour
  • if I click on the item checkbox the event handler is run twice = undesired

Terminal output:
Code: Pascal  [Select][+][-]
  1. box3 clicked on item 4  <<<< clicking once on item text = run once
  2. box3 clicked on item 3  <<<< clicking once on item checkbox = run twice
  3. box3 clicked on item 3

I suspect this is because I'm changing the state of the checkbox in the event handler?

I get the same behaviour if I also set Box3.OnClickCheck:= @Box3CK as well, no surprise, but I thought I'd check.  It did crash cinnamon (windows users = my linux desktop environment), but it wasn't repeatable.

Most of the time this is not a problem, but if the item in the checklistbox fails a test an error message dialog is displayed. If the user has clicked on the checkbox, this message appears twice = undesired.

I could:
  • set an event handler for OnClickCheck that does nothing, but this would mean the user has to click on the item text for anything to happen.
  • the OnClick handler recording the item index clicked in a global var - OnClickCheck reads this and if no change does nothing. Downside - the user has to then click on another checkbox before they can change the state of the original checkbox.
  • as above, but include time clicked, if OnClickCheck event handler called within say, 100ms, do nothing. But this is starting to get complicated...
Is there a better way, all I want is my event handler to run once whether the user clicks on the item text or checkbox?





winni

  • Hero Member
  • *****
  • Posts: 3197
Re: TCheckListBox with OnClick & OnClickCheck
« Reply #1 on: May 15, 2021, 02:27:12 pm »
Hi!

That's bad design because you created an endless loop by default.

But without changing the design you can get around the problem:

   
Code: Pascal  [Select][+][-]
  1.  Source:= Box3.ItemIndex;
  2.     CheckStatus:= Checked[Source];
  3.     writeln('box3 clicked on item ',Source);
  4.     //do some checks on the item, if it passes
  5.     Box3.OnClick := Nil;
  6.     Checked[Source]:= not CheckStatus;
  7.     Box3.OnClick :=  @Box3CK;
  8.  

Winni

VTwin

  • Hero Member
  • *****
  • Posts: 1215
  • Former Turbo Pascal 3 user
Re: TCheckListBox with OnClick & OnClickCheck
« Reply #2 on: May 15, 2021, 03:31:44 pm »
What widget set? I ran into a similar problem with TComboBox on Carbon, setting it in code was triggering the OnChange event.

It did not happen in Windows or Linux, so I assumed it was a Carbon bug. However, I was told it was expected behavior. My work around (an alternative to winni's, but not necessarily better) was to have a "fIsSetting" flag. The TComboBox checked this and exited if true.

Now Cocoa does not do this, so I assume it was a Carbon bug. Now Mac, Linux, and Windows all behave the same.

You might try this on several platforms, it sounds like a bug to me. I agree with jamie that this is at least undesirable behavior.
« Last Edit: May 15, 2021, 03:42:38 pm by VTwin »
“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)

VTwin

  • Hero Member
  • *****
  • Posts: 1215
  • Former Turbo Pascal 3 user
Re: TCheckListBox with OnClick & OnClickCheck
« Reply #3 on: May 15, 2021, 04:59:11 pm »
Perhaps I misunderstood. I tried to reproduce the bug with a simple example. This project works as expected on Windows, Linux, and Mac.

Can you post an example to illustrate the bug?
“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 #4 on: May 15, 2021, 05:33:23 pm »
GTK2 and the end application is linux (ubuntu) only.

Based on the comments I looked and realised I was also setting selection = false, which I think was the primary cause of the event handler firing twice. Most of my problems have gone away.

I tried winnie's suggestion:
Code: Pascal  [Select][+][-]
  1. Box3.OnClick:= Nil;
  2. Checked[Source]:= not CheckStatus;
  3. Box3.OnClick:= @Box3CK;
Problem here is that first click on item text does nothing, subsequent clicks toggle state. Clicking on the checkbox does not toggle state (no event handler defined for OnClickCheck).

In this checklistbox, the first item is the partition table in a backup:
Code: Pascal  [Select][+][-]
  1.        if Source = 0 then //it's the partition table - if changed it must be restored
  2.           begin
  3.             Checked[Source]:= true; //default assuming part table has changed
  4.             if CheckPartTable then Checked[Source]:= not CheckStatus;
  5.             //check to see if there is an extended partition, its state = state of part table
  6.             //for i:= 1 to Count - 1 do if pos(ExtendStr,Items[i]) > 0 then Checked[i]:= Checked[Source];
  7.             exit;
  8.           end;
  9.  
This is now behaving as it should - if the partition table has changed, the checkbox MUST be enabled. Although I have managed click on the item text occasionally and the handler not fire (but not in a repeatable fashion). Also managed to get to a point where I could toggle the checkbox with the Enter key - something I need to track down, doing a restore and not restoring a partition table that has changed would be a disaster.

Making progress, thanks to the comments received, but not quite there yet.
thanks  :)

andyH

  • Jr. Member
  • **
  • Posts: 99
Re: TCheckListBox with OnClick & OnClickCheck
« Reply #5 on: May 15, 2021, 06:33:33 pm »
Two remaining issues.

This is a skeleton of the event handler for OnClick (there is no event handler for OnClickCheck at the moment):
Code: Pascal  [Select][+][-]
  1. procedure TRestore.Box3CK(Sender: TObject);
  2. const
  3.   ExtendStr = 'extended';
  4.   PartStr = '[partition]';
  5. var
  6.   CheckStatus : boolean;
  7.   Source, i : byte;
  8.   Msg1 : string;
  9. begin
  10.   if Box3.ItemIndex < 0 then exit;
  11.   Source:= Box3.ItemIndex;
  12.   with Box3 do
  13.      begin
  14.        CheckStatus:= Checked[Source];
  15.        //do tests for specific items, set checkbox true or false as appropriate and exit
  16.        
  17.        //got here - then a default item
  18.        Checked[Source]:= not CheckStatus;
  19.      end
  20. end;
Click on the item text for a 'default item', it works and the checkbox is toggled, but click on the checkbox and no change, so change to Checked[Source]:= CheckStatus and this now works for the checkbox - clicking it toggles state, but now clicking on item text does nothing.

So I need to define a handler for OnClickCheck, that could set a flag and then call Box3CK which then checks the state of the flag and toggles the Checked[Source]:= statement, or is there a better way - can Box3CK determine whether it was called by OnClick or OnClickCheck?

Final issue - being able to toggle state with the Enter key. I assume I can fix this by using one of the OnKeyxxx hooks pointing at a procedure that does nothing?

VTwin

  • Hero Member
  • *****
  • Posts: 1215
  • Former Turbo Pascal 3 user
Re: TCheckListBox with OnClick & OnClickCheck
« Reply #6 on: May 15, 2021, 08:24:13 pm »
It is good to post a full project rather than code snippets, as folks are more likely to test it.

In addition to checking for ItemIndex < 0, you need to check for ItemIndex >= Items.Count, otherwise an exception occurs if clicking below the last item (see my example).

Glad you are making progress.


“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 #7 on: May 15, 2021, 08:31:29 pm »
Quote
need to check for ItemIndex >= Items.Count
Thanks for that.

As to posting a full project - at the moment it is 3,500 lines and growing  :)

andyH

  • Jr. Member
  • **
  • Posts: 99
Re: TCheckListBox with OnClick & OnClickCheck
« Reply #8 on: May 16, 2021, 04:03:29 pm »
My final problem, which is now becoming slightly frustrating:
  • Clicking on the item text does not change the state of of the checkbox, so in the event handler a statement of the form Checked[Source]:= not Checked[Source] works, it toggles the checkbox, but
     
  • Clicking on the checkbox itself does change the state of the checkbox so Checked[Source]:= not Checked[Source] simply resets the checkbox to the state it was before the user clicked it.
This means that a single event handler, OnClick, cannot handle the user clicking on either the item or the checkbox. I thought OnItemClick would be the answer to a maiden's prayer - have separate event handlers for OnClickCheck and OnItemClick. Then I found this:

https://forum.lazarus.freepascal.org/index.php/topic,42425.msg296186.html#msg296186
Quote
OnItemClick is also fired when the check box is clicked - duplicate of OnClickCheck.
This was 2018, suspect nothing has changed and I'm GTK.

At the moment my options appear to be
  • Do nothing - accept that the user has to click on the item to change state.
     
  • Find some way of saving the state of the TCheckListBox before the user clicks on any item and then maintaining this shadow copy = sounds like a lot of work, getting in the way of the rest of the application.
     
Grateful for any opinion on this.

BTW - is there a way of inserting in-line code as opposed to code blocks, I use it on other forums but haven't found it here, hence the use of blue above.

lucamar

  • Hero Member
  • *****
  • Posts: 4219
Re: TCheckListBox with OnClick & OnClickCheck
« Reply #9 on: May 16, 2021, 04:11:50 pm »
BTW - is there a way of inserting in-line code as opposed to code blocks, I use it on other forums but haven't found it here, hence the use of blue above.

Yep, using:
    [tt]This is code[/tt]
gives:
    This is code.

Use the "Tt" button in the reply editor.
« Last Edit: May 16, 2021, 04:13:32 pm by lucamar »
Turbo Pascal 3 CP/M - Amstrad PCW 8256 (512 KB !!!) :P
Lazarus/FPC 2.0.8/3.0.4 & 2.0.12/3.2.0 - 32/64 bits on:
(K|L|X)Ubuntu 12..18, Windows XP, 7, 10 and various DOSes.

andyH

  • Jr. Member
  • **
  • Posts: 99
Re: TCheckListBox with OnClick & OnClickCheck
« Reply #10 on: May 16, 2021, 04:24:17 pm »
Yep, using:
    [tt]This is code[/tt]
Use the "Tt" button in the reply editor.
Thanks!

andyH

  • Jr. Member
  • **
  • Posts: 99
Re: TCheckListBox with OnClick & OnClickCheck
« Reply #11 on: May 17, 2021, 05:17:08 pm »
Now I am frustrated!

Have a single OnClick handler as this is supposed to trigger by clicking on either the checkbox or the item, Box3.OnClick:= @Box3CK;

To deal with the issues of clicking on the checkbox changing state, while clicking on the item does not visually change the checkbox state, I now have a custom TCheckListBox containing BoxCheck : array of boolean that mirrors the state of the checkboxes. Solves that problem.

The start of the event handler, very first line, has writeln('box3 clicked on item ',Box3.ItemIndex); So I can see when Box3CK gets fired.

In the event handler, Box3CK, the test on the first item in the TCheckListBox is 'has the partition table changed since the backup'. The code:
Code: Pascal  [Select][+][-]
  1.   with Box3 do //a set of if statements each exiting the procedure
  2.      begin
  3.        if Source = 0 then //it's the partition table - if changed it must be restored
  4.           begin
  5.             Checked[Source]:= true; //default assuming part table has changed
  6.             if CheckPartTable then //then okay to toggle
  7.                begin
  8.                  Checked[Source]:= not BoxCheck[Source];
  9.                  BoxCheck[Source]:= Checked[Source]; //save the new state
  10.                end
  11.             else BoxCheck[Source]:= true; //must be restored
  12.             //check to see if there is an extended partition, its state = state of part table
  13.             //for i:= 1 to Count - 1 do if pos(ExtendStr,Items[i]) > 0 then Checked[i]:= Checked[Source];
  14.             exit;
  15.           end;
Tested in a scenario where the partition table has changed since the backup so it MUST be restored = checkbox should always be true. CheckPartTable generates a message dialog if the part table has changed warning the user.

Testing - compile, run, select backup, box3 get populated (default all checkboxes = true):
  • Click on checkbox for item 0, event handler fires, message displayed, checkbox stays true = correct behaviour.
     
  • Click on checkbox a second time - event handler not fired, but checkbox showing false - see screenshot. This should not happen - it should fire the event handler.
     
  • Subsequent clicks on the checkbox - it behaves as it should, Box3CK fired, checkbox set true.
     
  • Clicking on the item, not the checkbox = behaves as it should.

To fix the problem, tried setting Box3.OnClickCheck:= @Box3CK as well as using OnClick. Seems to solve the problem, but now, of course, the handler is fired twice when you click on the checkbox = warning message displayed twice to user  :(

Yes it is a bit of a rant, but not sure how to proceed.

dseligo

  • Hero Member
  • *****
  • Posts: 1177
Re: TCheckListBox with OnClick & OnClickCheck
« Reply #12 on: May 17, 2021, 07:37:23 pm »
As to posting a full project - at the moment it is 3,500 lines and growing  :)

He didn't mean the whole project you are working on. He means that you create small project which shows problem, and that we can compile and test.
I think I might have solution for you using OnClick and OnItemClick events.

andyH

  • Jr. Member
  • **
  • Posts: 99
Re: TCheckListBox with OnClick & OnClickCheck
« Reply #13 on: May 18, 2021, 04:32:02 pm »
you do not need to click on the Check box although this event will also still get called if you do..
That is the issue, I can't control whether the end user clicks the checkbox or the item, so have to cater for both. Have sorted out the different behaviour of clicking on the checkbox (checkbox changes state) and clicking on the item (checkbox does not change state), but in my previous found a set of circumstances where clicking on the checkbox changed its state but did not fire the event handler?

I'll try to find time over the next few days to compile a sample project (non computer priorities getting in the way).

General question, is a tar.gz acceptable or is zip preferred?

lucamar

  • Hero Member
  • *****
  • Posts: 4219
Re: TCheckListBox with OnClick & OnClickCheck
« Reply #14 on: May 18, 2021, 04:58:45 pm »
General question, is a tar.gz acceptable or is zip preferred?

It should be: most Windows archivers (Windows being where one might find problems) support both formats (tar and gz). But if at all possible it's better to use zip; it has become a de-facto standard.
Turbo Pascal 3 CP/M - Amstrad PCW 8256 (512 KB !!!) :P
Lazarus/FPC 2.0.8/3.0.4 & 2.0.12/3.2.0 - 32/64 bits on:
(K|L|X)Ubuntu 12..18, Windows XP, 7, 10 and various DOSes.

 

TinyPortal © 2005-2018