Reputation: 981
I am working on a WPF application where I have a ListBox
.
<ListBox Grid.Column="1" ItemsSource="{Binding MyUserControls}" ScrollViewer.HorizontalScrollBarVisibility="Disabled"
HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel IsItemsHost="True" Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
</ListBox>
When user clicks a button I am adding a UserControl
instance to MyUserControls(ObservableCollection
). The Listbox
is in a Grid
in my Main Window. What I need is when more and more user controls are added they should resize dynamically upto a min width and then add to the next row. But I couldn't achieve this. I am able to get wrappanel behaviour but the elements are not resizing (I have to set the width properties to make sure that only 3 user controls are there in a row). Even when one user control is added its not filling the whole grid.
Please tell me if you need more information.
Upvotes: 0
Views: 255
Reputation: 8791
There are already tons of nice tuts on the internet showing you how to have such WrapPanel. Here is one:
public class ElasticWrapPanel : Panel
{
/// <summary>
/// Identifies the <see cref="DesiredColumnWidth"/> dependency property.
/// </summary>
internal static readonly DependencyProperty DesiredColumnWidthProperty = DependencyProperty.Register("DesiredColumnWidth", typeof(double), typeof(ElasticWrapPanel), new PropertyMetadata(100d, new PropertyChangedCallback(OnDesiredColumnWidthChanged)));
private int _columns;
protected override Size MeasureOverride(Size availableSize)
{
_columns = (int) (availableSize.Width / DesiredColumnWidth);
foreach (UIElement item in this.Children)
{
item.Measure(availableSize);
}
return base.MeasureOverride(availableSize);
}
protected override Size ArrangeOverride(Size finalSize)
{
if (_columns != 0)
{
double columnWidth = Math.Floor(finalSize.Width/_columns);
double top = 0;
double rowHeight = 0;
int column = 0;
foreach (UIElement item in this.Children)
{
item.Arrange(new Rect(columnWidth * column, top, columnWidth, item.DesiredSize.Height));
column++;
rowHeight = Math.Max(rowHeight, item.DesiredSize.Height);
if (column == _columns)
{
column = 0;
top += rowHeight;
rowHeight = 0;
}
}
}
return base.ArrangeOverride(finalSize);
}
private static void OnDesiredColumnWidthChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
var panel = (ElasticWrapPanel)obj;
panel.InvalidateMeasure();
panel.InvalidateArrange();
}
public double DesiredColumnWidth
{
get
{
return (double)GetValue(DesiredColumnWidthProperty);
}
set
{
SetValue(DesiredColumnWidthProperty, value);
}
}
}
In xaml you will have something like this:
<c:ElasticWrapPanel DesiredColumnWidth="200">
<Button Content="Item1" />
<Button Content="Item2" />
<Button Content="Item3" />
<Button Content="Item4" />
<Button Content="Item5" />
<Button Content="Item6" />
</c:ElasticWrapPanel>
Upvotes: 2
Reputation: 69979
The behaviour that you describe is the normal behaviour of the WrapPanel
. If you want to have a Panel
that automatically fills the space on each row, then you will need to create a custom Panel
for yourself. Creating a custom Panel
is not as difficult as it seems if you're good at mathematics... there are only two methods to override in which you calculate the sizes of each element.
You can find out hot to create a custom Panel
from the following links:
How to create a Custom Layout Panel in WPF
Creating Custom Panels In WPF
Custom Panel Elements section of Panels Overview
Upvotes: 1