This post describes how to align items in a container so the two neighbour items are always aligned with equal distance. Solution should be flexible, meaning that when items are changed or when container is resized items should keep equal distance in between. Reader should have some basic knowledge about C# and XAML for Silverlight.
Ideas
- using Canvas and positioning items with absolute coordinates – items are always fixed, coordinates need to be calculated, so it is the least flexible approach, no
- using StackPanel with items that have margin – margins need to be recalculated when an item is added or removed or when container is resized, maybe
- using Grid with predefined rows and columns – notice predefined, means less flexibility, but offers equal size rows/columns, maybe
- using a custom control – making Grid more dynamic but extending number of rows/columns when items are added, yes
Custom Control: EqualDistanceStackPanel
As written before, custom control should extend Grid to become more flexible
public class EqualDistanceStackPanel : Grid
{
}
EqualDistanceStackPanel should implement ItemsSource and ItemTemplate just like ItemsControl
#region ItemsSource
public IEnumerable ItemsSource
{
get { return (IEnumerable)GetValue(ItemsSourceProperty); }
set { SetValue(ItemsSourceProperty, value); }
}
public static readonly DependencyProperty ItemsSourceProperty =
DependencyProperty.Register("ItemsSource", typeof(IEnumerable),
typeof(EqualDistanceStackPanel), new PropertyMetadata(ItemsSourceChanged));
private static void ItemsSourceChanged(DependencyObject d,
DependencyPropertyChangedEventArgs e)
{
var panel = d as EqualDistanceStackPanel;
if (panel != null)
{
if (e.OldValue != null)
{
panel.ClearItems();
}
if (e.NewValue != null)
{
panel.BindItems(e.NewValue as IEnumerable);
}
}
}
#endregion
public DataTemplate ItemTemplate
{
get;
set;
}
ClearItems method deletes all previous elements from the panel and BindItems attaches fresh controls from item template.
protected void ClearItems()
{
this.Children.Clear();
}
protected void BindItems(IEnumerable items)
{
if (this.ItemTemplate == null)
{
return;
}
//Create and attach children
foreach (var item in items)
{
var element = this.ItemTemplate.LoadContent() as FrameworkElement;
element.DataContext = item;
this.Children.Add(element);
}
//Realign in container
Refresh();
}
How should the custom control look in XAML? EqualDistanceStackPanel gets a collection of items, e.g. of UIElement type.
<Grid xmlns:c="clr-namespace:Avivo.Controls">
<c:EqualDistanceStackPanel ItemsSource="{Binding Items}" Orientation="Vertical">
<c:EqualDistanceStackPanel.ItemTemplate>
<DataTemplate>
<ContentPresenter Content="{Binding}" />
</DataTemplate>
</c:EqualDistanceStackPanel.ItemTemplate>
</c:EqualDistanceStackPanel>
</Grid>
Example
Ruler has been made to demonstrate the usage of EqualDistanceStackPanel. Numbers are bound to the control and ticks are bound to another control using the same binding collection. Left image represents container height 300px and right when resized to 460px – elements are automatically aligned with equal distance.

Try demo, resize browser to see the effect. This control has been originally developed for custom chart axes in QR Code Statistics application. EqualDistanceStackPanel control is lightweight, works for the statistics application and should be improved for general usage.
Source code
- Source code, Visual Studio sample Silverlight and web project
References