TileView control
The TileView
control lets you arrange a collection of items (Tile
s) in a tile fashion, and one among the rest of the Tile
s become active
and is shown in a relatively larger area. Activation and deactivation of a Tile
is animated. This behaves more or less like
a Carousel
control, but they are different.
Choosing the base
TileView
presents a collection of tiles and arranges them in a different fashion. When it comes to presenting a collection of items,
ItemsControl
is the best pick, and most of the controls we often use are derived from ItemsControl
, ListView
, TreeView
,
or Menu and each of them have their own Item
s as well. TileView
simply presents a collection so it derives from ItemsControl
, and each Tile
presents content. Obviously we one can go for ContentControl
so we can present any content, but for the needs that may arise in future,
Tile
is derived from HeaderedContentControl
. By ‘future needs’ I mean one may want to show a header, or buttons to activate / deactivate a tile.
Primary (simple) requirements
The behavior of the control is well known, and is defined in the introduction part of it. Here is the list of requirements to achieve this behavior.
- Tiles need to be arranged in a uniform manner, and the active one gains more space.
Given that a field is required to keep track of the active tile, we have ActiveTile
.
A layout mechanism is needed to proportionally divide the space between the ActiveTile
and the others, all the other tiles equally share the remaining space.
- One of the
Tile
s becomes active on load, and any other Tile
can be activated using the mouse. A tile must be aware of the mouse events, and notify the TileView
when activated, and for this, we need the Activated
event.
- Activation and deactivation should be animated and smooth.
ActiveTile
is relatively large so an inactive Tile
needs
two transformations: ScallingTransform
- to make it larger, TranslateTransform
- to move it to the active position.
Bringing the words into action
Let's see how these requirements are achieved in reverse order.
Animating the tiles
As stated, two transforms are involved in activating a tile which operates on four different properties of a Tile
.
To move a Tile
to the active position, TranslateTransform
is used and it operates on the X and Y values of it, and to make the Tile
larger,
ScaleTransform
is used and it acts on the width and height values. The following code demonstrates how this is done.
This is just for representation of the values and the actual implementations differ from the representation.
tile.RenderTransform = new TranslateTransform();
Storyboard tileActivator = new Storyboard();
DoubleAnimation translateX =
new DoubleAnimation { From = 0, To = 10, Duration = TimeSpan.FromSeconds(1) };
Storyboard.SetTarget(translateX, tile);
Storyboard.SetTargetProperty(translateX,
new PropertyPath("(UIElement.RenderTransform).(TranslateTransform.X)"));
tileActivator.Children.Add(translateX);
tileActivator.Begin();
The same approach is used in all four properties of the Tile
to make it active from an inactive state.
Tile activation notification
The TileView
must be notified when a user activates it by a mouse click, this implementation defines an event Activated
and just overrides
the mouse down event to raise the Activated
event. TileView
listens for this Activated
event and sets the sender as the new ActiveTile
.
public event RoutedEventHandler Activated;
protected override void OnMouseDown(System.Windows.Input.MouseButtonEventArgs e)
{
base.OnMouseDown(e);
if (Activated != null)
Activated(this, new RoutedEventgArgs());
}
TileView
listens to this event, casts the sender
as Tile
, and activates it.
tile.Activated += new RoutedEventHandler(OnTileActivated);
void OnTileActivated(object sender, RoutedEventArgs e)
{
ActivateTile(sender as Tile);
}
Arranging the tiles
The layout process in WPF happens in two passes: MeasureOverride
and ArrageOverride
, both of these are explained well in many articles. The former
lets the child know the space available for it and asks the child the space it would take. And the former gives the space it can take.
protected override Size MeasureOverride(Size constraint)
{
var sz = base.MeasureOverride(constraint);
foreach (Tile tile in Items)
{
tile.Measure(size)
}
return sz;
}
protected override Size ArrangeOverride(Size arrangeBounds)
{
foreach (Tile tile in OrderedItems)
{
tile.Arrange(rect);
}
return arrangeBounds;
}
History
- 16/11/2011 - Initial post.
- 30/11/2011 - Added the
TileState
API, source and demo files updated.
Hope this helps !!! Happy coding !!!