Hi guys!
Today I developed a nice application showing how is possibile displaying items in a radial panel.The application we are going to develop looks like the following figure and is downloadable here:

ListView Radial Items

Clicking the ‘play’ centered toggle button some numbers appear moving away the toggle button forming a radial figure as the following pictures show:

Radial Menu WPF

Radial Menu WPF

The result of clicking a second time on the toggle button is to move the numbers form their radial position towards the toggle button self. After that the numbers disappear. Morover, moving the mouse over a number the nice effect shown in this figure is applied:

Mouse Over Trigger WPF

A sample application scenario could be touch screen applications, but here it is useful to understand the following WPF concepts:  XmlDataProvider, DataTemplate, Storyboard and Triggers.

Step 1 – Binding ItemsSource to XmlDataProvider

The first step is to provide data and binding them into our ItemsSource control. For doint it in a fastest but customizable way I created an xml file (MenuItems.xml)  that looks like this:

<MenuItems>

  <MenuItem>
    <Text>1</Text>
  </MenuItem>

  <MenuItem>
    <Text>2</Text>
  </MenuItem>
...
</MenuItems>

For reading the xml file I created an XmlDataProvider into Windows.Resources tag.  The XmlDataProvider is a WPF class that allow you to read any xml data. The advantage of using XmlDataProvider is that you can bind it to any UI Source Property in order to display the data in UI controls. I created my DataDS XmlDataProvider for reading my xml file in the following way:

<XmlDataProvider x:Key="DataDS" Source="MenuItems.xml" />

I binded the DataDS’s MenuItems into my ItemsControl ItemsSource with these lines of xaml code:

<ItemsControl x:Name="itemsControl"
              Width="300"
              Height="300"
              ItemsSource="{Binding Mode=Default, Source={StaticResource DataDS},
				 XPath=/MenuItems/MenuItem}">
</ItemsControl>

Step 2 – Making a Radial Panel Template

You can set the Panel Template using the PanelTemplate Property after creating your own ItemsPanelTemplate resource. A Panel is a class for defining how objects should be displayed into a certain space. The mains Panels provided by WPF are: WrapPanel, StackPanel, Grid, DockPanel and Canvas. However a radial Panel is not provided, so inspired by the article i read here , I decided to write my own RadialPanel class that basically consinst in the following lines of code:

public class RadialPanel : Panel
    {

	...

        // Measure each children and give as much room as they want
        protected override Size MeasureOverride(Size availableSize)
        {
            foreach (UIElement elem in Children)
            {
                //Give Infinite size as the avaiable size for all the children
                elem.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
            }

	    ...

            return base.MeasureOverride(availableSize);
        }

        //Arrange all children based on the geometric equations for the circle.
        protected override Size ArrangeOverride(Size finalSize)
        {
            if (Children.Count == 0)
                return finalSize;

            double _angle = 0;

            //Degrees converted to Radian by multiplying with PI/180
            double _incrementalAngularSpace = (360.0 / Children.Count) * (Math.PI / 180);

            //An approximate radii based on the avialable size , obviusly a better approach is needed here.
            double radiusX = finalSize.Width / 2.4;
            double radiusY = finalSize.Height / 2.4;

            foreach (UIElement elem in Children)
            {

                //Calculate the point on the circle for the element
                Point childPoint = new Point(Math.Cos(_angle) * radiusX, - Math.Sin(_angle) * radiusY);

                // Center Element
                Point centerPoint = this.TranslatePoint(new Point(finalSize.Width / 2, finalSize.Height / 2), this);
                elem.Arrange(new Rect(centerPoint.X - elem.DesiredSize.Width / 2,
                                      centerPoint.Y - elem.DesiredSize.Height / 2,
                                      elem.DesiredSize.Width,
                                      elem.DesiredSize.Height));

                //Calculate the new _angle for the next element
                _angle += _incrementalAngularSpace;

                ...
            }

            return finalSize;
        }
    }

In order to display the elements in a radial way I override the two Panel method calculating for every elements the size and the position:

  • MeasureOverride: calculates the size in layout required for child elements and determines a size for the FrameworkElement derived class

  • ArrangeOverride: calculate the child elements positions and determines a size for a FrameworkElement derived class

Step 4 – Creating ItemTemplate

