CircularPanel
A layout panel that places its children round a circle or an ellipse – evenly round the whole ring, or fanned across part of it – and can turn the ring to any angle.
public class CircularPanel : Panel
Inherits from / implements: Panel
Remarks
Use it for radial menus, a ring of shortcuts round a central picture, the hours on a clock
face, seats round a table plan, or a carousel-like ring of thumbnails that turns when
RenderAngle is animated. It works as a panel in its own right or, through
ItemsControl.ItemsPanel, as the layout of any bound list.
The ring
The ring is centred in the panel and follows its shape: an ellipse whose horizontal radius is
the panel's width divided by ItemsClosenessFactor and whose vertical radius is its
height divided by the same factor. A square panel gives a circle. The ring grows and shrinks as the
panel is resized.
Placing the items
Each child keeps the size it asks for and is centred on its point on the ring; the panel never
squeezes a child to fit. The first child sits at RenderAngle, measured in radians
clockwise from the right-hand side of the ring, and each child after it one gap further round. The
gap depends on AngleCalculationStrategy: with
GapAngleCalculationStrategy.Dynamic the children share the whole circle equally, and
with GapAngleCalculationStrategy.Custom they share the angle in
ItemsGapAngleFactor.
Size
The panel takes all the room it is offered. Where it is offered unlimited room – in a
StackPanel, say, or an auto-sized row – it asks for enough for its largest child to
fit on the ring. It does not clip: an item placed past the edge is still drawn, unless a container
round the panel clips it.
Examples
Eight department shortcuts round a circle, the first at the top.
<Window x:Class="Intranet.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:panels="clr-namespace:Binarymission.WPF.Controls.Panels;assembly=Binarymission.WPF.Controls.CircularPanel"
Title="Departments" Width="520" Height="520">
<panels:CircularPanel Width="440" Height="440"
ItemsClosenessFactor="2.6"
RenderAngle="-1.5708">
<Button Width="90" Height="36" Content="Sales" />
<Button Width="90" Height="36" Content="Finance" />
<Button Width="90" Height="36" Content="HR" />
<Button Width="90" Height="36" Content="IT" />
<Button Width="90" Height="36" Content="Legal" />
<Button Width="90" Height="36" Content="Marketing" />
<Button Width="90" Height="36" Content="Operations" />
<Button Width="90" Height="36" Content="Support" />
</panels:CircularPanel>
</Window>A bound list laid out round a ring that turns slowly and continuously.
<ItemsControl ItemsSource="{Binding Products}" Width="400" Height="400">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<panels:CircularPanel ItemsClosenessFactor="3">
<panels:CircularPanel.Triggers>
<EventTrigger RoutedEvent="Loaded">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="RenderAngle"
From="0" To="6.2832" Duration="0:0:30"
RepeatBehavior="Forever" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</panels:CircularPanel.Triggers>
</panels:CircularPanel>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Image Source="{Binding Thumbnail}" Width="64" Height="64" ToolTip="{Binding Name}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>See also
Members at a glance
| Constructors | |
|---|---|
CircularPanel | Creates an empty circular panel that spreads its children evenly round the whole ring, starting at the right-hand side. |
| Properties | |
AngleCalculationStrategy | Gets or sets whether the children are spread evenly round the whole ring or across the angle in
ItemsGapAngleFactor. |
ItemsClosenessFactor | Gets or sets how far in from the panel's edge the ring of children sits: the larger the value, the smaller and tighter the ring. |
ItemsGapAngleFactor | Gets or sets the angle, in radians, that the children share between them when
AngleCalculationStrategy is GapAngleCalculationStrategy.Custom. |
RenderAngle | Gets or sets where on the ring the first child is placed, which turns the whole ring of children. |
| Methods | |
ArrangeOverride | Places each child, at the size it asked for, centred on its point on the ring. |
MeasureOverride | Measures each child at the size it wants, and works out how much room the panel needs. |
| Fields | |
AngleCalculationStrategyProperty | Identifies the AngleCalculationStrategy dependency property. |
ItemsClosenessFactorProperty | Identifies the ItemsClosenessFactor dependency property. |
ItemsGapAngleFactorProperty | Identifies the ItemsGapAngleFactor dependency property. |
RenderAngleProperty | Identifies the RenderAngle dependency property. |
Constructors
CircularPanel
Creates an empty circular panel that spreads its children evenly round the whole ring, starting at the right-hand side.
Properties
AngleCalculationStrategy
Gets or sets whether the children are spread evenly round the whole ring or across the angle in
ItemsGapAngleFactor.
- Value
- One of the
GapAngleCalculationStrategyvalues. The default isGapAngleCalculationStrategy.Dynamic: evenly round the whole ring.
Remarks
With the default ItemsGapAngleFactor of a full turn, both strategies place
the children identically.
See also
ItemsClosenessFactor
Gets or sets how far in from the panel's edge the ring of children sits: the larger the value, the smaller and tighter the ring.
- Value
- The number the panel's width and height are divided by to give the ring's horizontal and
vertical radii. The default is
2.5.
Remarks
At 2 the ring touches the panel's edges, so the children's centres lie on the edge and
half of each child hangs outside. Above 2 the ring moves inwards; the room left between a child and
the edge is size * (0.5 - 1 / factor) - childSize / 2. At the default of 2.5 that is a tenth
of the panel's size less half the child's, so a 480-pixel panel leaves 4 logical pixels round an
88-pixel child – and a smaller panel leaves less than nothing. Raise the factor, or enlarge the
panel, when large children run over the edge.
Below 2 the ring is larger than the panel and the children sit outside it. They are still drawn, which suits a ring deliberately spread round a central picture, but inside anything that clips they disappear. Values closer to zero than 0.1, and values that are not a finite number, are read as 0.1.
Example
<!-- A ring that sits well inside its panel, leaving room in the middle for a logo. -->
<Grid Width="360" Height="360">
<Image Source="Images/logo.png" Width="96" Height="96" />
<panels:CircularPanel ItemsClosenessFactor="3.2">
<TextBlock Text="Plan" />
<TextBlock Text="Build" />
<TextBlock Text="Test" />
<TextBlock Text="Release" />
</panels:CircularPanel>
</Grid>ItemsGapAngleFactor
Gets or sets the angle, in radians, that the children share between them when
AngleCalculationStrategy is GapAngleCalculationStrategy.Custom.
- Value
- An angle in radians. The default is
2 * Math.PI(6.2832), a full turn.
Remarks
The gap from one child to the next is this angle divided by the number of children, so the
children cover the angle less one gap: four children with Math.PI (a half turn) sit 0, 45, 90 and 135
degrees round from RenderAngle. To fan n children so that the first and last are exactly an angle
A apart, set this to A * n / (n - 1).
A negative angle runs the children anticlockwise. Ignored when
AngleCalculationStrategy is GapAngleCalculationStrategy.Dynamic. A value
that is not a finite number is read as a full turn.
Example
<!-- Five actions fanned across the top half of the ring, from left to right. -->
<panels:CircularPanel AngleCalculationStrategy="Custom"
RenderAngle="3.1416"
ItemsGapAngleFactor="3.927">
<Button Content="Reply" />
<Button Content="Reply all" />
<Button Content="Forward" />
<Button Content="Archive" />
<Button Content="Delete" />
</panels:CircularPanel>See also
RenderAngle
Gets or sets where on the ring the first child is placed, which turns the whole ring of children.
- Value
- An angle in radians, measured clockwise from the right-hand side of the ring (3 o'clock).
The default is
0.
Remarks
Every other child follows on from the first, so changing this turns the whole arrangement:
-Math.PI / 2 (-1.5708) puts the first child at the top, Math.PI (3.1416) on the
left. A value that is not a finite number is read as 0.
The panel lays itself out again whenever the angle changes, so it can be animated with a
DoubleAnimation to spin the ring, or bound to a slider
to let the user turn it. The children themselves are moved, not rotated: text stays upright.
Example
// Turn the ring a quarter turn clockwise, smoothly, each time the user clicks "Next".
double target = ring.RenderAngle + Math.PI / 2;
ring.BeginAnimation(CircularPanel.RenderAngleProperty,
new DoubleAnimation(target, TimeSpan.FromMilliseconds(400)) { FillBehavior = FillBehavior.Stop });
ring.RenderAngle = target;See also
Methods
ArrangeOverride
Places each child, at the size it asked for, centred on its point on the ring.
Parameters
finalSize- The area the panel has been given. The ring is centred in it and its radii
are this width and height divided by
ItemsClosenessFactor.
Returns
finalSize: the panel always uses all the area it is given.
Remarks
A RenderAngle or ItemsGapAngleFactor that is not a finite
number is read as 0 and a full turn respectively, so a stray binding value cannot place the
children at an impossible position.
MeasureOverride
Measures each child at the size it wants, and works out how much room the panel needs.
Parameters
availableSize- The room on offer. Either dimension may be infinite.
Returns
On each axis: all the room on offer when it is a finite amount; otherwise enough for the largest
child to sit on the ring inside the panel – the child's size times
factor / (factor - 2), where factor is ItemsClosenessFactor, or three
times the child's size when the factor is 2 or less.
Remarks
Children are measured with unlimited room, so each one is laid out at its natural size and never squeezed by the panel.
Fields
AngleCalculationStrategyProperty static
Identifies the AngleCalculationStrategy dependency property.
ItemsClosenessFactorProperty static
Identifies the ItemsClosenessFactor dependency property.
ItemsGapAngleFactorProperty static
Identifies the ItemsGapAngleFactor dependency property.
RenderAngleProperty static
Identifies the RenderAngle dependency property.