11

I have an owner drawn combo box that displays the strings in columns. The drawing routine can be shared across combos if I can somehow pass the column specifications to the OnDrawItem event. A natural way to do so, would be to pass the array of column widths in the ComboBox.Tag property and then cast it back to an array.

When I define the column array as:

const arrWidth :array[1..4] of integer = (100,100,100,70);

and set the Tag property to:

ComboBox.Tag := integer(@arrWidth);

and then in the OnDrawItem event, cast it back to an array:

Widths :array of integer;
Widths := pointer(ComboBox.Tag);

I can see the array elements fine, but the array does not know it's length. It appears to be much longer with all sorts of random values.

I have tried using a dynamic array, but then I don't even get the proper column values.

2
  • 4
    Alternatives that don't involve storing array pointers in Tag include writing a descendant that stores the values in a property dedicated to the task, and storing the column widths in an associative array (like TDictionary) that maps combo-box instances to width arrays. Commented Dec 14, 2011 at 17:41
  • Thanks for the comment. I had considered creating a descendant, but deemed it too much effort. Perhaps it would make the form more maintainable in the future, but this was the first time in 15 years I wanted a combo box with columns, so I didn't think I would re-use it much. Commented Dec 14, 2011 at 17:57

1 Answer 1

21

Casts are dangerous because you go outside the type checking system. That has caught you out here. The issue is that array[1..4] of integer and array of integer are not the same type.

You need to declare your array as a distinct type like this

TWidthArray = array [1..4] of Integer;
PWidthArray = ^TWidthArray;

Then do your constant like this:

const 
  arrWidth: TWidthArray = (100,100,100,70);

When you need to extract the array from the combo box do it like this:

Widths: TWidthArray;
...
Widths := PWidthArray(ComboBox.Tag)^;

If you need to support using dynamic array lengths then you would need to change your common type to reflect that. However, beware that casting to an Integer to put in Tag will bypass the reference counting of the dynamic array. So you need to really understand what you are doing if you go down that route.

One final point. If ever you wish to compile this code for 64 bit it will fail because of this line:

ComboBox.Tag := integer(@arrWidth);

since integer is a 32 bit data type. Instead you should use NativeInt which is is an integer the same width as a pointer.

ComboBox.Tag := NativeInt(@arrWidth);
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.