I encountered a need to come up with a solution to your second question. The solution is very susceptible to layout changes and not recommended, but it's the only thing that seems to get the job done:
private void List_OnSizeChanged(object sender, SizeChangedEventArgs e)
{
var list = sender as ListViewBase; // works with ListView or GridView
if (list == null || list.Items == null)
{
return;
}
var nMax = 6; // set this to your desired number of items
var n = list.Items.Count;
var gen = list.ItemContainerGenerator;
if (gen != null)
{
var height = 0.0;
DependencyObject o;
for (var i = 0;
i < nMax
&& i < n
&& (o = gen.ContainerFromIndex(i)) != null;
i++)
{
// calculate running height
var h = o.GetValue(ActualHeightProperty) as double? ?? 0;
height += h+2;
}
// set height, unsub and resub to listeners otherwise endless layout cycle
if (height > 0)
{
list.SizeChanged -= List_OnSizeChanged;
list.Height = height;
list.SizeChanged += List_OnSizeChanged;
}
}
}
Subscribe to your list's SizeChanged event with the above event handler and change nMax to your desired number of items.
If your list items are being virtualized, and ItemsContainerGenerator is null, you may have to force realization by changing the ItemsPanel to a concrete element like StackPanel:
<ListView>
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel/>
</ItemsPanelTemplate>
</ListView.ItemsPanel>
</ListView>
In my case I had a further need to allow a dynamic number of items such that they fill the container completely (rather than a fixed number of items), this solution is modified from the above, but even more susceptible to layout changes and will probably require more careful integration:
private void List_OnSizeChanged(object sender, SizeChangedEventArgs e)
{
var list = sender as ListViewBase;
if (list == null || list.Items == null)
{
return;
}
var n = list.Items.Count;
var containingFixedGrid = FindVisualParent<Grid>(list); // this is an attempt to get the first container that is bounded by the actual height of the UI, in my case and the general case it is probably going to be a grid, but you may need to update this to whatever element makes sense; you can also use a named element
if (containingFixedGrid == null || containingFixedGrid.ActualHeight > this.ActualHeight)
{
return;
}
var gen = list.ItemContainerGenerator;
if (gen != null)
{
var adjustOffset = 2; // small adjustment needed for alignment, adjust for your needs
var height = 0.0;
DependencyObject o;
for (var i = 0; i < n && (o = gen.ContainerFromIndex(i)) != null; i++)
{
var item = o as FrameworkElement;
if (item != null)
{
// calculate the offset of this item to its containing grid
var t = item.TransformToVisual(containingFixedGrid) as MatrixTransform;
if (t != null && t.Matrix.OffsetY + item.ActualHeight < containingFixedGrid.ActualHeight)
{
height += item.ActualHeight + adjustOffset;
}
}
}
// set height, unsub and resub to listeners otherwise endless layout cycle
if (height > 0)
{
list.SizeChanged -= List_OnSizeChanged;
list.Height = height;
list.SizeChanged += List_OnSizeChanged;
}
}
}
public static T FindVisualParent<T>(DependencyObject element) where T : DependencyObject
{
var parent = element;
while (parent != null)
{
T type = parent as T; if (type != null)
{
return type;
}
parent = VisualTreeHelper.GetParent(parent) as UIElement;
}
return null;
}
Subscribe the desired lists to the handler and now the number of items in each list should fill the list entirely without any of the items being clipped. Adjust the adjustOffset to account for any small discrepancies.