The way in wich numbers are displayed is described by the following DataTemplate that consists into basics two parts: a Border describing the circle around the number and a TextBox showing the number. Here you are the Xaml code and the relevant graphic result:

       <DataTemplate x:Key="itemTemplate">
            <Button Tag="{Binding}">
                <Button.Template>
                        <ControlTemplate>

			   <Grid Width="70" Height="70" x:Name="grid" RenderTransformOrigin="0.5,0.5">

                                <Border BorderThickness="8,8,8,8" CornerRadius="50,50,50,50" x:Name="border">

				    <Border.Background>
                                        <RadialGradientBrush>
                                            <GradientStop Color="#FFFFFFFF" Offset="0"/>
                                            <GradientStop Color="#FFFFFFFF" Offset="1"/>
                                        </RadialGradientBrush>
                                    </Border.Background>

				    <Border.BorderBrush>
                                        <RadialGradientBrush>
                                            <GradientStop Color="#FF0835B3" Offset="0.737"/>
                                            <GradientStop Color="#FF8DACFF" Offset="0.888"/>
                                            <GradientStop Color="#FF0835B3" Offset="1"/>
                                        </RadialGradientBrush>
                                    </Border.BorderBrush>

                                    <TextBlock Text="{Binding XPath=Text}"
                                       TextWrapping="Wrap"
                                       HorizontalAlignment="Center"
                                       VerticalAlignment="Center"
                                       FontSize="48"
                                       FontFamily="Rosewood Std"
                                       FontWeight="Normal"/>

                                </Border>
                            </Grid>
                </Button.Template>
            </Button>
        </DataTemplate>

Datatemplate radial list

Step 3 – Expaning\Collapsing Items with Animation

These kind of animations are built and enterly managed into the Radial Panel object.
Each displayed item is associated to two storyboards:

  • a storyboard describing the expansion of the item, fom the the button to the it’s final position,
  • a storyboard describing the collapse of the item from the external to the button

These animations are built and attached to the item by the Radial Panel when the item is added. This is done into ArrangeOverride method and you can easily see the relevant region code downloading the source code.
The first animation starts when the Radial Panel became visible, the second one starts when the panel became hidden. For doing it I handled the Radial Panel IsVisibleChanged event in the following way:

        private void RadialPanel_IsVisibleChanged(object sender,
                                        DependencyPropertyChangedEventArgs e)
        {
            if (Boolean.Parse(e.NewValue.ToString()) == true)
            {
                this.StartAnimation();
            }
            else
            {
                this.PlayBackAnimation();
            }
        }

        private void StartAnimation()
        {
            foreach (Storyboard s in _expandStoryBoard)
                s.Begin();
        }

        private void PlayBackAnimation()
        {
            _parentItemsControl.IsVisibleChanged -= RadialPanel_IsVisibleChanged;

            _parentItemsControl.Visibility = Visibility.Visible;

            foreach (Storyboard s in _collapseStoryBoard)
            {
                s.Completed += new EventHandler(s_Completed);
                s.Begin();
            }
        }

        void s_Completed(object sender, EventArgs e)
        {
            if (_parentItemsControl.Visibility == Visibility.Hidden)
                return;

            _parentItemsControl.Visibility = Visibility.Hidden;
            _parentItemsControl.IsVisibleChanged += new DependencyPropertyChangedEventHandler(RadialPanel_IsVisibleChanged);

        }

As you can see there is a little tricky for displaying the collapsing animation. During the execution of this storyboard the the Radial Panel Visibility is hidden, so before starting it I unhandled the IsVisibleChanged event, then I set the visibility to Visible and when the storyboard is ended I finally hide the Radial Panel and then I handled the IsVisibleChanged event again.

Step 4 – Applying OnMouseOver Trigger

