Here’s an example of displaying items within a panel with the optional benefit of letting them overlap, rotate and offset from the top or left hand side.
In this screenshot we have 3 OverlapStackPanels, each with 3 rectangles within them:
<UserControl x:Class="OverlapStackPanelDemo.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:OverlapStackPanelDemo"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
d:DesignHeight="600"
d:DesignWidth="400"
mc:Ignorable="d">
<Grid x:Name="LayoutRoot" Background="White">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto" />
</Grid.ColumnDefinitions>
<StackPanel>
<local:OverlapStackPanel LeftOffset="10"
Orientation="Vertical"
Overlap="90"
Rotation="355"
UpperOffset="10">
<Rectangle Width="100"
Height="100"
Fill="Blue" />
<Rectangle Width="100"
Height="100"
Fill="Red" />
<Rectangle Width="100"
Height="100"
Fill="Yellow" />
</local:OverlapStackPanel>
<local:OverlapStackPanel LeftOffset="10"
Orientation="Horizontal"
Overlap="90"
Rotation="355"
UpperOffset="10">
<Rectangle Width="100"
Height="100"
Fill="Green" />
<Rectangle Width="100"
Height="100"
Fill="Purple" />
<Rectangle Width="100"
Height="100"
Fill="Silver" />
</local:OverlapStackPanel>
<local:OverlapStackPanel LeftOffset="10"
Orientation="Vertical"
Overlap="90"
Rotation="355"
UpperOffset="10">
<Rectangle Width="100"
Height="100"
Fill="Blue" />
<Rectangle Width="100"
Height="100"
Fill="Red" />
<Rectangle Width="100"
Height="100"
Fill="Yellow" />
</local:OverlapStackPanel>
</StackPanel>
</Grid>
</UserControl>
using System;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
namespace OverlapStackPanelDemo
{
public class OverlapStackPanel : Panel
{
#region Dependency Properties
public static readonly DependencyProperty OrientationProperty = DependencyProperty.Register("Orientation", typeof(Orientation), typeof(OverlapStackPanel), new PropertyMetadata((Orientation.Vertical)));
public Orientation Orientation { get { return (Orientation) GetValue(OrientationProperty); } set { SetValue(OrientationProperty, (Enum) value); } }
public static readonly DependencyProperty OverlapProperty = DependencyProperty.Register("Overlap", typeof(double), typeof(OverlapStackPanel), new PropertyMetadata(double.NaN));
public double Overlap { get { return (double)GetValue(OverlapProperty); } set { SetValue(OverlapProperty, value); } }
public static readonly DependencyProperty RotationProperty = DependencyProperty.Register("Rotation", typeof(double), typeof(OverlapStackPanel), new PropertyMetadata(double.NaN));
public double Rotation { get { return (double)GetValue(RotationProperty); } set { SetValue(RotationProperty, value); } }
public static readonly DependencyProperty LeftOffsetProperty = DependencyProperty.Register("LeftOffset", typeof(double), typeof(OverlapStackPanel), new PropertyMetadata(double.NaN));
public double LeftOffset { get { return (double)GetValue(LeftOffsetProperty); } set { SetValue(LeftOffsetProperty, value); } }
public static readonly DependencyProperty UpperOffsetProperty = DependencyProperty.Register("UpperOffset", typeof (double), typeof (OverlapStackPanel), new PropertyMetadata(double.NaN));
public double UpperOffset { get { return (double)GetValue(UpperOffsetProperty); } set { SetValue(UpperOffsetProperty, value); } }
#endregion
#region Overrides
protected override Size MeasureOverride(Size availableSize)
{
var desiredSize = new Size();
var childrenResolved = 0;
if (Orientation == Orientation.Vertical)
availableSize.Height = double.PositiveInfinity;
else
availableSize.Width = double.PositiveInfinity;
foreach (UIElement child in Children.Where(child => child != null))
{
child.Measure(availableSize);
if (Orientation == Orientation.Vertical)
{
desiredSize.Width = LeftOffset + Math.Max(desiredSize.Width, child.DesiredSize.Width);
desiredSize.Height += UpperOffset + child.DesiredSize.Height - Overlap;
}
else
{
desiredSize.Height = UpperOffset + Math.Max(desiredSize.Height, child.DesiredSize.Height);
desiredSize.Width += LeftOffset + child.DesiredSize.Width - Overlap;
}
childrenResolved++;
}
// take into account the first item doesn't overlap
if (Orientation == Orientation.Vertical)
{
desiredSize.Height += Overlap;
}
else
{
desiredSize.Width += Overlap;
}
return desiredSize;
}
protected sealed override Size ArrangeOverride(Size arrangeSize)
{
int childrenResolved = 0;
double itemX = 0;
double itemY = 0;
foreach (UIElement child in Children.Where(child => child != null))
{
double itemOverlap = (childrenResolved == 0)
? 0
: Overlap;
Rect targetRect;
if (Orientation == Orientation.Vertical)
{
targetRect = new Rect
{
X = LeftOffset + (LeftOffset * childrenResolved),
Y = itemY + UpperOffset + (UpperOffset * childrenResolved) - itemOverlap,
Width = child.DesiredSize.Width,
Height = child.DesiredSize.Height
};
itemY += child.DesiredSize.Height - itemOverlap;
}
else
{
targetRect = new Rect
{
X = itemX + LeftOffset + (LeftOffset * childrenResolved) - itemOverlap,
Y = UpperOffset + (UpperOffset * childrenResolved),
Width = child.DesiredSize.Width,
Height = child.DesiredSize.Height
};
itemX += child.DesiredSize.Width - itemOverlap;
}
child.Arrange(targetRect);
var rotate = new RotateTransform {Angle = Rotation};
child.RenderTransform = rotate;
childrenResolved++;
}
return arrangeSize;
}
#endregion
#region Constructor
public OverlapStackPanel()
{
}
#endregion
}
}
Source code: http://stevenhollidge.com/blog-source-code/OverlapStackPanelDemo.zip