It's here! The new version of the elliptic control is now available!
As I promised a few months ago, I improved the control by adding databinding capabilities to add items around the ellipse. I also changed some code to correct some bugs and improve the animation of items.
Even though I had some bugs from the CTP when I attempted to manipulate the VisualTree, I found another strategy which does not affect the performance and the reliability of the control.

[Figure 1. The new version]
So what did I modify in the code:
- The EllipticItemSelectorControl derived from ItemsControl, implements and overrides some methods.
- The EllipticItem derived from ContentControl in order to be able to receive the data.
- The Viewbox disappeared and has been replaced by a scale transformation. The timeline using key frames has been adapted.
- The SelectionChanged event now publishes 2 properties, OldValue and NewValue that are the unselected and the selected item.
- The windows application that consumes the control defines the data in an Xml format and contains the DataTemplate to apply to the items.
The Data
The sample contains all you need to know about the RioterDeckers ;).
I choose to use a simple XmlDataProvider as describes below:
<XmlDataProvider x:Key="RDDataSource" XPath="/RioterDeckers/Geeks">
<x:XData>
<RioterDeckers xmlns="">
<Geeks>
<RioterDecker Name="AvalonBoy" Image="../images/avalonboy.rd.release.png" Drilling="18" Analysis="12" DataBlaster="15" Early="16" UI="17" Project="12" Business="12" Phrase="'Put this in your head.'" />
<RioterDecker Name="Chaz" Image="../images/chaz.rd.release.png" Drilling="16" Analysis="14" DataBlaster="15" Early="15" UI="18" Project="15" Business="12" Phrase="'I kicked the first, I smashed the second! And I swam!'" />
<RioterDecker Name="Raskal" Image="../images/raskal.rd.release.png" Drilling="15" Analysis="14" DataBlaster="15" Early="15" UI="12" Project="11" Business="18" Phrase="'It's integrated to GDPML. Neness!'" />
<RioterDecker Name="Thor" Image="../images/thor.rd.release.png" Drilling="15" Analysis="12" DataBlaster="17" Early="18" UI="15" Project="13" Business="12" Phrase="'Who is OK for a bullshit bingo ?'" />
<RioterDecker Name="WoZoI" Image="../images/wozoi.rd.release.png" Drilling="13" Analysis="18" DataBlaster="11" Early="14" UI="11" Project="15" Business="12" Phrase="'Vous allez rire, il n'y a pas de serrure (in french in the text).'" />
</Geeks>
</RioterDeckers>
</x:XData>
</XmlDataProvider>
How to consume the elliptic control
As any type of ItemsControl, you must specify the data source to apply and the template for each item added to the control.
The other properties are the same than the first version of the control. As you can see below, I simply bind to my data provider and I define which is the template to apply to each item. Notice that I also intercept the Click event and the (new version of) SelectionChanged event.
This time, that's all you need to add the elliptic control.
<cc:EllipticItemSelector x:Name="_selector" ItemWidth="90" ItemHeight="90" EllipticWidth="175" EllipticHeight="70" Angle="25" Canvas.Top="300" Canvas.Left="225" SelectionChanged="_SelectionChanged" Click="_SelectorClick" ItemsSource="{Binding Source={StaticResource RDDataSource}, XPath=RioterDecker}" ItemTemplate="{StaticResource RD_DataTemplate}" />
The DataTemplate below defines the visual appearance and the behavior of each item added to the control:
<DataTemplate x:Key="RD_DataTemplate">
<StackPanel Width="{Binding Path=Width, RelativeSource={RelativeSource TemplatedParent}}" Height="{Binding Path=Height, RelativeSource={RelativeSource TemplatedParent}}">
<Image Source="{Binding XPath=@Image}" Width="70" Height="70" />
<Grid>
<TextBlock x:Name="PART_Text" Text="{Binding XPath=@Name}" Style="{StaticResource RDText_Style}" />
</Grid>
</StackPanel>
<DataTemplate.Triggers>
<EventTrigger RoutedEvent="cc:EllipticItem.MouseEnter">
<EventTrigger.Actions>
<BeginStoryboard Storyboard="{StaticResource RDTextAnimation_Enter}" />
</EventTrigger.Actions>
</EventTrigger>
<EventTrigger RoutedEvent="cc:EllipticItem.MouseLeave">
<EventTrigger.Actions>
<BeginStoryboard Storyboard="{StaticResource RDTextAnimation_Leave}" />
</EventTrigger.Actions>
</EventTrigger>
</DataTemplate.Triggers>
</DataTemplate>
The new EllipticItemSelector
To enable adding items by data binding, the control must derive from ItemsControl which provides the ItemsSource and the ItemTemplate properties.
I need to ensure two things: each created item is of EllipticItem type and determine whether each item is its own container.
I also modified others methods which have no responsibility in data binding so download the source to have the entire modified code.
public partial class EllipticItemSelector : ItemsControl
{
protected override DependencyObject GetContainerForItemOverride(object item)
{
EllipticItem __ellipticItem = new EllipticItem();
__ellipticItem.MouseLeftButtonDown += new System.Windows.Input.MouseButtonEventHandler(__ellipticItem_MouseLeftButtonDown);
return __ellipticItem;
}
protected override bool IsItemItsOwnContainerOverride(object item)
{
return (item is EllipticItem);
}
}
Remember that in the first version, I used a Canvas to position the items. In this version, I always used the Canvas because I don't choose the translate transformation to move items because using the x and y coordinates instead of transformation preserve resources. In this version, the canvas is defined as a ControlTemplate within the control.
The new EllipticItem
To enable storing data of the item, the new control derives from ContentControl which provides the Content property, necessary to retrieve data.
I replaced the initial viewbox, attached during the life cycle of the item by a scale transformation which improves the quality of the animation (even though you are like me, with a poor video card).
The bonus
In this version of the sample, I decided to get benefits of the data bound to the control to display information about the RioterDeckers.
So I created a control with all the needed DependencyProperty to receive the appropriate data:
<Grid xmlns=http://schemas.microsoft.com/winfx/2006/xaml/presentation
xmlns:x=http://schemas.microsoft.com/winfx/2006/xaml
x:Class="RD.EllipticSample.RDAbilityControl"
Name="_mainControl" Opacity="0" Margin="20" Width="225">
<Grid.Resources>
<Style x:Key="RDText_Style" TargetType="{x:Type TextBlock}">
<Setter Property="FontFamily" Value="Segoe UI" />
<Setter Property="FontSize" Value="9" />
<Setter Property="Foreground" Value="White" />
<Setter Property="HorizontalAlignment" Value="Center" />
<Setter Property="Opacity" Value="0" />
<Setter Property="TextBlock.BitmapEffect">
<Setter.Value>
<DropShadowBitmapEffect Color="Black" Direction="0" Opacity="0.4" ShadowDepth="2" Softness="0.2" />
</Setter.Value>
</Setter>
</Style>
<Style x:Key="{x:Type TextBlock}" TargetType="{x:Type TextBlock}" BasedOn="{StaticResource RDText_Style}">
<Setter Property="Opacity" Value="1" />
<Setter Property="FontSize" Value="12" />
<Setter Property="HorizontalAlignment" Value="Left" />
</Style>
<Storyboard x:Key="SB_Open">
<DoubleAnimation From="0" To="1" Duration="0:0:0.2" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="_mainControl" />
</Storyboard>
<Storyboard x:Key="SB_Close">
<DoubleAnimation From="1" To="0" Duration="0:0:0.2" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="_mainControl" />
</Storyboard>
</Grid.Resources>
<Grid.DataContext>
<Binding ElementName="_mainControl" />
</Grid.DataContext>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
<RowDefinition />
<RowDefinition />
<RowDefinition />
<RowDefinition />
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<TextBlock Grid.Column="0" Grid.ColumnSpan="2" Grid.Row="0" FontSize="14" Margin="0,0,0,10" HorizontalAlignment="Center">
<TextBlock Text="The abilities of " />
<TextBlock Text="{Binding Path=RDName}" />
</TextBlock>
<TextBlock Grid.Column="0" Grid.Row="1" Text="DotNet drilling" />
<TextBlock Grid.Column="1" Grid.Row="1" Text="{Binding Path=Drilling}" HorizontalAlignment="Right" />
<TextBlock Grid.Column="0" Grid.Row="2" Text="Analysis gathering" />
<TextBlock Grid.Column="1" Grid.Row="2" Text="{Binding Path=Analysis}" HorizontalAlignment="Right" />
<TextBlock Grid.Column="0" Grid.Row="3" Text="DataBlaster" />
<TextBlock Grid.Column="1" Grid.Row="3" Text="{Binding Path=DataBlaster}" HorizontalAlignment="Right" />
<TextBlock Grid.Column="0" Grid.Row="4" Text="Early adopter" />
<TextBlock Grid.Column="1" Grid.Row="4" Text="{Binding Path=Early}" HorizontalAlignment="Right" />
<TextBlock Grid.Column="0" Grid.Row="5" Text="UI Arts" />
<TextBlock Grid.Column="1" Grid.Row="5" Text="{Binding Path=UI}" HorizontalAlignment="Right" />
<TextBlock Grid.Column="0" Grid.Row="6" Text="Project handling" />
<TextBlock Grid.Column="1" Grid.Row="6" Text="{Binding Path=Project}" HorizontalAlignment="Right" />
<TextBlock Grid.Column="0" Grid.Row="7" Text="Business opportunist" />
<TextBlock Grid.Column="1" Grid.Row="7" Text="{Binding Path=Business}" HorizontalAlignment="Right" />
<TextBlock Grid.Column="0" Grid.ColumnSpan="2" Grid.Row="8" Margin="0,10,0,0" Text="Master in technologeek" />
<TextBlock Grid.Column="0" Grid.ColumnSpan="2" Grid.Row="9" Margin="0,20,0,0" HorizontalAlignment="Center" FontStyle="Italic" TextWrapping="Wrap" Text="{Binding Path=Phrase}" />
</Grid>
I added a method in the window to set data to the properties. Keep in mind that the items of the elliptic control provides data to be displayed. I retrieve data within the Content property when the event SelectionChanged is raised. Don't forget that the content of the item is an XmlElement because I used data in an Xml format:
private void _SelectionChanged(object sender, EllipticSelectionChangedEventArgs e)
{
EllipticItem __item = e.NewItem;
_BuildAbilityControl(__item);
_rdAbilityControl.OpenScene();
}
private void _BuildAbilityControl(EllipticItem item)
{
XmlElement __element = (XmlElement)item.Content;
_rdAbilityControl.SetValue(RDAbilityControl.RDNameProperty, __element.Attributes["Name"].Value);
_rdAbilityControl.SetValue(RDAbilityControl.DrillingProperty, __element.Attributes["Drilling"].Value);
_rdAbilityControl.SetValue(RDAbilityControl.AnalysisProperty, __element.Attributes["Analysis"].Value);
_rdAbilityControl.SetValue(RDAbilityControl.DataBlasterProperty, __element.Attributes["DataBlaster"].Value);
_rdAbilityControl.SetValue(RDAbilityControl.EarlyProperty, __element.Attributes["Early"].Value);
_rdAbilityControl.SetValue(RDAbilityControl.UIProperty, __element.Attributes["UI"].Value);
_rdAbilityControl.SetValue(RDAbilityControl.ProjectProperty, __element.Attributes["Project"].Value);
_rdAbilityControl.SetValue(RDAbilityControl.BusinessProperty, __element.Attributes["Business"].Value);
_rdAbilityControl.SetValue(RDAbilityControl.PhraseProperty, __element.Attributes["Phrase"].Value);
}
Finally
With the next CTP, I will upgrade this version of the control and I will provide another functionality: determine the minimum size and the maximum size of items used during the scale transformation to simulate the 3-D depth.
Download the sources
Download the binaries
(WinFx Feb. CTP)