Recent

Author Topic: Loading string-object pairs in a TComboBox  (Read 7271 times)

Relativity

  • Full Member
  • ***
  • Posts: 103
Loading string-object pairs in a TComboBox
« on: August 24, 2015, 04:01:53 pm »
I am trying to load a series of strings in a combo, so that the user can see them and choose one of them. To each stringX is a TObject(stringY) associated.
But after doing that I always find as result that the TObject(stringY)'s are not correctly associated to the stringX's as I meant.
For example I tried something simple like this, with CB_KKW a TComboBox of 9 Items and memo1 a TMemo to show the results:

CB_KKW.Clear;
memo1.Clear;
for counter := 0 to 8 do
  CB_KKW.Items.InsertObject(counter,IntToStr(counter*2),TObject(counter*3));

for counter := 0 to 8
  Memo1.Append( IntToStr(counter) + '-'
                         + CB_KKW.Items.Strings[counter] + '-'
                         + IntTostr(Integer(CB_KKW.Items.Objects[counter])));

I expected to see in the memo something like:
0-0-0
1-2-3
2-4-6
3-6-9
etc..

Instead in the memo I find:
0-0-0
1-10-0
2-12-0
3-14-0
4-16-0
5-2-3
6-4-6
7-6-9
8-8-24


and in the combo the user sees:

0
10
12
14
16
2
4
6
8

I find the values completely disarranged.
Is there any index or offset that must be initialized before loading the values in the TComboBox ?
Because it's clear that I haven't understood how it works.
Thank you.
"How'm I gonna get through?"
  -- Pet Shop Boys

molly

  • Hero Member
  • *****
  • Posts: 2330
Re: Loading string-object pairs in a TComboBox
« Reply #1 on: August 24, 2015, 05:14:47 pm »
This is normal behavior if you have your strings in the combo-box sorted.

I have no idea if you realize that those strings are sorted in ASCII alphabetical order , and not in numerical order (as you seem to expect).

In case you have control over the numerical order in which items are added/inserted, then set the sorted property of the combobox to false _before_ adding or inserting your objects.

If that's not possible then the other option is to use a custom sort routine (based on ASCII numeric sorting) in your combobox item strings.
« Last Edit: August 24, 2015, 05:27:11 pm by molly »

Relativity

  • Full Member
  • ***
  • Posts: 103
Re: Loading string-object pairs in a TComboBox
« Reply #2 on: August 24, 2015, 05:59:25 pm »
molly, you are right.
Everything has been sorted in ASCII alphabetical order and I had not realized that.

But I expected that when I associate a certain TObject to a certain String this association can not be broken, and the same should apply to the association index-string.
Instead, the set of strings and the set of TObject's undergo internal ASCII-sorting indipendently from each other and irrespective of the ties between index-string-TObject I had established through my insertObject statements.
And this is my problem: if the ties don't hold, what is the usage of the string-TObject associations, whereby I would like to get a TObject as function of the string selected by the user ?
"How'm I gonna get through?"
  -- Pet Shop Boys

molly

  • Hero Member
  • *****
  • Posts: 2330
Re: Loading string-object pairs in a TComboBox
« Reply #3 on: August 24, 2015, 06:17:58 pm »
Quote
And this is my problem: if the ties don't hold, what is the usage of the string-TObject associations, whereby I would like to get a TObject as function of the string selected by the user ?
I suspect that you have made an error when you draw your conclusion.

The combobox string (representation) and _associated_ Object _are_ a inseparable pair. It would be very odd if they aren't as that would beat its purpose (as you stated correctly yourself).

What might be the case is that you seem to expect something different then the results that you are presented with.

I don't have enough information to conclude what went wrong in your reasoning, but i can see that you are using quite a bit of numbers in your objects.

