Recent

Author Topic: [solved] TCkeckListBox: Select only one and prevent item selection.  (Read 13907 times)

squirreldancer

  • New member
  • *
  • Posts: 7
I have an application with 2 checklistboxes. In one, I want the user to be able to select only one item. If they check another item, the first is unchecked. I have set extended select and multiselect to false, but this doesn't seem to make any difference. Multiple items can be checked at the same time.
In the second checklist box, I want to display the same list as in the first, but disable the ability to check the item selected in the first. In the second list, the user can check as many items as they want, but not the same item selected in the first box.
Two questions:
1. How do I set up the first box so only one selection is allowed? Is this a bug? Is there simple code to work around it?
2. Is there a property to show but prevent selection of a single checklistbox item? If not, how would I go about coding for this?
« Last Edit: November 30, 2013, 04:31:42 pm by squirreldancer »

howardpc

  • Hero Member
  • *****
  • Posts: 4144
Re: TCkeckListBox: Select only one and prevent item selection.
« Reply #1 on: November 29, 2013, 06:28:26 pm »
Two questions:
1. How do I set up the first box so only one selection is allowed? Is this a bug? Is there simple code to work around it?
This is no bug. Checkboxes are designed to be optional. You are looking for radiogroup functionality combined with a list of checkboxes.
To add this functionality requires a few lines of code.

Quote
2. Is there a property to show but prevent selection of a single checklistbox item? If not, how would I go about coding for this?
The property you want is an array property called ItemEnabled.


The following skeleton unit indicates the sort of code required, which you need to add to the first checklistbox's OnItemClick event handler.

Code: [Select]
unit mainChecklistBoxes;

{$mode objfpc}{$H+}

interface

uses
  Forms, CheckLst;

type

  { TForm1 }

  TForm1 = class(TForm)
    clbMonoCheck: TCheckListBox;
    clbReverseMono: TCheckListBox;
    procedure clbMonoCheckItemClick(Sender: TObject; Index: integer);
  end;

var
  Form1: TForm1;

implementation

{$R *.lfm}

{ TForm1 }

procedure TForm1.clbMonoCheckItemClick(Sender: TObject; Index: integer);
var
  clb: TCheckListBox;
  i: Integer;
begin
  if not (Sender is TCheckListBox) then Exit;
  clb:=TCheckListBox(Sender);
  for i:= 0 to clb.Items.Count-1 do begin
    if i=Index then Continue;
    if clb.Checked[i] then clb.Checked[i]:= not clb.Checked[i];
  end;
  for i:= 0 to clb.Items.Count-1 do
    clbReverseMono.ItemEnabled[i]:=True;
  if clb.Checked[Index] then
    clbReverseMono.ItemEnabled[Index]:=False;
end;

end.       

Note that synchronising two (or more) controls in this way really requires additional code to make sure the two listboxes have identical arrays of items. If they differ at all the above code would fall over, of course, either not working as intended, or giving rise to exceptions or crashes. However, I'll leave it to you to sort out that side of it.

squirreldancer

  • New member
  • *
  • Posts: 7
Re: TCkeckListBox: Select only one and prevent item selection.
« Reply #2 on: November 29, 2013, 07:55:37 pm »
Thank you for your quick and thorough reply. I learned my Pascal on a Vax a few decades ago, and I'm really not too sharp with the Object stuff (happy to learn it, though!) so if I could add 2 more questions?

1. What does the "Multiselect=false" property do, if it doesn't prevent multiple selections?

2. Why do I need "if not (Sender is TCheckListBox) then Exit;". Could any other sender "call" (if that is the right word) the procedure?

Incidentally, the two checklistboxes are presented to the user sequentially. The user must pick one field from the first, then they see the second, where they can pick any or all of the remaining fields, but I want the field chosen in the first list to remain in place for reference. I don't need to synchronize the two list boxes -- I just need to prevent one item from being chosen in the second list, but still have it visible.

Thank you again.

howardpc

  • Hero Member
  • *****
  • Posts: 4144
Re: TCkeckListBox: Select only one and prevent item selection.
« Reply #3 on: November 29, 2013, 11:30:07 pm »
1. What does the "Multiselect=false" property do, if it doesn't prevent multiple selections?

It does exactly what it says on the tin, preventing more than one item being selected if False. But don't misread "checked" for "selected". Because TChecklistBox descends from TListBox, most of its properties are related to listbox functionality. Hence when you click a checkbox to check it you also select and highlight the entire listbox item line. This is not needed for the checkbox functionality, indeed it could be considered an unwanted feature. However the selection is built in to the listbox ancestor, and would be difficult (perhaps impossible) to remove. For your application selection (or unselection) is irrelevant. You're only interested in whether a checkbox is checked or not.

Quote
2. Why do I need "if not (Sender is TCheckListBox) then Exit;". Could any other sender "call" (if that is the right word) the procedure?

The typecast TCheckListBox(Sender) is unsafe - it will cause an exception or worse if it fails - unless you can guarantee that Sender is a TCheckListBox. Although it is hard to conceive of a situation in which Sender would not be the control you expect, data coming from outside your routine is outside your control. Just because you expect it to be a TCheckListbox does not guarantee that it will be. Better safe than sorry. So I'm more of a belt-and-braces person (if I were American that would be belt-and-suspenders).

Quote
I don't need to synchronize the two list boxes -- I just need to prevent one item from being chosen in the second list, but still have it visible.

My point is that as it is written the code depends on the items in the two listboxes being identical, but does not ensure that they are. That is a problem waiting to happen if you ever share this code with others who don't realise that the code has this hidden requirement.
So it ought to be documented in comments or an FPDoc file. Or you should add code that tests for identical items and (say) raises an exception if the listbox items differ.

squirreldancer

  • New member
  • *
  • Posts: 7
Re: [solved] TCkeckListBox: Select only one and prevent item selection.
« Reply #4 on: November 30, 2013, 04:35:52 pm »
Thanks for another detailed answer. I learned Delphi 1 as part of a "programming for scientists" course, back in the dark ages (Win 98?). Now I'm trying to get back into programming with Lazarus to deal with data handling problems that things like Excel just can't manage.
The forums are proving to be a great resource.

BigChimp

  • Hero Member
  • *****
  • Posts: 5740
  • Add to the wiki - it's free ;)
    • FPCUp, PaperTiger scanning and other open source projects
Re: TCkeckListBox: Select only one and prevent item selection.
« Reply #5 on: November 30, 2013, 05:41:25 pm »
So I'm more of a belt-and-braces person (if I were American that would be belt-and-suspenders).
Hard to imagine that given your accent ;)
<GDR> or <SCNR>, take your pick ;)

(Agreed with belts & braces though, very sensible approach)
Want quicker answers to your questions? Read http://wiki.lazarus.freepascal.org/Lazarus_Faq#What_is_the_correct_way_to_ask_questions_in_the_forum.3F

Open source including papertiger OCR/PDF scanning:
https://bitbucket.org/reiniero

Lazarus trunk+FPC trunk x86, Windows x64 unless otherwise specified

 

TinyPortal © 2005-2018