delphic++builderc++builder-xe8

TListView automatically appends image in imagelist to first column of my TListView. How do I get rid of this behavior?


I am using Embarcadero's C++ Builder XE8. I have a ListView and am adding columns with their items dynamically. I need an imagelist because I want to add an image to the 2nd column aka using (ListItem->SubItemImages[0] = i;)

It seems that by adding this imagelist, C++ Builder by defaults thinks that the primary column also needs an image so it is attaching the 0th element of my image list to all of the items in that column by default. I do not want this to happen. Instead under the column "ID", I just want the ID and not any images. How can I do this?

Screenshot showing whats going on

My code:

void __fastcall TForm1::FormCreate(TObject *Sender)
{
  const string firstName[] = {"Snowball", "Scavies", "Tinkerbell", "Fido", "Garfield", "Kobe"};
  const string lastName[] = {"M", "E", "O", "W", "W", "W"};
  TListColumn  *NewColumn;
  TListItem  *ListItem;
  TListView   *ListView = ListView1;

  ListView->SmallImages = ImageList1;

  ListView->ViewStyle = vsReport;
  NewColumn = ListView->Columns->Add();
  NewColumn->Caption = "ID";
  NewColumn = ListView->Columns->Add();
  NewColumn->Caption = "Node";
  NewColumn = ListView->Columns->Add();
  NewColumn->Caption = "First";
  for (int i = 0; i < sizeof( firstName ) / sizeof( firstName[0] ); i++)
  {
    char buffer[3];
    itoa(i,buffer,10);
    ListItem = ListView->Items->Add();
    ListItem->Caption = buffer;
    ListItem->SubItems->Add(firstName[i].c_str());
    ListItem->SubItems->Add(lastName[i].c_str());
    ListItem->SubItemImages[0] = i;
    //ListItem->ImageIndex = NULL;
  }
}

Solution

  • When you add an item to a listview that has an ImageList associated it will assume that your intention is to use those images for the items themselves and so each item added will be assigned a default ImageIndex of 0 (the first image in the image list).

    To remove the image from the item itself all you need to do is replace the default ImageIndex of 0 with -1, indicating "no image".

    ListItem->ImageIndex = -1; 
    ListItem->SubItemImages[0] = i;
    

    As noted in the comments, this deals with not painting the images next to the item captions (Column 0) but the listview will still leave room for those images, even if none are to be painted:

    Space left for non-existent images

    There are two ways to avoid that:

    1. Do not associate an image list with the listview and use custom drawing to paint any sub-items requiring images.

    2. Set the column width of Column 0 to 0 to effectively hide that column and treat the first sub-item as the item caption.

    Each approach has trade-offs.

    The first requires the added complexity of custom drawing. The second is more straightforward but means that you will need to enable RowSelect behavior on the list view. Without that, sub-items cannot be used to select items in the list and do not paint with any selection highlight.

    It also means that in any event handling etc you have to remember to treat Subitem[0] as a special case (the item 'caption'):

    ListView->RowSelect = true;
    
    // ..
    
    NewColumn = ListView->Columns->Add();
    NewColumn->Caption = "Not Used";
    NewColumn->Width   = 0;
    NewColumn = ListView->Columns->Add();
    NewColumn->Caption = "ID";
    
    // ..
    
    // No need to set ListItem->Caption since it is hidden
    
    ListItem->SubItems->Add(buffer);  // subitem[0] = 'the item'
    ListItem->SubItems->Add(firstName[i].c_str());
    ListItem->SubItems->Add(lastName[i].c_str());
    ListItem->SubItemImages[1] = i;
    

    There is also a slim chance that some inquisitive user might resize column[0] and discover your hidden secret. :)