The ListBox is a very versatile control in WPF, when you want to have some kind of list selection, regardless of the actual item layout, you'll probably opt for a ListBox with a custom ItemsTemplate. Due to this versatility I've used the ListBox control quite a bit in my WPF applications, and a common task I find myself doing is hacking replacing the ItemsPanelTemplate.
This is the panel that houses and arranges all the child items that make up the list. For example, you might want a horizontal list rather than the default vertical list. This a simple task, as shown below:
Orientation="Horizontal"
IsItemsHost="True" />
The VirtualizingStackPanel is the default panel for the ListBox, so I'm using the same control here, but changing the value of the Orientation property to Horizontal. Job done.
Another common panel "trick" I like to do is to swap out the VirtualizingStackPanel for a WrapPanel. This gives me item selection in a WrapPanel, similar to the Windows Explorer experience when you're looking at your files in list mode. At first, this might appear to be a simple task:
Lovely, however, things start getting icky when you want the items to actually wrap - which kind of the point of using a WrapPanel in the first place!! Grrrr! Below is a screen-shot showing the problem.
The items are clearly not wrapping. This is because the Width property of the panel is set to Auto, the default value, meaning infinite width. The other issue is that further up the chain in the visual tree is a ScrollContentPresenter. The implications of that are if a width value is not explicitly set for the contained control, the WrapPanel in this case, it will be allowed to be as wide as it needs to be to display all of its content. The ScrollContentPresenter will then show a scroll bar if the width is larger than the viewable area. Drat and double drat!
One approach could be to remove the ScrollContentPresenter from the visual tree, but that involves replacing the ListBox's control template, which is a lot of hard work just to have the items wrap in the panel.
So what we really want to do is stop the ScrollContentPresenter from thinking that the WrapPanel is bigger than it is, which will stop the scrollbars AND ensure that the WrapPanel has a finite width, making it wrap. We can achieve both goals with a "simple" binding expression in the ItemsPanelTemplate defined for our WrapPanel:
IsItemsHost="True"
Width="{Binding
Path=ActualWidth,
RelativeSource={RelativeSource
Mode=FindAncestor,
AncestorType=
{x:Type ScrollContentPresenter}}}" />
Now, I know that this is pretty funky looking syntax; especially if you've not done too much data binding. But quite simply, all I'm doing here is setting the Width of the WrapPanel to the value of the ActualWidth property on the ScrollContentPresenter, and I find the ScrollContentPresenter by walking up the visual tree.
See, simple(?)
On question you might have is why not just use the RelativeSource of TemplatedParent? The answer, to your great question, is that the parent of the WrapPanel is an ItemsPresenter not the ScrollContentPresenter, as shown below with an image taken from XAMLPad; which suffers from the same problem as the WrapPanel, in that it can have infinite width and will cause the scroll bars to appear.
I'm sure that there are other solutions to the WrapPanel issue described in this post and if you have one I would love to hear from you.