When the mouse is over an item, the number and the circle became a little bit bigger and the background became blue in a slowly manner. For doing these effects I build the following storyboard using Microsoft Expression Blend:

  <ControlTemplate.Resources>
      <Storyboard x:Key="OnMouseEnter1">
          <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="grid" Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleX)">
              <SplineDoubleKeyFrame KeyTime="00:00:00" Value="1"/>
              <SplineDoubleKeyFrame KeyTime="00:00:00.3000000" Value="1.1"/>
          </DoubleAnimationUsingKeyFrames>
          <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="grid" Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleY)">
              <SplineDoubleKeyFrame KeyTime="00:00:00" Value="1"/>
              <SplineDoubleKeyFrame KeyTime="00:00:00.3000000" Value="1.1"/>
          </DoubleAnimationUsingKeyFrames>
          <ColorAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="border" Storyboard.TargetProperty="(Panel.Background).(GradientBrush.GradientStops)[0].(GradientStop.Color)">
              <SplineColorKeyFrame KeyTime="00:00:00" Value="#FFFFFFFF"/>
              <SplineColorKeyFrame KeyTime="00:00:00.3000000" Value="#FF5F95E4"/>
          </ColorAnimationUsingKeyFrames>
          <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="border" Storyboard.TargetProperty="(Panel.Background).(GradientBrush.GradientStops)[1].(GradientStop.Offset)">
              <SplineDoubleKeyFrame KeyTime="00:00:00" Value="1"/>
              <SplineDoubleKeyFrame KeyTime="00:00:00.3000000" Value="0"/>
          </DoubleAnimationUsingKeyFrames>
          <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="border" Storyboard.TargetProperty="(Panel.Background).(GradientBrush.GradientStops)[0].(GradientStop.Offset)">
              <SplineDoubleKeyFrame KeyTime="00:00:00" Value="0"/>
              <SplineDoubleKeyFrame KeyTime="00:00:00.3000000" Value="1"/>
          </DoubleAnimationUsingKeyFrames>
          <ColorAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="border" Storyboard.TargetProperty="(Panel.Background).(GradientBrush.GradientStops)[1].(GradientStop.Color)">
              <SplineColorKeyFrame KeyTime="00:00:00" Value="#FFFFFFFF"/>
              <SplineColorKeyFrame KeyTime="00:00:00.3000000" Value="#FFCBE2F9"/>
          </ColorAnimationUsingKeyFrames>
      </Storyboard>
  </ControlTemplate.Resources>

Basically the first two DoubleAnimationUsingKeyFrames applying a rendertransform to the Grid containing the entire item. This allow the item to increase its size and off course you have to add the Grid.Transform to the Grid itself as you can notice downloading the sample.
The rests of lines allow the background to became blue in a slow way. I don’t know the precise meaning of these lines, but Expression Blend build them for me
Finally here you are the Triggers that start and end the storyboard when the mouse is over or leave the item:

<ControlTemplate.Triggers>
   <EventTrigger RoutedEvent="Mouse.MouseLeave">
     <RemoveStoryboard BeginStoryboardName="OnMouseEnter1_BeginStoryboard"/>
   </EventTrigger>

   <EventTrigger RoutedEvent="Mouse.MouseEnter">
      <BeginStoryboard Storyboard="{StaticResource OnMouseEnter1}" x:Name="OnMouseEnter1_BeginStoryboard"/>
   </EventTrigger>
</ControlTemplate.Triggers>

Hi guys!
Today I am going to explain how completely customizing a WPF ListView in your applications using an XML configuration File.

The Source File in VS2008 is available HERE .

The Sample Application

The Application I developed simply displays a list of Files describing the name, the extension and the size. The amazing feature is that the displayed columns and the individual styles of columns are configurable from an xml file!
The Application looks like the following:

application
And here you can take a look at the configuration file:

xml-file
As you can see all the displayed columns are configured into the XML file and for each columns the Style to be used is configured as well. The xml file describes the following configuration:  the FileName displayed in an editable TextBox, the FileExtension displayed as text, the KBSize displayed as text as well, the FileExtension again but displayed with icons, the KBSize again but displayed with a green circle for smaller files and displayed with a red circle for largest files.

How To

In the next sections are described the steps for building the Sample Application.

1. Deserializing the XML File (using XSD.EXE tool)

In order to develop the application as fast as possible I used the xsd.exe tool which is capable to automatically generate a C# source file from an xsd schema. The source file generated is able to represent an xml file content conform to the described schema file.
The xsd schema describing the configuration file we want is the following:

