Recent

Author Topic: TListBox with Item ID's  (Read 2432 times)

Weitentaaal

  • Full Member
  • ***
  • Posts: 172
TListBox with Item ID's
« on: May 04, 2021, 08:49:30 am »
Hello Guys :)

Lately i was about to rewrite Some of my Code.

I Had 2 ListBoxes where 1 should display all selected items and the other the non-selected items.
All fine until i had to sort it by relevance. So i had to Idetnify my items so i could write a proper sorting logarithm for my items.

Is it somehow possible to make something like this happen ?:

Code: Pascal  [Select][+][-]
  1.    
  2.   TSZ = Object
  3.       ID : integer;
  4.       Name : String;
  5.   end;
  6.  
  7.  
  8. procedure TForm1.FormShow(Sender: TObject);
  9. var
  10.    sz : Array of TSZ;
  11.    Tests : TSZ;
  12.    i : Integer;
  13. begin
  14.    for i := 0 to 10 - 1 do begin
  15.       //Tests.setName('Test' + i.ToString);
  16.       //Tests.setID(i);
  17.       Tests.Name:= 'Test'+ i.ToString;
  18.       Tests.ID := i;
  19.       sz[i] := Tests;
  20.    end;
  21.  
  22.    for i := 0 to SizeOf(sz) - 1 do begin
  23.       ListBox1.Items.AddObject(sz[i].Name, sz[i]);  //<--- Error
  24.    end;
  25. end;
  26.  

Just wanted To have an "ID" property on my Items, so i dont have to compare them by Name and detect wich item it is by Name. Is there any other (better) Component then ListBox ?
« Last Edit: May 04, 2021, 08:52:57 am by Weitentaaal »
Lazarus: 2.0.6 x86_64-win64-win32/win64
FPC-Version: 3.0.4
Compiler Version: 3.2.0

y.ivanov

  • Full Member
  • ***
  • Posts: 110
Re: TListBox with Item ID's
« Reply #1 on: May 04, 2021, 11:08:08 am »
You can use ListBox1.Items.AddObject() for that, just use proper type casting:
Code: Pascal  [Select][+][-]
  1. ListBox1.Items.AddObject(ItemName, TObject(ItemId)); // ItemId is an integer
  2. ...
  3. ItemId := Integer(ListBox1.Items.Objects[index_of_item]);
  4.  

Your sample code is wrong also for another reason - because you use local variable sz it will cease to exist when you exit the FormShow() method.

If you decide to use Object[] property to point to something useful it must be allocated on heap or at least to be a pointer to a global variable.

Weitentaaal

  • Full Member
  • ***
  • Posts: 172
Re: TListBox with Item ID's
« Reply #2 on: May 04, 2021, 11:14:51 am »
Illegal type conversion (Attached Picture of Errors)

and btw. i "sz" is Global. This is just a new Project to test ListBox
Lazarus: 2.0.6 x86_64-win64-win32/win64
FPC-Version: 3.0.4
Compiler Version: 3.2.0

korba812

  • Full Member
  • ***
  • Posts: 167
Re: TListBox with Item ID's
« Reply #3 on: May 04, 2021, 11:22:16 am »
If you are using the 64 bit compiler then you must first cast to the appropriate integer type. It is best to use PtrInt, which is defined separately for each platform.

Code: Pascal  [Select][+][-]
  1. ListBox1.Items.AddObject(ItemName, TObject(PtrInt(ItemId))); // ItemId is an integer
  2. ...
  3. ItemId := PtrInt(ListBox1.Items.Objects[index_of_item]);

ASerge

  • Hero Member
  • *****
  • Posts: 1784
Re: TListBox with Item ID's
« Reply #4 on: May 04, 2021, 12:32:18 pm »
Just wanted To have an "ID" property on my Items, so i dont have to compare them by Name and detect wich item it is by Name. Is there any other (better) Component then ListBox ?
For me, the data and its representation should be separated.

Let's take, for example, some data:
Code: Pascal  [Select][+][-]
  1. type
  2.   TMyItem = record
  3.     Id: SizeInt;
  4.     Name: string;
  5.   end;
  6.  
  7.   TMyItemArray = array of TMyItem;

And somehow initialize them:
Code: Pascal  [Select][+][-]
  1. procedure PrepareData(out Data: TMyItemArray);
  2. const
  3.   CCount = 10000;
  4. var
  5.   i: SizeInt;
  6.   N: SizeInt;
  7. begin
  8.   SetLength(Data, CCount);
  9.   for i := 0 to CCount - 1 do
  10.   begin
  11.     N := Random(CCount);
  12.     Data[i].Id := N;
  13.     Data[i].Name := Format('Item %.4d', [N]);
  14.   end;
  15. end;

To represent the data, we take TListView in virtual mode:
Code: Pascal  [Select][+][-]
  1. procedure PrepareListViewAsVirtual(AListView: TListView; ACount: SizeInt);
  2. begin
  3.   AListView.ColumnClick := False;
  4.   AListView.HideSelection := False;
  5.   AListView.OwnerData := True;
  6.   AListView.ReadOnly := True;
  7.   AListView.RowSelect := True;
  8.   AListView.ShowColumnHeaders := False;
  9.   AListView.ViewStyle := vsReport;
  10.   AListView.Columns.Add;
  11.   AListView.Items.Count := ACount;
  12. end;

