Limiting checked items of TCheckListBox on Delphi - delphi

I want to limit a TCheckListBox.
I desire only 2 items should be checked, and all unchecked items will be disabled and grayed.
Since the checked / unchecked items are dynamic, i can not use a static itemIndex.
Here is what i tried, but i got "Out of chip bounds" error.
On click event of my CheckListBox ;
var
NumberOfCheckedItems, I: Integer;
begin
NumberOfCheckedItems := 0;
for I := 0 to CkLst1.Count - 1 do
begin
if CkLst1.Checked[I] then
NumberOfCheckedItems := NumberOfCheckedItems + 1;
end;
if NumberOfCheckedItems > 1 then
begin
CkLst1.Checked[I] := Enabled;
CkLst1.Enabled := FALSE;
CkLst1.AllowGrayed := TRUE;
end
else
begin
//no idea
end;
end;

This method should do the job
procedure DoCheckListBox( AChkLb : TCheckListBox; AMaxCheck : Integer );
var
LIdx : Integer;
LCheckCount : Integer;
begin
// counting
LCheckCount := 0;
for LIdx := 0 to AChkLb.Count - 1 do
begin
if AChkLb.Checked[LIdx] then
if LCheckCount = AMaxCheck then
AChkLb.Checked[LIdx] := False
else
Inc( LCheckCount );
end;
// enable/disable
for LIdx := 0 to AChkLb.Count - 1 do
AChkLb.ItemEnabled[LIdx] := AChkLb.Checked[LIdx] or ( LCheckCount < AMaxCheck );
end;
UPDATE
You better call this inside TCheckListBox.OnClickCheck event instead of OnClick event.
A double-click can affect the check-state but OnClick is not called.
OnClickCheck is called whenever the check-state changes.

Related

ActionMainMenuBar and auto-hiding doubled separators

with TMainMenu we have AutoLineReduction property to hide doubled separators when menu item is hidden, how to do the same with ActionMainMenuBar and ActionManager?
I didn't find an internal method for this, but we can do it manually.
We have to add the OnPopup method to the ActionMainMenuBar:
procedure TFormMain.MenuBarPopup(Sender: TObject; Item: TCustomActionControl);
begin
// Make all separators visible
for var I := 0 to Item.ActionClient.Items.Count - 1 do begin
var Itm := Item.ActionClient.Items[I];
if (Itm.Caption = '-') then
Itm.Visible := True;
end;
// Hide doubled separators
for var I := 0 to Item.ActionClient.Items.Count - 1 do begin
var Itm := Item.ActionClient.Items[I];
if (Itm.Caption = '-') then begin // Search next separator
var bFound := False;
for var J := I + 1 to Item.ActionClient.Items.Count - 1 do begin
var Itm2 := Item.ActionClient.Items[J];
if Itm2.Visible then begin
bFound := (Itm2.Caption <> '-');
Break;
end;
end;
Itm.Visible := bFound;
end;
end;
end;
Is it very strange that this component does not contain such a property...

How to read an integer value into a TCheckColumn in Delphi FMX with TStringGrid?

