title: What is the best way to draw "child" controls (such as buttons) within each item in a WinForms ListBox?
body: <p>I'm writing a simple custom owner-drawn <code>ListBox</code> control and banging my head against the wall trying to figure out how to implement what seems like a straight-foward feature. My custom <code>ListBox</code> is supposed to work similarly to the "Add/Remove Programs" list in Windows XP. That is, it displays a list of items as usual, but when the user selects an item in the list, a clickable button should appear next to the item text. In my case, I'm trying to display an "Import" button next to each item in my <code>ListBox</code>.</p>
  
  <p>In order to keep the custom <code>ListBox</code> somewhat encapsulated, I'm trying to display the button by overriding the <code>ListBox.OnDrawItem</code> method (i.e. the button is conceptually part of the list box item), but I can't get it to work quite right.</p>
  
  <p>I should note that I'm trying to use a single button for the entire <code>ListBox</code>. When the user selects a item in the list, the <code>OnDrawItem</code> method just re-positions this single button so that it appears next to the selected item. I have it 99% working: the problem now is that when the <code>ListBox</code> is scrolled and the selected item goes off-screen, the button is still drawn to its previous position, so it draws on top of the wrong item. I'm guessing this is because Windows won't try to redraw the selected item if it is off-screen, and thus the repositioning code doesn't get called.</p>
  
  <p>Here is trimmed-down version of what I have right now:</p>
  
  <pre><code>public partial class EventListBox : ListBox
  {
      private Button _importButton;
  
      public EventListBox()
      {
          InitializeComponent();
          this.DrawMode = DrawMode.OwnerDrawVariable;
  
      // set up the button that will appear next to the currently-selected item
          _importButton = new Button();
          _importButton.Text = "Import...";
          _importButton.AutoSize = true;
          _importButton.Visible = false;
  
      // Add the button as a child control of this ListBox
          Controls.Add(_importButton);
      }
  
  
      protected override void OnDrawItem(DrawItemEventArgs e)
      {
          if (this.Items.Count &gt; 0)
          {
              e.DrawBackground();
  
      // draw item here (omitted)
  
      // if drawing the selected item, re-position the "Import" button so that appears
      // inside the current item, and make it visible if it is hidden.
      // These checks prevent the resulting repaint that will occur from causing an infinite loop
  
      // The problem seems to be that if the ListBox is scrolled such that the selected item
      // moves off-screen , this code won't run, because it won't repaint the selected item anymore...
      // This means the button will be painted in its previous position.
  
      // The real question is: Is there a better way to approach the whole notion of
      // rendering buttons within ListBox items?
  
              if (e.State &amp; DrawItemState.Selected == DrawItemState.Selected)
              {
                  _importButton.Top = e.Bounds.Bottom - _importButton.Height - 20;
                  _importButton.Left = e.Bounds.Left;
                  if(!_importButton.Visible) _importButton.Visible = true;
              }
          }
  
          base.OnDrawItem(e);
      }
  
      protected override void OnMeasureItem(MeasureItemEventArgs e)
      {
          base.OnMeasureItem(e);
          e.ItemHeight = 100; //hard-coded for now...
      }
  
  }
  </code></pre>
  
  <p><strong>Rationale for using a single button</strong></p>
  
  <p>I would rather create a separate button for each item in the <code>ListBox</code>, but I can't find any way to track when items are added/removed from the listbox. I couldn't find any relevant <code>ListBox</code> methods that I could override, and I can't re-assign the <code>ListBox.Items</code> property to a custom collection object, since <code>ListBox.Items</code> is read-only. Because of this, I went with the above approach of using a single button and re-positioning it as needed, but as I mentioned, this isn't very robust and breaks easily.</p>
  
  <p>My current thinking is it would make the most sense to create a new button at the point when new items are added to the <code>ListBox</code>, and remove buttons when items were removed.</p>
  
  <p>Here are some possible solutions I came up with, but is there a better way to implement this?</p>
  
  <ul>
  <li>I could just create my own <code>AddItem</code> and <code>RemoveItem</code> methods directly on my derived <code>ListBox</code> class. I could create a corresponding button each time a new item is added, and remove the item's button in <code>RemoveItem</code>. However, to me, this is an ugly hack, because it forces me to call these special add/remove methods instead of just using <code>ListBox.Items</code>.</li>
  <li>I could draw a new button manually in <code>OnDrawItem</code> using <code>System.Windows.Forms.ButtonRenderer</code>, but then I have to do a lot of extra work to make it act like a real button, since <code>ButtonRenderer</code> is doing nothing more than drawing the button. Figuring out when the user hovers over this "button", and when it is clicked, seems like it would be difficult to get right.</li>
  <li>When <code>OnDrawItem</code> is called, I could create a new button if the item being drawn doesn't already have a button associated with it (I could keep track of this with a <code>Dictionary&lt;Item, Button&gt;</code>), but I still need a way to remove unused buttons when their corresponding items are removed from the list. I guess I could iterate over my dictionary of item-button mappings and remove items that don't exist in the <code>ListBox</code> anymore, but then I'm iterating over two lists every time an item in the <code>ListBox</code> is redrawn (ack!).</li>
  </ul>
  
  <p>So, is there a better way to include clickable buttons inside a <code>ListBox</code>? It's obviously been done before, but I can't find anything useful on Google. The only examples I've seen that have buttons in a <code>ListBox</code> were WPF examples, but I'm looking for how to do this with WinForms.</p>