Now let's link the data to the view:
Code: Pascal  [Select][+][-]
  1.   TForm1 = class(TForm)
  2.     ListView1: TListView;
  3. ...
  4.   private
  5.     FData: TMyItemArray;
  6. ...
  7. procedure TForm1.FormCreate(Sender: TObject);
  8. begin
  9.   PrepareData(FData);
  10.   PrepareListViewAsVirtual(ListView1, Length(FData));
  11. end;
  12.  
  13. procedure TForm1.ListView1Data(Sender: TObject; Item: TListItem); // OnData Event
  14. var
  15.   i: SizeInt;
  16. begin
  17.   i := Item.Index;
  18.   if Math.InRange(i, 0, High(FData)) then
  19.     Item.Caption := FData[i].Name;
  20. end;

For a better display, we'll adjust it a little:
Code: Pascal  [Select][+][-]
  1. procedure TForm1.FormShow(Sender: TObject);
  2. begin
  3.   ListView1.AutoWidthLastColumn := True;
  4.   if ListView1.ItemIndex < 0 then
  5.     ListView1.ItemIndex := Low(FData);
  6. end;

Now, suppose we want to sort the array by ID. Let's use STL garrayutils unit:
Code: Pascal  [Select][+][-]
  1. type
  2.   TMyItemSortWrapper = record
  3.     class function c(const Left, Right: TMyItem): Boolean; static; // Left < Right
  4.   end;
  5.  
  6. class function TMyItemSortWrapper.c(const Left, Right: TMyItem): Boolean;
  7. begin
  8.   Result := Left.Id < Right.Id;
  9. end;
  10.  
  11. {$PUSH}
  12. {$WARN 6058 off : Call to subroutine "$1" marked as inline is not inlined}
  13. procedure SortDataById(var Data: TMyItemArray);
  14. type
  15.   TSorter = specialize TOrderingArrayUtils<TMyItemArray, TMyItem, TMyItemSortWrapper>;
  16. begin
  17.   TSorter.Sort(Data, Length(Data));
  18. end;
  19. {$POP}

And assign the sorting to some button:
Code: Pascal  [Select][+][-]
  1. procedure TForm1.Button1Click(Sender: TObject);
  2. begin
  3.   SortDataById(FData);
  4.   ListView1.Refresh;
  5. end;

To see all this in action, create an empty project, add a ListView and a Button to the Form. Add the code specified above to the desired events.
Don't forget to specify {$MODESWITCH ADVANCEDRECORDS} in the header and add "uses Math, garrayutils" in the implementation.

Weitentaaal

  • Full Member
  • ***
  • Posts: 172
Re: TListBox with Item ID's
« Reply #5 on: May 04, 2021, 12:42:04 pm »
@Korba812 and @y.ivanov with ur Suggestions it worked ! Thanks :)

@ASerge Thanks for your reply :)
 you are right data and gui should definitely be separated so i guess i will have a look at ur work. It realy looks easy and will also provide me a better overview.

Thanks to all of u again :)

Edit: Why do i need Advanced Records(Only for Sorting method ? wich i will write on my won so i dont have to use modeswitch ?) and Why should the list be virtual ?
Edit2: is it possible to use listBox instead of listView ?
« Last Edit: May 04, 2021, 01:07:13 pm by Weitentaaal »
Lazarus: 2.0.6 x86_64-win64-win32/win64
FPC-Version: 3.0.4
Compiler Version: 3.2.0

Weitentaaal

  • Full Member
  • ***
  • Posts: 172
Re: TListBox with Item ID's
« Reply #6 on: May 04, 2021, 03:38:04 pm »
What happens to the Item index when i implement a drag and drop. Will this still work wich drag and drop ?
Lazarus: 2.0.6 x86_64-win64-win32/win64
FPC-Version: 3.0.4
Compiler Version: 3.2.0

howardpc

  • Hero Member
  • *****
  • Posts: 3702
Re: TListBox with Item ID's
« Reply #7 on: May 04, 2021, 03:42:07 pm »
Edit: Why do i need Advanced Records(Only for Sorting method ? wich i will write on my won so i dont have to use modeswitch ?) and Why should the list be virtual ?
Edit2: is it possible to use listBox instead of listView ?
Advanced records are needed only for the sorting record wrapper which contains a method.
It is certainly possible to use a listbox. See the attached project which is an adaptation of ASerge's code.

Weitentaaal

  • Full Member
  • ***
  • Posts: 172
Re: TListBox with Item ID's
« Reply #8 on: May 04, 2021, 03:51:25 pm »
Yes thanks .. that was what i was looking for ? so my (hopefully) last question then is about doing this with DragDrop. I Have lets say 100 items wich will be initialized like we did in the First ListBox or Listview. But how do i keep my ID's correct when i drop some items from 1 into 2 . Is it possible to make a DataField  for both ListViews ? maybe witch a property wich tells if Parent is ListView 1 or 2.

Thanks :)
Lazarus: 2.0.6 x86_64-win64-win32/win64
FPC-Version: 3.0.4
Compiler Version: 3.2.0

 

TinyPortal © 2005-2018