I need to make the TCheckColumn from the FMX.StringGrid to work from an integer value but I don't know how.
My code reads from a JSON request and translates it to a stringgrid. In the database the "boolean" field is stored as integer, so 0 for false and 1 for true.
This is the code that reads from the request:
procedure TDM.CarregaDados(aTable: string; aGrid: TStringGrid);
begin
TThread.CreateAnonymousThread(
procedure
var
str: string;
begin
aGrid.RowCount := 0;
REST.Response := nil;
REST.Resource := aTable;
REST.Method := rmGET;
REST.Params.ClearAndResetID;
REST.Execute;
RESTDSA.Response := REST.Response;
RESTDSA.DataSet := RESTDS;
RESTDSA.Active := true;
TThread.Synchronize(nil,
procedure
var
I: Integer;
begin
aGrid.BeginUpdate;
while not RESTDS.Eof do
begin
aGrid.RowCount := aGrid.RowCount + 1;
for I := 0 to RESTDS.FieldCount - 1 do
aGrid.Cells[I, aGrid.RowCount - 1] := RESTDS.Fields.Fields
[I].AsString;
RESTDS.Next;
end;
aGrid.EndUpdate;
end);
REST.ClearBody;
REST.Params.ClearAndResetID;
end).Start;
end;
REST is the TRESTRequest component,
RESTDS is the TFDMemTable,
RESTDSA is the TRESTRequestDataSetAdapter component,
aGrid is a TStringGrid and
aTable is the endpoint resource.
What I wanna know is how I can tweak this code to make it work with TCheckColumn in my grid. Yes, Of course I have a TIntegerColumn, a TStringColumn and a TCheckColumn previously added to the grid.
This is an example JSON response:
[
{
"ID" : 1,
"Descr" : "test",
"ischeck" : 0
},
{
"ID" : 2,
"Descr" : "test",
"ischeck" : 1
}
]
Well I know It's late, but I am a newbie here, and it's the first time I use FMX.TStringGrid without Livebindings.
I found a solution to this problem, with my own data
procedure TCsv4Presta.StringGrid1CellClick(const Column: TColumn;
const Row: Integer);
begin
case Column.Index of
0 : begin // my checkboxcolumn
StringGrid1.Cells[0,Row]:= BooltoStr(Not StrToBool(StringGrid1.Cells[0,Row]),true);
Column.UpdateCell(Row); // important to refresh checkbox
end;
end;
end;
just a problem with this onclick, you have to manage click in the cell but not on the checkbox
So I can suggest you a code like
while not RESTDS.Eof do
begin
aGrid.RowCount := aGrid.RowCount + 1;
for I := 0 to RESTDS.FieldCount - 1 do
begin
if aGrid.Columns[I] is TCheckBoxColumn then
begin
aGrid.Cells[I, aGrid.RowCount - 1] := BooltoStr(RESTDS.Fields.Fields
[I].AsString='1',true) ;
// Column.UpdateCell(Row);
end
else aGrid.Cells[I, aGrid.RowCount - 1] := RESTDS.Fields.Fields
[I].AsString;
end;
RESTDS.Next;
end;

Why is my TListBox getting blank when its last TListBoxItem is checked?

Problem
My TListBox is getting blank when its last TListBoxItem is programatically checked. To illustrate it better, hereby what I mean by getting blank:
Context
I'm generating a list from a TJSONArray. Each item looks like {"event_code","event_name"}.
Then, I compare if the event_code is written on a second TJSONArray : json_response_available_events. If it does, the ListBoxItem will be checked.
Code
procedure TFormHome.TimerGetEventsTimer(Sender: TObject);
var
K : Integer;
Z : Integer;
ListCount : Integer;
AvailableList_Count: Integer;
lb_item: TListBoxItem;
event_code_first_array: string;
event_code : string;
event_name : string;
begin
// Disable this timer for now
TimerGetEvents.Enabled := false;
// Get List of Notifications
json_response_events := DM_Auth0.ServerMethods1Client.GetEventsCodeAndDescription(communication_token);
json_response_available_events := DM_Auth0.ServerMethods1Client.GetAllowedNotificationsList(communication_token, genset_id);
ListCount := json_response_events.Count -1;
AvailableList_Count := json_response_available_events.Count - 1;
for K := 0 to (ListCount) do
begin
// Get complete Event Code and Name
event_name := json_response_events.Items[K].toString;
// Get Event Code
event_code_first_array := StringReplace(event_name.Split([':'])[0], '"', '', [rfReplaceAll]);
// Get Event Name
event_name := StringReplace(event_name.Split([':'])[1], '"', '', [rfReplaceAll]);
// Create ListBoxItem
lb_item := TListBoxItem.Create(self);
lb_item.Parent := lb_notifications;
lb_item.Text := event_name;
lb_item.StyleLookup := 'listboxitemleftdetail';
// Check if this Item code is available
for Z := 0 to (AvailableList_Count) do
begin
if json_response_available_events.Items[Z] <> nil then
begin
// Get Event Code
event_code := json_response_available_events.Items[Z].toString;
// Format
event_code := StringReplace(event_code, '"', '', [rfReplaceAll]);
if event_code_first_array.Contains(event_code) then
begin
if K <= ListCount then
begin
lb_item.IsChecked := true;
lb_item.IsSelected := false;
end;
end;
end;
end;
end;
end;
Analysis
If we set to < only, it displays the list correctly but the last item will remain unchecked.
if K < ListCount then
begin
lb_item.IsChecked := true;
lb_item.IsSelected := false;
end;
I can even change it's properties when its = like
if K = ListCount then
begin
lb_item.Text := 'Deadpool for President';
end;
and lb_item.isChecked := false works fine, but when setting lb_item.isChecked := true it gets all weirdly blank.
Why is it happening? And if there's a better way to do what I'm doing, the help will be appreciated.

