Thursday, May 8, 2014

Azure Portal Navigation Style in WPF

When it comes to product user experiences, Microsoft has always delighted and surprised its users with innovative user interfaces, be it the Office Ribbon UI, Dynamics CRM or Metro UI in Windows 8. The Windows Azure portal too is a great example of elegant user experience that embraces effective call to action. Below is one of the screens from the management portal:

clip_image002

Image source: Microsoft Azure portal.

Ever since I got a glimpse of the portal I have been inching to use this navigation pattern in my application and when finally I did get a chance to put the navigation pattern to use, WPF was the obvious choice. The final result of the ListBox styling is shown below:

clip_image004

The tools and techniques used to design/style the UI is described below.

Tools of the trade:

  • Kaxaml – the hands down best XAML editor on this planet (and beyond)
  • ColorZilla’s eye dropper – for extracting colors from the portal image above
  • Metro Studio – the awesome developer friendly icon editor for XAML icons

 

Step 1: Defining base colors

<!-- background color of the grid and the list box -->

<SolidColorBrush x:Key="azureItemBackground" Color="#3C454F" />

<!-- background color of the selected list box item -->

<SolidColorBrush x:Key="azureItemSelected" Color="#6D747B" />

<!-- foreground color for the extra text displayed under the primary text -->

<SolidColorBrush x:Key="azureItemHighlightText" Color="#89C402" />

<!-- metro color for the title and other highlighting -->

<SolidColorBrush x:Key="AccentColorBrush" Color="CornflowerBlue" />

Step 2: Styling the ListBox

The ListBox control along with the content composition model in WPF makes designing this interface a breeze. First the ListBox control itself requires some property changes as below:

<Style x:Key="azureListBoxStyle" TargetType="ListBox">

   <Setter Property="BorderThickness" Value="0,0,1,0" />

   <Setter Property="Background" Value="{StaticResource azureItemBackground}" />

   <Setter Property="HorizontalAlignment" Value="Left" />

   <Setter Property="VerticalAlignment" Value="Stretch" />

   <Setter Property="HorizontalContentAlignment" Value="Stretch" />

   <Setter Property="VerticalContentAlignment" Value="Stretch" />

</Style>

Step 3: Creating the ListBoxItemTemplate (the critical piece)

<DataTemplate x:Key="azureListItemTemplate">

        <Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch">

            <Grid.ColumnDefinitions>

                <ColumnDefinition Width="20" />

                <ColumnDefinition Width="Auto" />

                <ColumnDefinition Width="*" />

            </Grid.ColumnDefinitions>

            <Rectangle Width="13" Margin="-30,0,0,0" Grid.Column="0">

                <Rectangle.Style>

                    <Style TargetType="Rectangle">

                        <Setter Property="Fill" Value="Transparent" />

                        <Style.Triggers>

                            <DataTrigger Binding="{Binding Path=IsSelected,

                                                   RelativeSource={RelativeSource Mode=FindAncestor,

                                                   AncestorType={x:Type ListBoxItem}}}"

                                     Value="True">

                                <Setter Property="Fill" Value="White" />

                            </DataTrigger>

                            <DataTrigger Binding="{Binding Path=IsMouseOver,

                                                   RelativeSource={RelativeSource Mode=FindAncestor,

                                                   AncestorType={x:Type ListBoxItem}}}"

                                     Value="True">

                                <Setter Property="Fill" Value="{DynamicResource AccentColorBrush}" />

                            </DataTrigger>

                        </Style.Triggers>

                    </Style>

                </Rectangle.Style>

            </Rectangle>

   

            <Path Margin="0,10,5,10" Grid.Row="0" Grid.Column="1" x:Name="listItemIcon"

              HorizontalAlignment="Center" VerticalAlignment="Center"

              Stretch="Uniform" Width="30" Height="30"

              Data="{Binding XPath=@Picture}">

                <Path.Style>

                    <Style TargetType="Path">

                        <Setter Property="Fill" Value="#DADCDE" />

                        <Style.Triggers>

                            <DataTrigger Binding="{Binding Path=IsSelected, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ListBoxItem}}}"

                                     Value="True">

                                <Setter Property="Fill" Value="White" />

                            </DataTrigger>

                            <DataTrigger Binding="{Binding Path=IsMouseOver,

                                                   RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ListBoxItem}}}"

                                     Value="True">

                                <Setter Property="Fill" Value="White" />

                            </DataTrigger>

                        </Style.Triggers>

                    </Style>

                </Path.Style>

            </Path>

   

            <TextBlock Grid.Row="0" Grid.Column="2" Margin="10"

                   HorizontalAlignment="Left" VerticalAlignment="Center">

              <Run Text="{Binding XPath=@Name}" FontWeight="SemiBold"

                   Foreground="{Binding ElementName=listItemIcon, Path=Fill}" />

              <Run Text="{Binding XPath=@Hint}" FontWeight="Bold"

                   Foreground="{StaticResource azureItemHighlightText}" />

            </TextBlock>

        </Grid>

    </DataTemplate>

 

Step 4: Styling the ItemContainer

<Style x:Key="azureItemContainerStyle" TargetType="ListBoxItem">

   <Setter Property="Cursor" Value="Hand" />

   <Setter Property="Background" Value="{StaticResource azureItemBackground}" />

   <Style.Triggers>

      <Trigger Property="IsMouseOver" Value="True">

         <Setter Property="Background" Value="{StaticResource azureItemSelected}" />

      </Trigger>

      <Trigger Property="IsSelected" Value="True">

         <Setter Property="Background" Value="{StaticResource azureItemSelected}" />

</Trigger>

   </Style.Triggers>

</Style>

 

Step 5: Overwriting windows Highlight Brush styles

<ListBox Grid.Row="1" ItemTemplate="{StaticResource azureListItemTemplate}" Width="300" Style="{StaticResource azureListBoxStyle}" ItemContainerStyle="{StaticResource azureItemContainerStyle}"

     ItemsSource="{Binding Source={StaticResource AzureActions}, XPath=//Action}">

   <ListBox.Resources>

        <SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}" Color="#6D747B" />

        <SolidColorBrush x:Key="{x:Static SystemColors.InactiveSelectionHighlightBrushKey}" Color="#6D747B" />

   </ListBox.Resources>

</ListBox>

For populating the ListBox’s with data, XmlDataProvider has been used.

Please download the full XAML file and paste it in Kaxaml or Visual Studio to see it in action. No external dependencies.

download AzureNavigationStyle.xaml

Imitation is indeed the best form of flattery.

Happy Coding!