I have a simple question. How to get the count of checked item in CheckBoxListBox without using a loop?
TCheckListBox does not provide the option that you are looking for. A loop through its Checked[] property is required.
If you were using Delphi, you could create a class helper to hide that loop. But class helpers are not available in C++.
you can use a function like this.
function GetCheckedCount(CH:TCheckListBox):Integer;
var I:Integer;
begin
Result := 0;
for i := 0 to ch.Items.Count - 1 do
if ch.Checked[i] then inc(result);
end;
Also, SelCount is the number of "selected" items when MultiSelect is true ,Not number of "Checked" items
In Delphi you can [*] do the following:
TCustomMultiSelectListControl(TheBox).MultiSelect := True;
and then SelCount works:
CountOfCheckedItems := TheBox.SelCount;
Isn't the equivalent possible in C++?
[*] Although it causes other problems.
Related
I have a PopupMenu with submenus and only one item in total shall be checked at a time. GroupIndex and RadioItem properties do not work outside of the respective submenus as far as I have tried.
I have found this piece of code to check a PopupMenu and its direct sub-components but I haven't had any luck with creating a popup-wide variety of this.
I need a solution that is fast - the PopupMenu has 4x14 entries, always iterating through all menus and subentries can't be the best solution for this, I suppose.
Is there a simple property for this that I am missing or is the rocky path of iteration my only option?
Add all 56 items as actions to one ActionList and give all GroupIndex properties the same value.
Now, add menu-items, sub-menu's and sub-sub-menu's in any tree-like fashion and link each of them to an action. Checking one menu-item, wherever positioned, will automatically uncheck all others.
Et voilĂ !
NGLN's answer is better, but if you really don't want or don't like to use an ActionList, then this routine will also do:
procedure CheckMenuItem(Item: TMenuItem);
procedure UncheckMenu(Menu: TMenuItem; GroupIndex: Byte);
var
I: Integer;
begin
if Menu.RadioItem and (Menu.GroupIndex = GroupIndex) then
Menu.Checked := False;
for I := 0 to Menu.Count - 1 do
UncheckMenu(Menu[I], GroupIndex);
end;
begin
if (not Item.Checked) and Item.RadioItem and (Item.GroupIndex <> 0) then
begin
UncheckMenu(Item.GetParentMenu.Items, Item.GroupIndex);
Item.Checked := True;
end;
end;
In Delphi 7, I'm using a TCheckListBox. I want it to use a TStringList rather than a TStrings, so I can set Duplicates to dupIgnore, and Sorted to TRUE.
Can I just do this:
Form1 = class(TObject
CheckListBox1: TCheckListBox; // created by the IDE
end;
procedure TForm1.FormCreate
begin
CheckListBox1.Items.Free;
CheckListBox1.Items := TStringList.Create;
CheckListBox1.Items.Sorted := TRUE;
CheckListBox1.Items.Duplicates := dupIgnore;
end;
Is this safe? Any caveats or suggestions?
EDIT: Removed declaration for MyStringList and added .Items to the last two assignment lines.
EDIT 2: Trying to compile the above, it looks like I'd have to cast the two final lines like this:
TStringList(CheckListBox1.Items).Sorted := TRUE;
TStringList(CheckListBox1.Items).Duplicates := dupIgnore;
Although I might be able to get this to run, I'm asking the question because just getting it to run doesn't mean it will always run or is safe.
You don't control what class TCheckListBox uses to store its items. Assigning the Items property a value only assigns its items to the internal storage.
Also, you shouldn't call Items.Free;. TCheckListBox depends on its internal instance of TListBoxStrings.
To answer your edits in your question: Don't hard-cast the Items property to TStringList, either. The typecast is wrong (the instance exposed by Items is not a TStringList) and will only cause problems.
Edit, to suggest a workaround for what you seem to try to achieve: To keep the checklistbox sorted, you can set its Sorted property to True. To avoid duplicates, you can check the list before adding an item in code.
I'm still a beginner and I have been trying to solve this problem by my self but I guess I 'm out of luck. I think it is probably quite simple but here's the deal.
I have 3 checkboxes. Each one writes a specific line in a text file when a button is pressed but if none are selected. I want a message to be displayed. But what happens there is that the message pops out even if one checkbox is checked. Here's the code: (btw, feel free to suggest any other code that would make it easier/clearer)
if cbSCV.Checked then
WriteLn(permFile, 'scv');
if cbMP.Checked then
WriteLn(permFile, 'mp');
if cbBTK.Checked then
WriteLn(permFile, 'btk');
if not (cbBTK.Checked) and not (cbMP.Checked) and not (cbBTK.Checked) then
showmessage('Choose at least 1 option.');
try replacing the if sentence to
if not (cbBTK.Checked) and not (cbMP.Checked) and not (cbSCV.Checked) then
because you are checking the cbBTK.checked value twice
For what it's worth I'd probably reverse the logic and write the troublesome test like this:
if not (cbBTK.Checked or cbMP.Checked or cbSCV.Checked) then
To complement #soid's answer: I'd probably write it like this:
procedure TForm1.CheckIt;
var
Count: Integer;
procedure HandleCheckBox(ACheckBox: TCheckBox; const AID: string);
begin
if ACheckBox.Checked then
begin
WriteLn(permFile, AID);
Inc(Count);
end;
end;
begin
Count := 0;
HandleCheckBox(cbSCV, 'scv');
HandleCheckBox(cbMP, 'mp');
HandleCheckBox(cbBTK, 'btk');
if Count = 0 then
ShowMessage('Choose at least 1 option.');
end;
This is a few more lines but it is IMHO less error prone and more "automatic" if you later need a fourth or fifth checkbox.
I would rewrite it like this:
if cbSCV.Checked then WriteLn(permFile, 'scv');
if cbMP .Checked then WriteLn(permFile, 'mp' );
if cbBTK.Checked then WriteLn(permFile, 'btk');
if not (cbSCV.Checked) and
not (cbMP .Checked) and
not (cbBTK.Checked) then
showmessage('Choose at least 1 option.');
This takes the same number of lines but places the repeated elements together to make it easy to read the whole construct quickly and spot places where you are not following the pattern. Your bug, which we have all had in our code, is easier to see if it is written like this.
Hmmm. For those things I like a set-based approach.
One way is this
type
TEnumSomething = (esSCV, esMP, esBTK);
TSomethingSet = set of TEnumSomething;
{var section}
var
Conj: TSomethingSet;
{code section}
Conj := [];
if cbSCV.checked then
begin
Conj := conj + [esSCV];
WriteLn(permFile, 'scv');
end;
{do this for the other 2 checkboxes}
If Conj = [] then ShowMessage('');
You can also make Conj an form field and make checkboxes
set/unset this on their OnClick event.
Warning: maybe some syntax detail is missing, I'm not on delphi IDE now...
I probably wouldn't rewrite it like this, but hey, this is fun. I'm at work and I don't have Delphi here, so this is just sample code. Generics!
type
TCheckBoxDict: TDictionary<String, TCheckBox>;
var
Dict: TCheckBoxDict;
function HandleCheckBoxes(ADict: TCheckBoxDict) : boolean;
var
Key: String;
CheckBox: TCheckBox;
begin
Result := false;
for Key in ADict.Keys do
if ADict.Items[Key].Checked then
begin
WriteLn(permFile, Key);
Result := true;
end;
end;
begin
Dict := TCheckBoxDict.Create;
Dict.Add('scv', cbSCV);
Dict.Add('mp', cbMP);
Dict.Add('btk', cbBTK);
if not HandleCheckBoxes(Dict) then
ShowMessage('Choose at least one option');
Dict.Destroy;
end;
I want to update values in a TListView. I tryed this
...
lvProcess : TListView;
liEdit : TlistItem;
...
liEdit:=lvProcess.Items.Item[1];
liEdit.Caption:='11';
liEdit.SubItems.ValueFromIndex[0]:='22';
liEdit.SubItems.ValueFromIndex[1]:='33';
...
this should do what I want, but after this, the values of the subitems are this ones '=22' and '=33'
I don't want the equal character to be added.
Can anyone help me? I don't know if this is the right way to edit/update a listitem
thanks
You could try the following :
with LvProcess.Items[1] do
begin
Caption := '11';
SubItems.Strings[0] := '22';
SubItems.Strings[1] := '33';
end;
And if you're updating many items at once it is better to surround the update in a way like this:
try
lvProcess.Items.BeginUpdate;
//Do your updates
finally
lvProcess.Items.EndUpdate;
end;
SubItems is a TStrings, so if you want to update the string values, do so like this:
SubItems[0] := '22'
SubItems[1] := '33'
The way you're doing it now, you're using the TStrings as Key/Value list. That's a nice feature of TStrings when you want it, but probably not what you intend.
This is probably a stupid question, but my brain is just cooked enough I think I'm going to use one of my "lifelines" to see if I can get some help from my stack overflow friends. ;)
I need to delete all occurrences of a particular component type on my main form (some of them are inside panels or tabsheets, but all on and owned by the same form). Here's what I have now:
for i := 0 to frmMain.ComponentCount - 1 do
begin
if frmMain.Components[i] is TMyClass then frmMain.Components[i].Destroy;
end;
The problem is (and I knew it would be before I compiled it) that once I destroy the component, the form's component list re-indexes and I end up out of bounds.
What's the best way to solve this? I thought about adding the "found" components to a standalone array, and then walk through that after this loop to delete them, which I think will work.... but is that the best approach?
TIA
UPDATE:
You guys rock. Thanks. : )
You're nearly right. Your loop should look like
for i := frmMain.ComponentCount - 1 downto 0 do
begin
if frmMain.Components[i] is TMyClass then
frmMain.Components[i].Free;
end;
This way the call to the function "frmMain.ComponentCount" gets done at the beginning and not again.
You should also call Free as above, not Destroy - I can't remember why at the moment.
Bri
Start at the top and work backwards.
viz:
for i := frmMain.ComponentCount - 1 downto 0 do
begin
if frmMain.Components[i] is TMyClass then frmMain.Components[i].Free;
end;
Call free instead of Destroy. Free calls Destroy after checking for a valid reference.
It may not happen in your case, but the if frmMain.Components[i] is TMyClass check will also return true for descendant classes of TMyClass. If you are really looking for the removal of one specific class, you may need to add an extra check of the ClassName.
The same solution with a while-loop:
i := 0;
while i < frmMain.ComponentCount do
begin
if frmMain.Components[i] is TMyClass then
frmMain.Components[i].Free
else
inc(i);
end;
For Fine Contrle in form or panel can use this code
var
i:Integer;
begin
for i := 0 to Panel1.ControlCount - 1 do
begin
if Panel1.Controls[i] is TEdit then
Tedit(Panel1.Controls[i]).text := '';
end;
If you need to check & destroy a named known component
Use
If YourComponent <> Nil Then
YourComponent.Free;