Delphi: Adding Combobox Dropdown to TADVStringGrid

I have a form that has a TADVStringGrid. I am trying to add a combobox to some rows in a specific column (2); however, I can't get the data to show on the dropdown. I added the HasComboBox event, I set the DirectComboDrop, and it still did not show any data in the dropdown. They were just empty. I check the object that I am adding to the dropdown, and they had data. What am I missing here?
procedure UserForm.DisplayGrid(Sender: TObject);
var
J : Integer;
begin
... additional logic
...
if OutputList.Count > 2 then
begin
with UserGrid.Combobox do
begin
for J := 0 to OutputList.Count - 1 do
BEGIN
if not(OutputList[J] = '') then
begin
dValue := DropDownValue.Create;
dValue.ID := J + 1;
dvalue.Name := OutputList[J];
dvalue.TestValue := OutputList[J] + 'testvalue'; // where value will be a list to choose from
ListOfTest.Add(dValue); // this is a glabal variable where I for later reference
ItemIndex := dValue.ID;
end;
END;
end;
end;
//event
procedure UserForm.UserGridHasComboBox(Sender: TObject; ACol, ARow: Integer;
var HasComboBox: Boolean);
begin
HasComboBox := True;
end;
There is an event handle called EditorProp that needed to be added. Data that need to be added for a specific column have to be added when the EditorProp event is called. The piece of code below was moved into the editorprop event, and it's working fine since.
for J := 0 to OutputList.Count - 1 do
BEGIN
if not(OutputList[J] = '') then
begin
dValue := DropDownValue.Create;
dValue.ID := J + 1;
dvalue.Name := OutputList[J];
dvalue.TestValue := OutputList[J] + 'testvalue'; // where value will be a list to choose from
ListOfTest.Add(dValue); // this is a glabal variable where I for later reference
ItemIndex := dValue.ID;
end;

how to fixed a number of dynamic buttons in 1 row?

i have create 7 dynamic buttons in a scroll box. Each row need to have 2 buttons (number of buttons may change) only, but the result of the code below shows the first row with 2 buttons but all the rest in 2nd row, how should i fixed it to 2 buttons 1 row?
procedure TForm1.CreateDynamicBtn2;
var
abtn: TBitBtn;
i, j, iNum, iCount : integer;
begin
if ScrollBox2.ControlCount > 0 then begin
for j := ScrollBox2.ControlCount - 1 downto 0 do begin
with ScrollBox2.Controls[j] AS TBitBtn do begin
Free;
end;
end;
end;
iCount := 0;
for i := 0 to 6 do begin
iNum := i + 1;
abtn := TBitBtn.Create(self);
abtn.Parent := ScrollBox2;
abtn.Visible := True;
abtn.Caption := 'dynamic' + IntToStr(i);
if iNum*abtn.Width > (iCount+2)*abtn.Width then begin
iCount := iCount + 1;
abtn.Left := (iCount * abtn.Width) - abtn.Width;
abtn.Top := abtn.Height;
end else begin
abtn.Left := i * abtn.Width;
abtn.Top := 0;
end;
end;
end;
Because you are making things way too complicated?
abtn.Left := (i mod 2) * abtn.Width;
abtn.Top := Trunc((i div 2)) * abtn.Height;
Should do the trick nicely.

Resources