This would be a simple owner-drawn ListBox, for which several articles on this site already cover what you need to do (or alternatives, at least). Read ImageListBox - exposing localizable custom object collection as a property[^]. If covers a few things you didn't mention requiring, but the source will give you an idea. Basically, you just set DrawMode to DrawMode.OwnerDrawVariable (since they could be different heights, as you mentioned), and handle both the MeasureItem and DrawItem events. If you would actually be better, however, to extend ListBox with your own implementation (perhaps something like ImageListBox) that overrides the DrawMode property (always returns DrawMode.OwnerDrawVariable but does nothing or throws a NotSupportedException or something in the set accessor) and the OnMeasureItem and OnDrawItem methods (don't forget to call the base class's implementation so that events are fired). This is a far better approach because you won't have to implement all this code again every time you want to have an ImageListBox-like control, and overriding methods is faster than handling events. It's all about encapsulation.
Microsoft MVP, Visual C# My Articles