If all else fails, you can try to use a TList or TObjectList to hold all your objects and use the itemindex from that list as a value to the object in the combo-box (simply add the tObject from the combobox as Longword or Longint to your (external) list index.

EDIT: i've attached a small project that hopefully helps you understand the link between a combobox item + its string and object better.
« Last Edit: August 24, 2015, 07:51:12 pm by molly »

howardpc

  • Hero Member
  • *****
  • Posts: 4144
Re: Loading string-object pairs in a TComboBox
« Reply #4 on: August 24, 2015, 09:42:10 pm »
The InsertObject method uses an index which you supply... which may not give the result you imagine.
If you simply use the AddObject method (not trying to guess how the combo list will internally index the items, particularly if the list is sorted) you can achieve the effect you desire. Such as like this:

Code: [Select]
procedure TForm1.LoadComboAndMemo;
var
  counter: integer;
  obj: TObject;
begin
  CB_KKW.Clear;
  Memo1.Clear;
  for counter:=0 to 8 do
    begin
      obj:=TObject(Format('%d-%d',[counter*2, counter*3]));
      CB_KKW.Items.AddObject(IntToStr(counter), obj);
    end;
  for counter:=0 to 8 do
    memo1.Append(Format('%d-%s-%s',[counter,CB_KKW.Items[counter],string(CB_KKW.Items.Objects[counter])]));
end;

Relativity

  • Full Member
  • ***
  • Posts: 103
Re: Loading string-object pairs in a TComboBox
« Reply #5 on: August 25, 2015, 09:46:34 am »
howardpc,
I have tried your program and in the memo I get:

0-0--
1-1--
2-2-
3-3-3
4-4-4-4-
5-5-5-5-
6-6-6-6-
7-7-7-7-
8-8-8-8-

But this is wrong.
That is, the first two columns are correct and effectively in the combo I see the items 1,2,3,4 ecc.. but your objects have gone lost: in the third column I should see 0,2,4,6 etc. and in the fourth one 0,3,6,9, etc.

Sorry for this mess with all these numbers, it was just a simple way to show elements that are bound to each other (counter -> counter*2 -> counter*3).
"How'm I gonna get through?"
  -- Pet Shop Boys

Bart

  • Hero Member
  • *****
  • Posts: 5575
    • Bart en Mariska's Webstek
Re: Loading string-object pairs in a TComboBox
« Reply #6 on: August 25, 2015, 10:29:50 am »
A note.
The Items property of the TComboBox is internally of a widgetset dependant TStrings derived type (see TCustomComboBox.InitialzeWnd).
You have to inspect this implementation's assign (or addstrings etc) methods to see if the objects you have assigned to the items will be copied to that implemenation.
(E.g. for a TMemo, which has a similar widgetset implementation, this is not the case).

Bart

molly

  • Hero Member
  • *****
  • Posts: 2330
Re: Loading string-object pairs in a TComboBox
« Reply #7 on: August 25, 2015, 10:35:26 am »
I've attached another small sample project, showing what i believe goes wrong with your reasoning.

If you create the list for the first time then the counter you use matches the item-index used in the combobox, which is purely coincidental.

You do not 'link' the counter to the item index with your calculate values, therefor, if you let the Combobox do strange things like inserting at certain positions or let the combobox strings do that (automatically) for you when in sort mode, your 'connection' between the counter and itemindex is lost by default.

As a sidenote, TObject is a number, not a string.

is this the output what you seek (before sorting that is) ?
Code: [Select]
----- This is the list of items as added to the combobox -----
0-0-0
1-2-3
2-4-6
3-6-9
4-8-12
5-10-15
6-12-18
7-14-21
8-16-24
----- This is the list of items in the combobox but sorted -----
0-0-0
1-10-15
2-12-18
3-14-21
4-16-24
5-2-3
6-4-6
7-6-9
8-8-12
----- Note that your "link" with the itemindex is lost     -----
----- after reorganizing items in the ComboBox             -----

Note that after sorting the 'link' between counter and itemindex is 'lost' as the itemindexes where changed after sorting, but the other two values still do match in pairs as you initially defined them.
« Last Edit: August 25, 2015, 10:46:00 am by molly »

howardpc

  • Hero Member
  • *****
  • Posts: 4144
Re: Loading string-object pairs in a TComboBox
« Reply #8 on: August 25, 2015, 11:23:36 am »
howardpc,
I have tried your program and in the memo I get:

0-0--
1-1--
2-2-
3-3-3
4-4-4-4-
5-5-5-5-
6-6-6-6-
7-7-7-7-
8-8-8-8-

But this is wrong.
That is, the first two columns are correct and effectively in the combo I see the items 1,2,3,4 ecc.. but your objects have gone lost: in the third column I should see 0,2,4,6 etc. and in the fourth one 0,3,6,9, etc.

Sorry for this mess with all these numbers, it was just a simple way to show elements that are bound to each other (counter -> counter*2 -> counter*3).

Indeed. The problem (I overlooked this) is that simple casting of strings to TObject, while allowed by the compiler, does not have the same effect as casting an ordinal value (say an integer) to a TObject. (Ansi)Strings are reference counted, and so the cast, while legal, has unwanted side effects. You quickly lose track of the string's value because each string is compiler-managed, and you are interfering with that (hidden) management and reference count. (Casting a string as a non-string is saying to the compiler "Trust me, this is not a string" when in fact it is).

To do what you want properly you have to go to the trouble of constructing a 'proper' containing object, on which a TObject() cast is deterministic. You also then have to worry about freeing these objects afterwards.
This results in code such as this:
Code: [Select]
type

 TStrObject = class
 strict private
   FStr: string;
 public
   constructor CreateWithString(aStr: string);
   property Str: string read FStr;
 end;

{ TStrObject }

constructor TStrObject.CreateWithString(aStr: string);
begin
  FStr:=aStr;
end;

procedure TForm1.LoadComboAndMemo;
var
  counter: integer;
  obj: TStrObject;
  s: string;
begin
  CB_KKW.Clear;
  Memo1.Clear;
  for counter:=0 to 8 do
    begin
      s:=Format('%d-%d',[counter*2, counter*3]);
      obj:=TStrObject.CreateWithString(s);
      CB_KKW.Items.AddObject(IntToStr(counter), obj);
    end;
  for counter:=0 to 8 do
    memo1.Append(Format('%d-%s-%s',
                        [counter,
                         CB_KKW.Items[counter],
                         TStrObject(CB_KKW.Items.Objects[counter]).Str]));
end;           

procedure TForm1.FormDestroy(Sender: TObject);
var
  i: integer;
begin
  for i:=0 to CB_KKW.Items.Count-1 do
    TStrObject(CB_KKW.Items.Objects[i]).Free;
end;

sfeinst

  • Full Member
  • ***
  • Posts: 245
Re: Loading string-object pairs in a TComboBox
« Reply #9 on: August 25, 2015, 02:16:03 pm »
But I expected that when I associate a certain TObject to a certain String this association can not be broken, and the same should apply to the association index-string.

From what I can see from your message, I do not see a break in the string to TObject association.  If you remove the index number from your printout and just show the string and object for the given loop index, you stated you are expecting:
2-3
4-6
6-9

You actual output shows those same value combinations.

The only break you are seeing is your assumption of the index value to the string/object combination because you used InsertObject at a specific index and expect the combination to remain at that index.  But if you are sorting the TComboBox, that is not a valid assumption.  You should never assume an index anyway.

When a user selects a string, you can find out its index.  From that index you can get the correct TObject.  As for order of appearance in the TComboBox, follow the suggestions of either turning off the sort and controlling it from your code or adding a custom sort.

Relativity

  • Full Member
  • ***
  • Posts: 103
Re: Loading string-object pairs in a TComboBox
« Reply #10 on: August 25, 2015, 04:30:34 pm »
molly, you are perfectly right.
I have also tried your last program: it works and it works also (I have modified it) using strings.
Thanks to everybody.

I am still in trouble with my program and my TComboBox, but this issue in particular has been totally cleared.
"How'm I gonna get through?"
  -- Pet Shop Boys

 

TinyPortal © 2005-2018