<xs:schema xmlns:mstns="http://tempuri.org/MyCustomizedColumns.xsd"
           elementFormDefault="qualified"
           targetNamespace="http://tempuri.org/MyCustomizedColumns.xsd"
           id="MyCustomizedColumns"
           xmlns:xs="http://www.w3.org/2001/XMLSchema">

  <xs:simpleType name="CellStyles">
    <xs:restriction base="xs:string">
      <xs:enumeration value="SimplePlainText" />
      <xs:enumeration value="HyperLinkText" />
      <xs:enumeration value="EditableTextBox" />
      <xs:enumeration value="KBSizeIcon" />
      <xs:enumeration value="IconFileType" />
    </xs:restriction>
  </xs:simpleType>

  <xs:simpleType name="Columns">
    <xs:restriction base="xs:string">
      <xs:enumeration value="Summary" />
      <xs:enumeration value="FileName" />
      <xs:enumeration value="KBSize" />
      <xs:enumeration value="FileExtension" />
    </xs:restriction>
  </xs:simpleType>

  <xs:complexType name="ColumnDefinition">
    <xs:attribute name="Column" type="mstns:Columns" use="required" />
    <xs:attribute name="CellStyle" type="mstns:CellStyles" use="required" />
  </xs:complexType>

  <xs:element name="ColumnsDefinitions">
    <xs:complexType>
      <xs:sequence>
        <xs:element minOccurs="0"
                    maxOccurs="unbounded"
                    name="ColumnDefinition"
                    type="mstns:ColumnDefinition" />
      </xs:sequence>
    </xs:complexType>
  </xs:element>

</xs:schema>

The above xsd schema describes two simple types: the CellStyles and the Columns which are enumerations describing the possibles columns and the possibles styles. Then the schema definins the ColumnDefinition complexType which describes a column type and its relevant style. At the end of the schema the element ColumnsDefinitions defines a sequence of ColumnDefinition types representing the entire ListView customization.

The following few lines of code are able to reading any xml files that are conformed to the xml schema. Here an XmlReaderSettings object is created and is configured for validating any xml file conformed to the above schema. After that the XmlSerializer object is created and used for deserializing the xml file. The xml content is put into the ColumnsDefinitions object which is defined into the automatically generated file.

        private ColumnsDefinitions ReadConfiguredColumns()
        {
            try
            {
                bool o = File.Exists("ColumnsConfiguration.xml");
                using (FileStream xmlConfigStream = File.OpenRead("ColumnsConfiguration.xml"),
                                  xmlSchemaStream = File.OpenRead("MyCustomizedColumns.xsd"))
                {
                    XmlSchemaSet schemaSet = new XmlSchemaSet();
                    XmlReader schemaReader = XmlReader.Create(xmlSchemaStream);
                    schemaSet.Add(@"http://tempuri.org/MyCustomizedColumns.xsd", schemaReader);

                    XmlReaderSettings settings = new XmlReaderSettings();
                    settings.ValidationType = ValidationType.Schema;
                    settings.IgnoreWhitespace = true;
                    settings.IgnoreComments = true;
                    settings.Schemas = schemaSet;

                    XmlReader reader = XmlReader.Create(xmlConfigStream, settings);

                    XmlSerializer xs = new XmlSerializer(typeof(ColumnsDefinitions));
                    ColumnsDefinitions configuredColumns = xs.Deserialize(reader) as ColumnsDefinitions;
                    return configuredColumns;
                }
            }
            catch (Exception) { return null; }
        }

2. The FileItem Class

Just for simplifying the reading of the subsequent sections, here is the code of the FileItem class. I choose the FileItem to inherit from the DependencyObject in order to enables animation, styling, binding, and so on in the easier way.

C# File Item

3. The Xaml Sample Application Code (ResourceDictionary, Styles and Binding Rock!)

The xaml code consists into two parts: the first one is contained into Windows.Resources and defines the same styles defined into the above xsd schema, the other one is the ListView itself simply containing a GridView that is used in codebehind for building columns and applying relevant styles (as described in the last section).

Xaml Resources ListView GridView
In the above code is displayed the SimplePlainTextStyle, which can be applied to a ContentControl and simply displays the ContentControl Content. In other words the <ContentPresenter/> means: “here is displayed the Content of the ContentControl, regardless the Content type is”. The ContentControl Content can be whatever you want, even if in our case the simply a string, as I’m going to explain.
Instead the HyperLinkText style displays an hyperlink text but it is  very closed to the SimplePlainTextStyle.
The EditableTextBox style works like the above styles but it is defined in the following way:

            <Style x:Key="EditableTextBox"
                     TargetType="{x:Type ContentControl}">
                <Setter Property="Template">
                    <Setter.Value>
                        <ControlTemplate TargetType="{x:Type ContentControl}">
                            <Grid Margin="5"
                                  VerticalAlignment="Top" >
                                <TextBox Text="{TemplateBinding Property=Content}"/>
                            </Grid>
                        </ControlTemplate>
                    </Setter.Value>
                </Setter>
            </Style>

Because there are any way to write something like <TextBox…> <ContentPresenter/> </TextBox>, the only way to display the content into the TextBox is to write <TextBox Text=”{TemplateBinding Property=Content}”/>.  This means: “the Text to be displayed is the same displayed into the Content Property of the above Template. The above template is aContentControl Template, because the TargetType of our Style is the ContentControl”.
The KBSizeIcon and the IconFileType Styles are different from the above styles because these are specific styles that can be applied only to specific columns. What I mean is: a SimplePlainTextStyle can be applied to the FileSize or to the FileName or the FileExtensio as well, while the KBSizeIcon style can be applied only to the KBSize column, as well as it makes any sense to apply the IconFileType style to columns different from the FileExtension column!  Because these styles are specifics, they are directly binded to specifics properties of the FileItem object.  The following image displays the xaml code describing the KBSizeIcon:

            <Style x:Key="KBSizeIcon"
                     TargetType="{x:Type ContentControl}">
                <Setter Property="Template">
                    <Setter.Value>
                        <ControlTemplate TargetType="{x:Type ContentControl}">
                            <Grid Margin="5"
                                  VerticalAlignment="Top" >
                                <Border x:Name="myborder" Width="12" Height="12" Background="{x:Null}"
                                        CornerRadius="12" BorderThickness="0">
                                </Border>
                            </Grid>
                            <ControlTemplate.Triggers>
                                <DataTrigger Binding="{Binding Path=IsBiggerFile}"
                                                             Value="True">
                                    <Setter TargetName="myborder" Property="Background" Value="Red"/>
                                </DataTrigger>
                                <DataTrigger Binding="{Binding Path=IsBiggerFile}"
                                                             Value="False">
                                    <Setter TargetName="myborder" Property="Background" Value="Green"/>
                                </DataTrigger>
                            </ControlTemplate.Triggers>
                        </ControlTemplate>

                    </Setter.Value>
                </Setter>
            </Style>

As you can see the displayed icon (green or red) is choosen using ControlTemplate.Triggers that are binded to IsBiggerFile FileItem Dependency Property.

4. The C# Code for building Customized Columns (Factory pattern Rock!)

Finally the engine code that is able to building the columns and applying the styles:

        private void CreateCostumizedColumns(ColumnsDefinitions configuredColumns)
        {
            foreach (ColumnDefinition col in configuredColumns.ColumnDefinition)
            {
                GridViewColumn gridColumn = new GridViewColumn();

                FrameworkElementFactory contentControlFactory =
                                            new FrameworkElementFactory(typeof(ContentControl));
                Style confStyle = this.FindResource(col.CellStyle.ToString()) as Style;
                contentControlFactory.SetValue(ContentControl.StyleProperty, confStyle);

                if (col.CellStyle != CellStyles.KBSizeIcon &&
                    col.CellStyle != CellStyles.IconFileType)
                {
                    Binding binding = new Binding(col.Column.ToString());
                    contentControlFactory.SetBinding(ContentControl.ContentProperty, binding);
                }

                gridColumn.Header = col.Column.ToString();
                gridColumn.CellTemplate = new DataTemplate();
                gridColumn.CellTemplate.VisualTree = contentControlFactory;

                myGridView.Columns.Add(gridColumn);
            }
        }

The code explains itself in an easy way. For each column described into the configuration xml file, a new GridColumn is created. Using  the FrameworkElementFactory we are able create a “factory” that, in our code,  is an object able to build ContentControls object. The configured Style is associated to the contentControlFactory object and than, if the style we are applying is not a “specific style” (I mean it is not KBSizeIcon or IconFileType), we set a binding between the Content Property of the ContentControl and the relevant FileItem Dependency Property. Finally the Column Header is set, a new DataTemplate is created and assigned to the column CellTemplate property, the VisualTree CellTemplate is associated to the contentControlFactory and the new column is added to the GridView.